<?php
namespace nur\mapper\item;

use nur\A;
use nur\b\params\Tparametrable;
use nur\b\ValueException;
use nur\mapper\base\capacitor\Capacitor;
use nur\mapper\base\Mapper;
use Traversable;

/**
 * Class StreamMapper: un mapper qui fait des opérations sur le flux
 *
 * --autogen-properties-and-methods--
 * @method string setAction(string $value)
 * @method int setMaxCount(int $value)
 */
class StreamMapper extends Mapper {
  use Tparametrable;

  const MAP_EOF = true;

  /** @var Capacitor */
  protected $capacitor;

  function setCapacitor(Capacitor $capacitor): self {
    $this->capacitor = $capacitor;
    return $this;
  }

  protected function capacitor(): Capacitor {
    if ($this->capacitor === null) $this->capacitor = new Capacitor();
    return $this->capacitor;
  }

  const ACTION_SPLIT = "split";
  const ACTION_JOIN = "join";

  const PARAMETRABLE_PARAMS_SCHEMA = [
    "action" => ["string", null, "action à effectuer sur le flux"],
    "max_count" => ["int", null, "nombre d'élément par batch"],
  ];

  /** @var string */
  protected $ppAction;

  /** @var int */
  protected $ppMaxCount = 0;

  function pp_setAction(?string $action, ?int $maxCount=null): void {
    switch ($action) {
    case "split":
    case "s":
      $this->ppAction = self::ACTION_SPLIT;
      break;
    case "join":
    case "j":
      $this->ppAction = self::ACTION_JOIN;
      $this->count = 0;
      break;
    default:
      if ($action !== null) {
        throw ValueException::invalid_value($action, "action");
      }
    }
    if ($maxCount !== null) $this->ppMaxCount = $maxCount;
  }

  function setActionSplit(): self {
    $this->pp_setAction("split");
    return $this;
  }

  function setActionJoin(?int $maxCount=null): self {
    $this->pp_setAction("join", $maxCount);
    return $this;
  }

  function pp_setMaxCount(int $maxCount): self {
    $this->pp_setAction(null, $maxCount);
    return $this;
  }

  protected $count;

  function mapper($item) {
    switch ($this->ppAction) {
    case self::ACTION_SPLIT:
      if (!is_iterable($item)) $item = A::with($item);
      return $this->mapTo($item);
    case self::ACTION_JOIN:
      if (!$this->eof) $this->capacitor()->charge($item);
      $this->count++;
      $discharge = $this->ppMaxCount > 0 && $this->count >= $this->ppMaxCount;
      if ($discharge || $this->eof) {
        $values = $this->capacitor()->discharge();
        $this->count = 0;
        if ($values instanceof Traversable) $values = iterator_to_array($values);
        $item = $values? [$values]: null;
      } else {
        $item = null;
      }
      return $this->mapTo($item);
    }
    return $item;
  }

  #############################################################################
  const _AUTOGEN_CONSTS = [
    "" => [self::class, "_autogen_consts"],
  ];
  const _AUTOGEN_LITERALS = /*autogen*/[
    [
      \nur\b\params\parametrable_utils::class,
      '\\nur\\b\\params\\parametrable_utils::class',
    ],
    [
      self::PARAMETRABLE_PARAMS_SCHEMA,
      'self::PARAMETRABLE_PARAMS_SCHEMA',
    ],
  ];
  const _AUTOGEN_METHODS = /*autogen*/[
    [
      \nur\b\params\parametrable_utils::class,
      '_autogen_methods_getters',
      self::PARAMETRABLE_PARAMS_SCHEMA,
      null,
    ],
    [
      \nur\b\params\parametrable_utils::class,
      '_autogen_methods_setters',
      self::PARAMETRABLE_PARAMS_SCHEMA,
      null,
    ],
  ];
  const _AUTO_GETTERS = /*autogen*/[
    'getAction' => 'action',
    'getMaxCount' => 'max_count',
  ];
  const _AUTO_SETTERS = /*autogen*/[
    'setAction' => 'action',
    'setMaxCount' => 'max_count',
  ];
  #--autogen-dynamic--
}
