<?php
namespace nur\data\types;

use nur\b\ValueException;
use nur\str;

class RawStringType extends AbstractSimpleType {
  use _Tparametrable;

  const TITLE = "chaine de caractères verbatim";

  const TRIM = false;
  const ALLOW_EMPTY = null;

  /**
   * @var bool si on doit trimmer la valeur, faut-il normaliser les caractères
   * de fin de ligne?
   */
  const NORM_LINES = true;

  /**
   * @var bool si on doit trimmer la valeur, faut-il trimmer chaque ligne de
   * façon indépendante?
   */
  const TRIM_LINES = false;

  /** @var string description de la valeur */
  const KIND = "value";

  const PARAMETRABLE_PARAMS_SCHEMA = [
    "allowed_values" => ["array", null, "valeurs autorisées"],
    "change_case" => ["?string", null, "faut-il transformer la chaine: upper, upper1, lower"],
  ];

  /** @var ?array */
  protected $ppAllowedValues;

  function pp_setAllowedValues(array $allowedValues): self {
    # classer les chaines par ordre inverse de taille
    $sizes = [];
    foreach ($allowedValues as $value) {
      $sizes[$value] = strlen($value);
    }
    arsort($sizes);
    $this->ppAllowedValues = array_keys($sizes);
    return $this;
  }

  /** @var ?string */
  protected $ppChangeCase;

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

  function beforeCheckInstance(&$value): bool {
    if (is_array($value)) $value = str::join3($value);
    if ($this->ppChangeCase !== null && is_string($value)) {
      switch ($this->ppChangeCase) {
      case "upper":
      case "uc":
        $value = str::upper($value);
        break;
      case "upper1":
      case "u1":
        $value = str::upper1($value);
        break;
      case "upperw":
      case "uw":
        $value = str::upperw($value);
        break;
      case "lower":
      case "lc":
        $value = str::lower($value);
        break;
      case "lower1":
      case "l1":
        $value = str::lower1($value);
        break;
      }
    }
    return true;
  }

  function isInstance($value, bool $strict=false): bool {
    if ($value !== null && !is_string($value)) return false;
    if ($value !== null && $this->ppAllowedValues !== null) {
      if (!in_array($value, $this->ppAllowedValues)) return false;
    }
    return true;
  }

  static function verifix_trim(string $value, bool $norm_lines, bool $trim_lines): string {
    if ($trim_lines) {
      $lines = [];
      foreach (str::split_nl($value) as $line) {
        $lines[] = trim($line);
      }
      return implode("\n", $lines);
    } elseif ($norm_lines) {
      return str::norm_nl(trim($value));
    } else {
      return trim($value);
    }
  }

  /** trim normalise aussi les caractères de fin de ligne */
  function verifixTrim(string $value): string {
    return self::verifix_trim($value, static::NORM_LINES, static::TRIM_LINES);
  }

  function parse(string &$input) {
    $allowedValues = $this->ppAllowedValues;
    if ($allowedValues) {
      $found = false;
      foreach ($allowedValues as $allowedValue) {
        if ($input === $allowedValue) {
          $found = true;
          $value = $allowedValue;
          $input = "";
          break;
        } elseif (str::_starts_with($allowedValue, $input)) {
          $found = true;
          $value = $allowedValue;
          $input = substr($input, strlen($value) + 1);
          break;
        }
      }
      if (!$found) {
        throw ValueException::unexpected_value($input, $allowedValues, static::KIND);
      }
    } else {
      $value = $input;
      $input = "";
    }
    return $value;
  }

  function verifixReplaceEmpty(&$value): void {
    if (!$this->ppAllowEmpty && $this->ppAllowNull) $value = null;
  }
}
