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

use ArrayAccess;
use IteratorAggregate;
use nur\sery\wip\schema\_assoc\AssocValue;
use nur\sery\wip\schema\_list\ListValue;
use nur\sery\wip\schema\_scalar\ScalarValue;
use nur\sery\wip\schema\types\IType;

abstract class Value implements ArrayAccess, IteratorAggregate {
  function isAssoc(?AssocValue &$assoc=null): bool { return false; }
  function isList(?ListValue &$list=null): bool { return false; }
  function isScalar(?ScalarValue &$scalar=null): bool { return false; }

  /** spécifier la valeur destination gérée par cet objet */
  abstract function reset(&$dest, $destKey=null, ?bool $verifix=null): self;

  /**
   * Obtenir la liste des clés valides pour les valeurs accessibles via cet
   * objet
   */
  abstract function getKeys(): array;

  /** obtenir un objet pour gérer la valeur spécifiée */
  abstract function getValue($key=null): Value;

  function getIterator() {
    foreach ($this->getKeys() as $key) {
      yield $key => $this->getValue($key);
    }
  }

  /**
   * obtenir le résultat de l'appel d'une des fonctions {@link set()} ou
   * {@link unset()}
   */
  abstract function getResult(): Result;

  /** retourner true si la valeur existe */
  abstract function isPresent(): bool;

  /** retourner le type associé à la valeur */
  abstract function getType(): IType;

  /** retourner true si la valeur est disponible */
  abstract function isAvailable(): bool;

  /** retourner true si la valeur est valide */
  abstract function isValid(): bool;

  /** retourner true si la valeur est dans sa forme normalisée */
  abstract function isNormalized(): bool;

  /** obtenir la valeur */
  abstract function get($default=null);

  /** remplacer la valeur */
  abstract function set($value): self;

  /** supprimer la valeur */
  abstract function unset(): self;

  /** formatter la valeur pour affichage */
  abstract function format($format=null): string;

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

  function offsetExists($offset): bool {
    return in_array($offset, $this->getKeys());
  }

  function offsetGet($offset) {
    return $this->getValue($offset);
  }

  function offsetSet($offset, $value): void {
    $this->getValue($offset)->set($value);
  }

  function offsetUnset($offset): void {
    $this->getValue($offset)->unset();
  }
}