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

use nur\sery\schema\input\Input;
use nur\sery\schema\ref\ref_analyze;
use nur\sery\schema\Result;
use nur\sery\schema\types\IType;
use nur\sery\schema\Value;
use nur\sery\schema\ValueException;

class ScalarValue extends Value {
  function __construct(ScalarSchema $schema, &$dest=null, $key=null, bool $verifix=true) {
    $this->schema = $schema;
    $this->result = new ScalarResult();
    $this->reset($dest, $key, $verifix);
  }

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

  /** @var ScalarSchema schéma de cette valeur */
  protected $schema;

  /** @var Input source et destination de la valeur */
  protected $input;

  /** @var string|int clé de la valeur dans le tableau destination */
  protected $destKey;

  /** @var IType */
  protected $type;

  /** @var ?ScalarResult résultat de l'analyse de la valeur */
  protected $result;

  function reset(&$dest, $destKey=null, bool $verifix=true): Value {
    if ($dest instanceof Input) $input = $dest;
    else $input = new Input($dest);
    $this->input = $input;
    $this->destKey = $destKey;
    $this->result = null;
    #XXX résoudre les types ici?
    if ($verifix) $this->verifix();
    else $this->_analyze();
    return $this;
  }

  function getKeys(): array {
    return [null];
  }

  function getValue($key=null): Value {
    if ($key === null) return $this;
    throw ValueException::invalid_key($key);
  }

  /** analyser la valeur et résoudre son type */
  function _analyze(): int {
    $schema = $this->schema;
    $input = $this->input;
    $destKey = $this->destKey;
    $result = $this->result;
    $result->reset();
    #XXX résoudre le type
    if (!$input->isAvailable()) return $result->setUnavailable($schema);
    $value = $input->get($destKey);
    if ($value === null) return $result->setNull($schema);
  }

  function isPresent(): bool {
    return $this->input->isPresent($this->destKey);
  }

  function isAvailable(): bool {
    return $this->input->isAvailable($this->destKey);
  }

  function get($default=null) {
    $destKey = $this->destKey;
    $input = $this->input;
    if ($input->isAvailable($destKey)) return $input->get($destKey);
    else return $default;
  }

  function set($value, bool $verifix=true): Value {
    $this->input->set($value, $this->destKey);
    if ($verifix) $this->verifix();
    return $this;
  }

  function getType(): IType {
    if ($this->type === null) $this->type = $this->schema->getType($this->destKey);
    return $this->type;
  }

  function isValid(): bool {
  }

  function isNormalized(): bool {
  }

  /**
   * analyser, corriger éventuellement et normaliser la valeur
   *
   * si la valeur était déjà normalisée, retourner false.
   */
  function verifix(bool $throw=true, ?Result &$result=null): bool {
    $type = $this->getType();
    $key = $this->destKey;
    if ($key === null) $modified = $type->verifix($this->input, $throw, $result);
    else $modified = $type->verifix($this->input[$key], $throw, $result);
    $this->result = $result;
    return $modified;
  }

  function parse($value, bool $throw=true, ?Result &$result=null) {
    $this->getType()->verifix($value, $throw, $result);
    $this->set($value);
    $this->result = $result;
    return $value;
  }

  function format(?string $format=null): string {
    $type = $this->getType();
    $key = $this->destKey;
    if ($key === null) return $type->format($this->input, $format);
    else return $type->format($this->input[$key], $format);
  }
}
