<?php
namespace nulib\schema\types;

use nulib\schema\input\Input;
use nulib\schema\Result;
use nulib\schema\Schema;
use nulib\ValueException;

/**
 * Interface IType: un type de données
 */
interface IType {
  static function get_params_from_definition(?array $definition): ?array;

  /**
   * obtenir, pour information, le nom officiel de ce type, utilisable dans une
   * définition de schéma
   */
  function getName(): string;

  /** obtenir la liste des aliases valides pour ce type */
  function getAliases(): array;

  /**
   * @return string la classe des objets gérés par ce format: le type attendu
   * par {@link format()} et le type retourné par {@link normalize()}
   *
   * Les valeurs "mixed", "bool", "float", "int", "string" et "array" peuvent
   * aussi être retournées, bien qu'elles ne soient pas à proprement parler des
   * classes.
   *
   * La valeur "mixed" signifie qu'il peut s'agir de n'importe quelle classe
   * et/ou que la valeur ne peut pas être déterminée à l'avance.
   */
  function getClass(): string;

  /**
   * comme {@link getClass()} mais peut être utilisé directement comme type dans
   * une déclaration PHP. notamment, si le type est nullable et que
   * $allowNullable==true, il y a "?" devant le nom.
   *
   * Par exemple:
   * - pour un type chaine nullable, {@link getClass()} retournerait "string"
   * alors que cette méthode retournerait "?string".
   * - pour un type mixed, {@link getClass()} retournerait "mixed" alors que
   * cette méthode retournerait null
   */
  function getPhpType(bool $allowNullable=true): ?string;

  /**
   * obtenir la valeur "inexistante" pour les objets de ce type
   *
   * si $valid reçoit la valeur false, il faut ignorer la valeur de retour:
   * cela veut dire qu'il n'y a pas de valeur "inexistant" pour les valeurs de
   * ce type
   */
  function getMissingValue(?bool &$valid=null);

  /** obtenir la valeur "nulle" pour les objets de ce type */
  function getNullValue();

  /**
   * si c'est le type d'une valeur qui ne prendre qu'une liste prédéterminée
   * d'états spécifiques, retourner le nombre d'états possibles, et mettre à
   * jour $states avec les valeurs possibles
   *
   * sinon, retourner 0 et ne pas mettre $states à jour
   */
  function getNbStates(?array &$states=null): int;

  /** la donnée $input($valueKey) est-elle disponible? */
  function isAvailable(Input $input, $valueKey): bool;

  /** la valeur $value est-elle nulle? */
  function isNull($value): bool;

  /**
   * la valeur $value est-elle valide et normalisée le cas échéant?
   *
   * NB: si $value est un string. elle doit avoir déjà été traitée au préalable
   * par extract() et parse()
   */
  function isValid($value, ?bool &$normalized=null): bool;

  /**
   * extraire de la chaine la valeur à analyser
   *
   * @throws ValueException en cas d'erreur d'analyse
   */
  function extract(string $value): string;

  /**
   * analyser la chaine et retourner la valeur "convertie"
   *
   * @throws ValueException en cas d'erreur d'analyse
   */
  function parse(string $value);

  /**
   * normaliser la valeur. elle *doit* déjà être valide.
   * Si $value est un string. elle *doit* avoir déjà été traitée au préalable
   * par extract() et parse()
   *
   * - si $result indique que la valeur est déjà normalisée, cette méthode ne
   * fait rien
   * - si la valeur était déjà normalisée, mettre à jour $result pour indiquer
   * que la valeur est normalisée et retourner false
   * - sinon, retourner true pour indiquer qu'il a fallut normaliser la valeur.
   * $result n'est pas modifié
   */
  function normalize(&$value, Result $result, Schema $schema): bool;

  /**
   * formatter la valeur pour affichage. si $value n'est pas null, elle est
   * garantie d'être du bon type
   */
  function format($value, $format=null): string;

  #############################################################################

  /** @return string le nom d'un getter pour une valeur de ce type */
  function getGetterName(string $name): string;

  /** @return string le nom d'un setter pour une valeur de ce type */
  function getSetterName(string $name): string;

  /** @return string le nom d'un deleter pour une valeur de ce type */
  function getDeleterName(string $name): string;

  /**
   * @return string le nom d'une constante de classe pour une valeur de ce type
   */
  function getClassConstName(string $name): string;

  /**
   * @return string le nom d'une propriété d'une classe pour une valeur de ce
   * type
   */
  function getObjectPropertyName(string $name): string;

  /** @return string le nom d'une clé d'un tableau pour une valeur de ce type */
  function getArrayKeyName(string $name): string;
}
