<?php
namespace nur\sery\wip\schema\_scalar;

use nulib\ref\schema\ref_schema;
use nur\sery\wip\schema\Schema;
use nur\sery\wip\schema\types\IType;
use nur\sery\wip\schema\Wrapper;

/**
 * Class ScalarSchema
 * 
 * @property-read array|IType $type
 * @property-read mixed $default
 * @property-read string|null $title
 * @property-read bool $required
 * @property-read bool $nullable
 * @property-read string|array|null $desc
 * @property-read callable|null $analyzerFunc
 * @property-read callable|null $extractorFunc
 * @property-read callable|null $parserFunc
 * @property-read callable|null $normalizerFunc
 * @property-read array|null $messages
 * @property-read callable|null $formatterFunc
 * @property-read mixed $format
 * @property-read array $nature
 * @property-read array|null $schema
 * @property-read string|int|null $name
 * @property-read string|array|null $pkey
 * @property-read string|null $header
 * @property-read bool|null $composite
 */
class ScalarSchema extends Schema {
  /** @var array meta-schema d'un schéma de nature scalaire */
  const METASCHEMA = ref_schema::SCALAR_METASCHEMA;

  /**
   * indiquer si $definition est une définition de schéma scalaire que
   * {@link normalize()} pourrait normaliser
   */
  static function isa_definition($definition): bool {
    # chaine ou null
    if ($definition === null) return true;
    if (is_string($definition)) return true;
    if (!is_array($definition)) return false;
    # nature explicitement spécifiée
    if (array_key_exists("", $definition)) {
      $nature = $definition[""];
      if ($nature === "scalar") return true;
      if (is_array($nature)
        && array_key_exists(0, $nature)
        && $nature[0] === "scalar") {
        return true;
      }
      return false;
    }
    # un unique élément chaine à l'index 0
    $count = count($definition);
    $haveIndex0 = array_key_exists(0, $definition);
    if ($count == 1 && $haveIndex0
      && ($definition[0] === null || is_string($definition[0]))) {
      return true;
    }
    # un élément à l'index 0, et d'autres éléments
    return $haveIndex0 && $count > 1;
  }

  static function normalize($definition, $definitionKey=null): array {
    self::_normalize($definition, $definitionKey);
    self::_ensure_nature($definition, "scalar");
    return $definition;
  }

  function __construct($definition=null, $definitionKey=null, bool $normalize=true) {
    if ($definition === null) $definition = static::SCHEMA;
    if ($normalize) {
      $definition = self::normalize($definition, $definitionKey);
      $this->_definition = $definition;
      self::_ensure_type($definition);
      self::_ensure_schema_instances($definition);
    }
    $this->definition = $definition;
  }

  function isScalar(?ScalarSchema &$schema=null): bool {
    $schema = $this;
    return true;
  }

  protected function newWrapper(): ScalarWrapper {
    return new ScalarWrapper($this);
  }

  function getWrapper(&$value=null, $valueKey=null, ?Wrapper &$wrapper=null): ScalarWrapper {
    if (!($wrapper instanceof ScalarWrapper)) $wrapper = $this->newWrapper();
    return $wrapper->reset($value, $valueKey);
  }

  #############################################################################
  # key & properties

  const _PROPERTY_PKEYS = [
    "analyzerFunc" => "analyzer_func",
    "extractorFunc" => "extractor_func",
    "parserFunc" => "parser_func",
    "normalizerFunc" => "normalizer_func",
    "formatterFunc" => "formatter_func",
    "nature" => ["", 0],
  ];
}
