<?php
namespace nur\sery\file\csv;

use nur\sery\A;
use nur\sery\php\time\Date;
use nur\sery\php\time\DateTime;

abstract class AbstractReader implements IReader {
  const SCHEMA = null;

  const HEADERS = null;

  /** @var bool faut-il utiliser les en-têtes pour retourner des tableaux associatifs? */
  const USE_HEADERS = true;

  /** @var ?string nom du fichier depuis lequel lire */
  const INPUT = null;

  /** @var bool faut-il trimmer le champ avant de le traiter? */
  const TRIM = true;

  /** @var bool faut-il considérer les chaines vides comme null? */
  const EMPTY_AS_NULL = true;

  /**
   * @var bool ne pas essayer de deviner le format des données: les laisser au
   * format chaine.
   */
  const PARSE_NOT = false;

  /**
   * @var bool faut-il forcer le type numérique pour une chaine numérique?
   * si false, ne forcer le type numérique que si la chaine ne commence pas par
   * zéro i.e "06" est une chaine, alors "63" est un nombre
   *
   * ce paramètre est ignoré si PARSE_NOT == true
   */
  const PARSE_NUMERIC = false;

  /**
   * @var bool faut-il forcer le type {@link Date} ou {@link DateTime} pour une
   * chaine au bon format?
   *
   * ce paramètre est ignoré si PARSE_NOT == true
   */
  const PARSE_DATE = true;

  function __construct($input, ?array $params=null) {
    if ($input !== null) $params["input"] = $input;
    #
    $this->schema = $params["schema"] ?? static::SCHEMA;
    $this->headers = $params["headers"] ?? static::HEADERS;
    $this->useHeaders = $params["use_headers"] ?? static::USE_HEADERS;
    $this->input = $params["input"] ?? static::INPUT;
    $this->trim = boolval($params["trim"] ?? static::TRIM);
    $this->emptyAsNull = boolval($params["empty_as_null"] ?? static::EMPTY_AS_NULL);
    $this->parseNot = boolval($params["parse_not"] ?? static::PARSE_NOT);
    $this->parseNumeric = boolval($params["parse_numeric"] ?? static::PARSE_NUMERIC);
    $this->parseDate = boolval($params["parse_date"] ?? static::PARSE_DATE);
  }

  protected ?array $schema;

  protected ?array $headers;

  protected bool $useHeaders;

  protected $input;

  protected bool $trim;

  protected bool $emptyAsNull;

  protected bool $parseNot;

  protected bool $parseNumeric;

  protected bool $parseDate;

  protected int $isrc = 0, $idest = 0;

  protected function cookRow(array &$row): bool {
    if (!$this->useHeaders) return true;
    if ($this->isrc == 0) {
      # ligne d'en-tête
      $headers = $this->headers;
      if ($headers === null) {
        if ($this->schema === null) $headers = null;
        else $headers = array_keys($this->schema);
        if ($headers === null) $headers = $row;
        $this->headers = $headers;
      }
      return false;
    }
    A::ensure_size($row, count($this->headers));
    $row = array_combine($this->headers, $row);
    return true;
  }

  protected function verifixCol(&$col): void {
    if ($this->trim && is_string($col)) {
      $col = trim($col);
    }
    if ($this->emptyAsNull && $col === "") {
      # valeur vide --> null
      $col = null;
    }
    if (!is_string($col) || $this->parseNot) return;
    if ($this->parseDate) {
      if (DateTime::isa_datetime($col, true)) {
        # datetime
        $col = new DateTime($col);
      } elseif (DateTime::isa_date($col, true)) {
        # date
        $col = new Date($col);
      }
      if (!is_string($col)) return;
    }
    $parseNumeric = $this->parseNumeric || substr($col, 0, 1) !== "0";
    if ($parseNumeric) {
      $tmp = str_replace(",", ".", $col);
      $float = strpos($tmp, ".") !== false;
      if (is_numeric($tmp)) {
        if ($float) $col = floatval($tmp);
        else $col = intval($tmp);
      }
    }
  }
}
