<?php
namespace nur\data\types;

use nur\A;
use nur\base;

/**
 * Class PatternType: une chaine qui correspond à une regexp
 */
class RegexpType extends AbstractSimpleType {
  use _Tparametrable;

  const ALLOW_NULL = true;
  const CHECK_CLASS = false;

  /** @var array|string */
  const PATTERN = ".*";

  /**
   * @var string un pattern qui matche *entièrement* une chaine normalisée de ce
   * type. de plus, ce pattern peut définir des groupes pour isoler certaines
   * parties de la valeur.
   * par défaut, utiliser {@link PATTERN} si cette constante est nulle.
   */
  const PATTERN_NORMALIZED = null;

  const PARAMETRABLE_PARAMS_SCHEMA = [
    "pattern" => ["array", null, "patterns valides"]
  ];

  /** @var array */
  protected $patterns;

  function pp_setPattern(array $patterns): self {
    $this->patterns = $patterns;
    return $this;
  }

  function __construct(?array $params=null) {
    parent::__construct($params);
    base::update_n($this->patterns, A::with(static::PATTERN));
  }

  function getClass(): string {
    return "string";
  }

  function isInstance($value, bool $strict=true): bool {
    if ($value === null) return true;
    if (!is_string($value)) return false;
    $patterns = null;
    if ($strict) {
      $patterns = static::PATTERN_NORMALIZED;
      if ($patterns !== null) $patterns = [$patterns];
    }
    if ($patterns === null) $patterns = $this->patterns;
    foreach ($patterns as $pattern) {
      if (preg_match($pattern, $value)) {
        return true;
      }
    }
    return false;
  }

  protected function extractParsedValue(array $ms) {
    return $ms[0];
  }

  function parse(string &$input) {
    foreach ($this->patterns as $pattern) {
      if (preg_match($pattern, $input, $ms)) {
        $value = $this->extractParsedValue($ms);
        $input = substr($input, strlen($ms[0]));
        return $value;
      }
    }
    return $this->ppAllowParseEmpty? null: false;
  }

  /**
   * normaliser la valeur qui a été analysée par {@link parse()}
   *
   * 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
   *
   * En fonction de l'implémentation, il est possible aussi que {@link parse()}
   * aie analysé une chaine syntaxiquement correcte mais invalide. dans ce cas,
   * cette fonction doit retourner false
   */
  function verifixConvert(&$value): bool {
    return true;
  }
}
