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

use ArrayAccess;
use IteratorAggregate;
use nur\sery\wip\schema\_assoc\AssocWrapper;
use nur\sery\wip\schema\_list\ListWrapper;
use nur\sery\wip\schema\_scalar\ScalarWrapper;
use nur\sery\wip\schema\types\IType;

abstract class Wrapper implements ArrayAccess, IteratorAggregate {
  function isAssoc(?AssocWrapper &$wrapper=null): bool { return false; }
  function isList(?ListWrapper &$wrapper=null): bool { return false; }
  function isScalar(?ScalarWrapper &$wrapper=null): bool { return false; }

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

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

  /**
   * sélectionner le wrapper associé à la clé spécifiée
   *
   * @param string|int|null $key
   * @return Wrapper $this
   */
  abstract function select($key): Wrapper;

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

  /**
   * 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->select($offset);
  }

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

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