<?php
namespace nur\sery\ext\spreadsheet;

use nur\sery\cl;
use nur\sery\file\csv\AbstractReader;
use OpenSpout\Reader\Common\Creator\ReaderEntityFactory;

class SpoutReader extends AbstractReader {
  /** @var string|int|null nom de la feuille depuis laquelle lire */
  const WSNAME = null;

  function __construct($input, ?array $params=null) {
    parent::__construct($input, $params);
    $this->ssType = $params["ss_type"] ?? null;
    $this->allSheets = $params["all_sheets"] ?? true;
    $wsname = static::WSNAME;
    if ($params !== null && array_key_exists("wsname", $params)) {
      # spécifié par l'utilisateur: $allSheets = false
      $this->setWsname($params["wsname"]);
    } elseif ($wsname !== null) {
      # valeur non nulle de la classe: $allSheets = false
      $this->setWsname($wsname);
    } else {
      # pas de valeur définie dans la classe, laisser $allSheets à sa valeur
      # actuelle
      $this->wsname = null;
    }
    $this->includeWsnames = cl::withn($params["include_wsnames"] ?? null);
    $this->excludeWsnames = cl::withn($params["exclude_wsnames"] ?? null);
  }

  protected ?string $ssType;

  /** @var bool faut-il retourner les lignes de toutes les feuilles? */
  protected bool $allSheets;

  function setAllSheets(bool $allSheets=true): self {
    $this->allSheets = $allSheets;
    return $this;
  }
  
  /**
   * @var array|null si non null, liste de feuilles à inclure. n'est pris en
   * compte que si $allSheets===true
   */
  protected ?array $includeWsnames;

  /**
   * @var array|null si non null, liste de feuilles à exclure. n'est pris en
   *  compte que si $allSheets===true
   */
  protected ?array $excludeWsnames;

  protected $wsname;

  /**
   * @param string|int|null $wsname l'unique feuille à sélectionner
   *
   * NB: appeler cette méthode réinitialise $allSheets à false
   */
  function setWsname($wsname): self {
    $this->wsname = $wsname;
    $this->allSheets = true;
    return $this;
  }

  function getIterator() {
    switch ($this->ssType) {
    case "ods":
      $ss = ReaderEntityFactory::createODSReader();
      break;
    case "xlsx":
      $ss = ReaderEntityFactory::createXLSXReader();
      break;
    default:
      $ss = ReaderEntityFactory::createReaderFromFile($this->input);
      break;
    }
    $ss->open($this->input);
    try {
      $allSheets = $this->allSheets;
      $includeWsnames = $this->includeWsnames;
      $excludeWsnames = $this->excludeWsnames;
      $wsname = $this->wsname;
      $first = true;
      foreach ($ss->getSheetIterator() as $ws) {
        if ($allSheets) {
          $wsname = $ws->getName();
          $found = ($includeWsnames === null || in_array($wsname, $includeWsnames))
            && ($excludeWsnames === null || !in_array($wsname, $excludeWsnames));
        } else {
          $found = $wsname === null || $wsname === $ws->getName();
        }
        if ($found) {
          if ($first) {
            $first = false;
          } else {
            yield null;
            # on garde le même schéma le cas échéant, mais supprimer headers
            # pour permettre son recalcul
            $this->headers = null;
          }
          $this->isrc = $this->idest = 0;
          foreach ($ws->getRowIterator() as $row) {
            $row = $row->toArray();
            foreach ($row as &$col) {
              $this->verifixCol($col);
            }; unset($col);
            if ($this->cookRow($row)) {
              yield $row;
              $this->idest++;
            }
            $this->isrc++;
          }
        }
      }
    } finally {
      $ss->close();
    }
  }
}
