<?php
namespace nulib\schema\types;

use nulib\cl;
use nulib\php\types\vbool;
use nulib\schema\_scalar\ScalarSchema;
use nulib\schema\input\Input;
use nulib\schema\Result;
use nulib\schema\Schema;
use nulib\ValueException;
use nur\prop;

class tbool extends _tformatable {
  const NAME = "bool";

  const ALIASES = ["boolean"];

  function getClass(): string {
    return "bool";
  }

  function getMissingValue(?bool &$valid=null) {
    $valid = false;
    return null;
  }

  function getNullValue() {
    return $this->nullable? null: false;
  }

  public function getNbStates(?array &$states=null): int {
    if ($this->nullable) {
      $states = [false, true, null];
      return 3;
    } else {
      $states = [false, true];
      return 2;
    }
  }

  function isAvailable(Input $input, $valueKey): bool {
    return $input->isAvailable($valueKey);
  }

  function isValid($value, ?bool &$normalized=null): bool {
    $normalized = is_bool($value);
    return is_scalar($value);
  }

  function extract(string $value): string {
    return trim($value);
  }

  function parse(string $value) {
    if (vbool::is_yes($value)) return true;
    elseif (vbool::is_no($value)) return false;
    throw new ValueException("une valeur booléenne est attendue");
  }

  /**
   * @var ScalarSchema $schema
   */
  function normalize(&$value, Result $result, Schema $schema): bool {
    if ($result->normalized) {
    } elseif (is_bool($value)) {
      $result->setNormalized();
    } elseif (is_scalar($value)) {
      $value = boolval($value);
      return true;
    }
    return false;
  }

  const OUINON_FORMAT = ["Oui", "Non", false];
  const OUINONNULL_FORMAT = ["Oui", "Non", ""];
  const ON_FORMAT = ["O", "N", false];
  const ONN_FORMAT = ["O", "N", ""];
  const XN_FORMAT = ["X", "", false];
  const OZ_FORMAT = ["1", "", false];
  const FORMATS = [
    "ouinon" => self::OUINON_FORMAT,
    "ouinonnull" => self::OUINONNULL_FORMAT,
    "on" => self::ON_FORMAT,
    "onn" => self::ONN_FORMAT,
    "xn" => self::XN_FORMAT,
    "oz" => self::OZ_FORMAT,
  ];

  const FORMAT = self::OUINON_FORMAT;

  function format($value, $format=null): string {
    $format ??= $this->params["format"] ?? static::FORMAT;
    if (is_string($format)) {
      $oformat = $format;
      $format = cl::get(self::FORMATS, strtolower($oformat));
      if ($format === null) throw ValueException::invalid_kind($oformat, "format");
    }
    if ($value === null) {
      $null = $format[2];
      if ($null !== false) return $null;
    }
    return $value? $format[0]: $format[1];
  }

  function getGetterName(string $name): string {
    return prop::get_getter_name($name, !$this->nullable);
  }
}
