<?php
namespace nulib\php\coll;

use Exception;
use nulib\cl;
use nulib\tests\TestCase;
use TypeError;

class CursorTest extends TestCase {
  function test_map_row() {
    $cursor = new class extends Cursor {
      function mapRow(array $row, ?array $map): array {
        return cl::map($row, $map);
      }
    };
    $row = ["a" => 1, "b" => 2, "c" => 3, "x" => 99];
    $map = ["a", "b" => "x", "c" => function() { return "y"; }, "d" => null];
    self::assertSame([
      "a" => $row["a"],
      "b" => $row["x"],
      "c" => "y",
      "d" => null
    ], $cursor->mapRow($row, $map));
  }

  function test_filter_row() {
    $cursor = new class extends Cursor {
      function filterRow(array $row, $filter): bool {
        return cl::filter($row, $filter);
      }
    };
    $row = ["a" => 1, "b" => 2, "c" => 3, "x" => 99];
    self::assertTrue($cursor->filterRow($row, "a"));
    self::assertTrue($cursor->filterRow($row, ["a"]));
    self::assertTrue($cursor->filterRow($row, ["a" => true]));
    self::assertFalse($cursor->filterRow($row, ["a" => false]));
    self::assertTrue($cursor->filterRow($row, ["a" => 1]));
    self::assertFalse($cursor->filterRow($row, ["a" => 2]));

    self::assertFalse($cursor->filterRow($row, "z"));
    self::assertFalse($cursor->filterRow($row, ["z"]));
    self::assertFalse($cursor->filterRow($row, ["z" => true]));
    self::assertTrue($cursor->filterRow($row, ["z" => false]));
    self::assertFalse($cursor->filterRow($row, ["z" => 1]));
  }

  const SCALARS = [0, 1, 2, 3, 4];

  function generator() {
    yield from self::SCALARS;
  }

  function testVanilla() {
    $c = new Cursor(self::SCALARS);
    self::assertSame([[0], [1], [2], [3], [4]], cl::all($c));
    self::assertSame([[0], [1], [2], [3], [4]], cl::all($c));

    $c = new Cursor($this->generator());
    self::assertSame([[0], [1], [2], [3], [4]], cl::all($c));
    self::assertException(Exception::class, function() use ($c) {
      // pas possible de rewind un générateur
      return cl::all($c);
    });

    $c = new Cursor(null, [
      "rows" => function() {
        return self::SCALARS;
      },
    ]);
    self::assertError(TypeError::class, function() use ($c) {
      // rows doit être un iterable, pas une fonction
      return cl::all($c);
    });

    $c = new Cursor(null, [
      "rows" => $this->generator(),
    ]);
    self::assertSame([[0], [1], [2], [3], [4]], cl::all($c));
    self::assertException(Exception::class, function() use ($c) {
      // pas possible de rewind un générateur
      return cl::all($c);
    });

    $c = new Cursor(null, [
      "rows_func" => function() {
        return self::SCALARS;
      },
    ]);
    self::assertSame([[0], [1], [2], [3], [4]], cl::all($c));
    self::assertSame([[0], [1], [2], [3], [4]], cl::all($c));

    $c = new Cursor(null, [
      "rows_func" => $this->generator(),
    ]);
    self::assertSame([[0], [1], [2], [3], [4]], cl::all($c));
    self::assertException(Exception::class, function() use ($c) {
      // pas possible de rewind un générateur
      return cl::all($c);
    });

    $c = new Cursor(null, [
      "rows_func" => function() {
        yield from self::SCALARS;
      },
    ]);
    self::assertSame([[0], [1], [2], [3], [4]], cl::all($c));
    self::assertSame([[0], [1], [2], [3], [4]], cl::all($c));
  }

  function testMap() {
    $c = new Cursor(self::SCALARS, [
      "map_func" => function(Cursor $c) {
        return [$c->raw + 1];
      },
    ]);
    self::assertSame([[1], [2], [3], [4], [5]], cl::all($c));
  }

  function testFilter() {
    $c = new Cursor(self::SCALARS, [
      "filter_func" => function(Cursor $c) {
        return $c->raw % 2 == 0;
      },
    ]);
    self::assertSame([[1], [3]], cl::all($c));
  }

  function testEach() {
    $c = new Cursor(self::SCALARS, [
      "filter_func" => function(Cursor $c) {
        return $c->raw % 2 == 0;
      },
      "map_func" => function(Cursor $c) {
        return [$c->raw + 1];
      },
    ]);
    $xs = [];
    $xitems = [];
    $oxs = [];
    $kitems = [];
    $c->each(function(Cursor $c) use (&$xs, &$xitems, &$oxs, &$kitems) {
      $xs[] = $c->index;
      $oxs[] = $c->origIndex;
      $xitems[$c->index] = $c->row[0];
      $kitems[$c->key] = $c->row[0];
    });
    self::assertSame([0, 1], $xs);
    self::assertSame([2, 4], $xitems);
    self::assertSame([1, 3], $oxs);
    self::assertSame([1 => 2, 3 => 4], $kitems);
  }
}
