<?php
namespace nur\data\types;

use Exception;
use nur\A;

/**
 * Class SimpleType: squelette d'implémentation pour un type simple
 */
abstract class AbstractSimpleType extends AbstractType {
  use _Tparametrable;

  /** @var bool suffit-il de vérifier la classe pour être sûr du type? */
  const CHECK_CLASS = true;

  /**
   * @var bool est-il autorisé que l'analyse ne consomme aucune donnée et
   * retourne null?
   */
  const ALLOW_PARSE_EMPTY = false;

  const PARAMETRABLE_PARAMS_SCHEMA = [
    "allow_parse_empty" => ["bool", null, "une analyse qui ne consomme aucun caractère est-elle autorisée?"],
  ];

  /** @var bool */
  protected $ppAllowParseEmpty;

  function isAllowParseEmpty(): bool {
    return $this->ppAllowParseEmpty;
  }

  #############################################################################

  function __construct(?array $params=null) {
    A::replace_n($params, "allow_parse_empty", static::ALLOW_PARSE_EMPTY);
    parent::__construct($params);
  }

  #############################################################################

  function canFormat(): bool {
    return true;
  }

  function format($value): string {
    return strval($value);
  }

  #############################################################################

  /**
   * fonction de support: mettre en forme la valeur, qui est déjà dans le bon
   * type. @return false s'il n'est pas possible de mettre en forme la valeur.
   */
  function beforeCheckInstance(&$value): bool {
    return true;
  }

  /**
   * fonction de support: mettre en forme la valeur, qui est déjà dans le bon type
   */
  function afterCheckInstance(&$value): void {
  }

  /** fonction de support: traiter les valeurs qui sont déjà dans le bon type */
  protected function verifixCheckClass(&$value, $orig, array &$result=null): bool {
    if (!static::CHECK_CLASS) return false;
    if (!$this->beforeCheckInstance($value)) return false;
    if (!$this->isInstance($value)) return false;
    $this->afterCheckInstance($value);
    self::result_valid($result, $value, $orig);
    return true;
  }

  /**
   * fonction de support: convertir le résultat de l'analyse de la chaine dans
   * le type approprié. retourner true si la valeur est valide, false si la
   * valeur est invalide (bien que peut-être syntaxiquement correcte)
   *
   * NB: si $this->allowParseEmpty==true, alors $value peut être null. il faut
   * donc toujours tester ce cas, parce que la classe peut-être instanciée avec
   * ce paramètre
   */
  function verifixConvert(&$value): bool {
    return true;
  }

  /**
   * retourner une description de la valeur, utilisable le cas échéant dans les
   * messages d'erreur
   */
  protected function getValueDesc($orig, $parsed, $value): ?string {
    return null;
  }

  /**
   * fonction de support: analyser la chaine et retourner la valeur dans le type
   * approprié
   */
  protected function verifixParse(string $value, $orig, ?array &$result=null) {
    try {
      $input = $unparsed = $value;
      $value = $this->parse($input);
      $parsed = substr($unparsed, 0, strlen($unparsed) - strlen($input));
      if (($input === "" || !$this->ppParseAll) && $this->verifixConvert($value)) {
        return self::result_valid($result, $value, $orig, $parsed, $input);
      }
      $orig_desc = $this->getValueDesc($orig, $parsed, $value);
      return self::result_invalid($result, "invalid", $orig, $orig_desc, $parsed, $input, $this->ppMessages, null);
    } catch (Exception $e) {
      $orig_desc = $this->getValueDesc($orig, false, $value);
      return self::result_invalid($result, "invalid", $orig, $orig_desc, false, false, $this->ppMessages, $e);
    }
  }

  protected function _verifix(&$value, array &$result=null): void {
    $orig = $value;
    if ($this->verifixNoParse($value, $result)) return;
    if ($this->verifixCheckClass($value, $orig, $result)) return;
    if (is_string($value)) {
      $value = $this->verifixParse($value, $orig, $result);
    } else {
      $orig_desc = $this->getValueDesc($orig, false, $value);
      $value = self::result_invalid($result, "invalid", $orig, $orig_desc, false, false, $this->ppMessages, null);
    }
  }
}
