<?php
namespace nulib\schema\input;

use nulib\php\access\IAccess;
use nulib\php\access\KeyAccess;
use nulib\php\access\PropertyAccess;
use nulib\ref\schema\ref_input;
use nulib\StateException;

/**
 * Class Input: accès à une valeur
 *
 * cette implémentation lit depuis et écrit dans une référence
 */
class Input {
  const ALLOW_EMPTY = true;

  private static function unexpected_access_type(): StateException {
    return StateException::unexpected_state("access_type");
  }

  function __construct(&$dest=null, ?array $params=null) {
    $accessType = $params["access_type"] ?? ref_input::ACCESS_AUTO;
    if ($accessType === ref_input::ACCESS_AUTO) {
      $accessType = is_object($dest)? ref_input::ACCESS_PROPERTY : ref_input::ACCESS_KEY;
    }

    $allowEmpty = $params["allow_empty"] ?? static::ALLOW_EMPTY;
    if ($accessType == ref_input::ACCESS_PROPERTY) {
      $this->access = new PropertyAccess($dest, null, [
        "allow_empty" => $allowEmpty,
        "allow_null" => true,
      ]);
    } elseif ($accessType == ref_input::ACCESS_KEY) {
      $this->access = new KeyAccess($dest, null, [
        "allow_empty" => $allowEmpty,
        "allow_null" => true,
      ]);
    } else {
      throw self::unexpected_access_type();
    }
  }

  protected IAccess $access;

  /** tester si la valeur existe sans tenir compte de $allowEmpty */
  function isPresent($key=null): bool {
    return $this->access->resetKey($key)->exists();
  }

  /** tester si la valeur est disponible en tenant compte de $allowEmpty */
  function isAvailable($key=null): bool {
    return $this->access->resetKey($key)->available();
  }

  function get($key=null) {
    return $this->access->resetKey($key)->get();
  }

  function set($value, $key=null): void {
    $this->access->resetKey($key)->set($value);
  }

  function unset($key=null): void {
    $this->access->resetKey($key)->del();
  }

  function addKey($key): Input {
    if ($key === null) return $this;
    $input = clone $this;
    $input->access = $this->access->addKey($key);
    return $input;
  }

  function ensureAssoc(array $keys, ?array $params=null): void {
    $this->access->ensureAssoc($keys, $params);
  }

  function ensureKeys(array $defaults, ?array $missings, ?array $params=null): void {
    $this->access->ensureKeys($defaults, $missings, $params);
  }

  function deleteMissings(array $missings, ?array $params=null): void {
    $this->access->deleteMissings($missings, $params);
  }

  function ensureOrder(array $keys, ?array $params=null): void {
    $this->access->ensureOrder($keys, $params);
  }
}
