<?php
namespace nur\b\coll;

use nur\A;
use nur\data\types\Metadata;

/**
 * Class FancyArray: une implémentation de array avec support d'un schéma et de
 * méthodes automatiques
 */
abstract class FancyArray extends IterableArray {
  use TAutomethods;

  const _AUTO_GETTERS = null;
  const _AUTO_SETTERS = null;
  const _AUTO_DELETERS = null;
  const _AUTO_CI_GETTERS = null;
  const _AUTO_CI_SETTERS = null;

  /** @var array */
  const SCHEMA = null;

  /**
   * retourner le gestionnaire de schéma. la façon la plus simple est d'utiliser
   * le trait {@link TArrayMd} qui maintient un cache partagé entre toutes les
   * instances de cette classe.
   */
  protected function md(): ?Metadata {
    $schema = static::SCHEMA;
    if ($schema === null) return null;
    else return new Metadata($schema);
  }

  /**
   * le cas échéant, s'assurer que $data est conforme au schéma. cette méthode
   * est prévue pour être appelée dans le constructeur. la façon la plus simple
   * est d'utiliser le trait {@link TArrayMd} qui maintient un cache partagé entre
   * toutes les instances de cette classe.
   *
   * @param mixed $key le cas échéant, clé associée à $data si cette valeur
   * provient d'un tableau.
   */
  protected function ensureData($data, $key=null): array {
    $md = $this->md();
    if ($md !== null) $md->ensureSchema($data, $key);
    else A::ensure_array($data);
    return $data;
  }

  function __construct($data=null, $key=null) {
    parent::__construct($this->ensureData($data, $key));
  }

  # Modèle d'implémentation dans une classe dérivée:
  ## ArrayAccess
  #function has(KeyType $key): bool { return $this->_has($key); }
  #function &get(KeyType $key, ?ValueType $default=null): ?ValueType { return $this->_get($key, $default); }
  #function set(KeyType $key, ?ValueType $value): self { return $this->_set($key, $value); }
  #function add(?ValueType $value): self { return $this->_set(null, $value); }
  #function del(KeyType $key): self { return $this->_del($key); }
  ## Iterator
  #function key(): KeyType { return $this->_key(); }
  #function current(): ?ValueType { return $this->_current(); }

  # Par exemple, pour un array générique, on aurait:
  ## ArrayAccess
  #function has($key): bool { return $this->_has($key); }
  #function &get($key, $default=null) { return $this->_get($key, $default); }
  #function set($key, $value): self { return $this->_set($key, $value); }
  #function add($value): self { return $this->_set(null, $value); }
  #function del($key): self { return $this->_del($key); }
  ## Iterator
  #function key() { return $this->_key(); }
  #function current() { return $this->_current(); }
}
