<?php
namespace nur\b\values;

use ArrayAccess;
use nur\A;
use nur\base;
use nur\data\types\md_utils;
use nur\data\types\Metadata;
use nur\md;
use nur\oprop;
use nur\session;
use nur\str;
use nur\valx;

/**
 * Class Mparams: mixin pour toutes les classes qui accèdent à des paramètres
 *
 * XXX vérifier si les méthodes ont toujours lieu d'être
 */
class Mparams {
  static final function fix_param2($value) {
    if (is_array($value)) {
      $vs = [];
      foreach ($value as $v) {
        $v = base::vn(str::trim($v));
        if (base::nz($v)) $vs[] = $v;
      }
      if ($vs) $value = $vs;
      else $value = false;
    } else {
      $value = base::vn(str::trim($value));
    }
    return $value;
  }

  static final function fix_param($value) {
    if (is_array($value)) {
      $vs = [];
      foreach ($value as $v) {
        $v = base::vn(str::trim($v));
        if (base::nz($v)) $vs[] = $v;
      }
      if ($vs) $value = $vs;
      else $value = null;
    } else {
      $value = base::vn(str::trim($value));
    }
    return $value;
  }

  /**
   * Obtenir un paramètre de formulaire.
   *
   * La valeur est transformée en chaine, trimmée, et si elle est vide,
   * remplacée par null. En définitive, la valeur finale est false s'il n'y
   * avait pas de valeur, null si la valeur était vide.
   *
   * Si la valeur est un tableau, les valeurs du tableau sont transformées en
   * chaine, trimmées et supprimées si elle sont vides. si le tableau se
   * retrouve sans élément, il est remplacé par false.
   */

  static final function get_param2($name, $default=false) {
    if (!static::has($name)) return $default;
    $value = static::get($name);
    return self::fix_param2($value);
  }

  /**
   * Obtenir un paramètre de formulaire.
   *
   * La différence avec get_param2() est qu'on ne distingue pas paramètre vide
   * ou inexistant: le résultat est toujours null dans ce cas. De même, si la
   * valeur est un tableau et que toutes les éléments sont vides, la valeur
   * finale est null.
   *
   * Utiliser cette variante quand on ne se préoccupe pas de faire la
   * différence entre paramètre vide et paramètre non fourni.
   */
  static final function get_param($name, $default=null) {
    $value = static::get($name, $default);
    return self::fix_param($value);
  }

  const PARAM2_NAME_SCHEMA = [
    "name" => [null, null, "nom du paramètre"],
    "default" => [null, false, "valeur par défaut du paramètre"],
  ];

  private static $param2_name_md;
  private static function param2_name_md(): Metadata {
    return md_utils::ensure_md(self::$param2_name_md, self::PARAM2_NAME_SCHEMA);
  }

  /**
   * Obtenir des paramètres de formulaire, selon les règles de get_param2()
   *
   * chaque élément de $names peut être une chaine $name ou un array avec la
   * structure [$name, $default=null]
   */
  static final function get_params2(...$names): array {
    $params = [];
    foreach ($names as $nd) {
      [$name, $default] = md::get_values($nd, self::PARAM2_NAME_SCHEMA);
      $params[$name] = self::get_param2($name, $default);
    }
    return $params;
  }

  /**
   * Initialiser un objet avec des paramètres de formulaire.
   *
   * Les règles sont les mêmes que pour get_params2(), mais au lieu de retourner
   * le résultat, initialiser les clés (si c'est un tableau) ou les propriétés
   * (si c'est un objet) du même nom dans l'objet destination.
   */
  static final function set_params2(&$obj, ...$names): void {
    $params = self::get_params2(...$names);
    valx::set_values($obj, $params);
  }

  ##

  /**
   * Obtenir les paramètres avec get_params2() et corriger les valeurs avec
   * A::verifix_values()
   */
  static final function get_verifixed_params2($verifixers): array {
    $names = array_keys($verifixers);
    $params = self::get_params2(...$names);
    A::verifix_values($params, $verifixers);
    return $params;
  }

  /**
   * Obtenir les paramètres avec get_params2(), corriger les valeurs avec
   * A::verifix_values(), puis initialiser l'objet comme avec set_params2()
   */
  static final function set_verifixed_params2(&$obj, $verifixers): void {
    $names = array_keys($verifixers);
    $params = self::get_params2(...$names);
    A::verifix_values($params, $verifixers);
    valx::set_values($obj, $params);
  }

  ##

  const PARAM_NAME_SCHEMA = [
    "name" => [null, null, "nom du paramètre"],
    "default" => [null, null, "valeur par défaut du paramètre"],
  ];

  private static $param_name_md;
  private static function param_name_md(): Metadata {
    return md_utils::ensure_md(self::$param_name_md, self::PARAM_NAME_SCHEMA);
  }

  /**
   * Obtenir des paramètres de formulaire, selon les règles de get_param()
   */
  static final function get_params(...$names): array {
    $param_name_md = self::param_name_md();
    $params = [];
    foreach ($names as $nd) {
      $param_name_md->ensureSchema($nd);
      ["name" => $name, "default" => $default] = $param_name_md->getValues($nd);
      $params[$name] = self::get_param($name, $default);
    }
    return $params;
  }

  /**
   * Initialiser un objet avec des paramètres de formulaire.
   *
   * Les règles sont les mêmes que pour get_params(), mais au lieu de retourner
   * le résultat, initialiser les clés (si c'est un tableau) ou les propriétés
   * (si c'est un objet) du même nom dans l'objet destination.
   */
  static final function set_params(&$obj, ...$names): void {
    $params = self::get_params(...$names);
    valx::set_values($obj, $params);
  }

  ##

  /**
   * Obtenir les paramètres avec get_params() et corriger les valeurs avec
   * A::verifix_values()
   */
  static final function get_verifixed_params($verifixers): array {
    $names = array_keys($verifixers);
    $params = self::get_params(...$names);
    A::verifix_values($params, $verifixers);
    return $params;
  }

  /**
   * Obtenir les paramètres avec get_params(), corriger les valeurs avec
   * A::verifix_values(), puis initialiser l'objet comme avec set_params()
   */
  static final function set_verifixed_params(&$obj, $verifixers): void {
    $names = array_keys($verifixers);
    $params = self::get_params(...$names);
    A::verifix_values($params, $verifixers);
    valx::set_values($obj, $params);
  }

  ##

  /**
   * Obtenir le paramètre spécifié.
   *
   * Si le paramètre n'est pas fourni, prendre la variable de session du même
   * nom si elle existe. Si la valeur est non nulle, mettre à jour la session
   * avec la valeur du paramètre.
   */
  static final function sget($name, $default=null, $session_key=null, $trim=false) {
    if ($session_key === null) $session_key = $name;
    if (static::has($name)) {
      $value = static::get($name, $default, $trim);
      if ($trim && base::f($value)) $value = null;
    } else {
      $value = session::get($session_key, $default);
    }
    if (base::nz($value)) session::set($session_key, $value);
    return $value;
  }

  ##

  /**
   * comme get_param2() avec les spécificités de sget()
   *
   * - si le paramètre n'est pas fourni, prendre la variable de session du même
   *   nom si elle existe.
   * - si le paramètre est null, supprimer la variable de session
   * - sinon mettre à jour la session avec la valeur du paramètre.
   */
  static final function sget_param2($name, $default=false, $session_key=null) {
    if ($session_key === null) $session_key = $name;
    $value = self::get_param2($name, false);
    if ($value === false) $value = session::get($session_key, $default);
    if ($value === null) session::del($session_key);
    elseif ($value !== false) session::set($session_key, $value);
    return $value;
  }

  const SPARAM2_NAME_SCHEMA = [
    "name" => [null, null, "nom du paramètre"],
    "default" => [null, false, "valeur par défaut du paramètre"],
    "session_key" => [null, null, "nom de la clé dans la session"],
  ];

  /**
   * Obtenir des paramètres de formulaire, selon les règles de sget_param2()
   *
   * chaque élément de $names peut avoir la structure
   * [$name, $default=false, $session_key=null]
   */
  static final function sget_params2(...$names): array {
    $params = [];
    foreach ($names as $nds) {
      [$name, $default, $session_key] = md::get_values($nds, self::SPARAM2_NAME_SCHEMA);
      $params[$name] = self::sget_param2($name, $default, $session_key);
    }
    return $params;
  }

  /** comme set_params2() avec les spécificités de sget() */
  static final function sset_params2(&$obj, ...$names): void {
    $params = self::sget_params2(...$names);
    valx::set_values($obj, $params);
  }

  ##

  /**
   * comme get_param() avec les spécificités de sget()
   *
   * Si le paramètre est vide ou inexistant, prendre la variable de session du
   * même nom si elle existe. Si la valeur est non nulle, mettre à jour la
   * session avec la valeur du paramètre.
   */
  static final function sget_param($name, $default=null, $session_key=null) {
    if ($session_key === null) $session_key = $name;
    $value = self::get_param($name, null);
    if ($value === null) $value = session::get($session_key, $default);
    if (base::nz($value)) session::set($session_key, $value);
    return $value;
  }

  const SPARAM_NAME_SCHEMA = [
    "name" => [null, null, "nom du paramètre"],
    "default" => [null, null, "valeur par défaut du paramètre"],
    "session_key" => [null, null, "nom de la clé dans la session"],
  ];

  /**
   * Obtenir des paramètres de formulaire, selon les règles de sget_param()
   *
   * chaque élément de $names peut avoir la structure
   * [$name, $default=null, $session_key=null]
   */
  static final function sget_params(...$names): array {
    $params = [];
    foreach ($names as $nds) {
      [$name, $default, $session_key] = md::get_values($nds, self::SPARAM_NAME_SCHEMA);
      $params[$name] = self::sget_param($name, $default, $session_key);
    }
    return $params;
  }

  /** comme set_params() avec les spécificités de sget() */
  static final function sset_params(&$obj, ...$names): void {
    $params = self::sget_params(...$names);
    valx::set_values($obj, $params);
  }

  ##

  /** Supprimer les clés spécifiées dans l'objet destination ET dans la session */
  static final function sclear(&$obj, ...$names): void {
    if (is_array($obj) || $obj === null) {
      if ($obj === null) $obj = [];
      foreach ($names as $name) {
        $obj[$name] = null;
        session::del($name);
      }
    } elseif ($obj instanceof ArrayAccess) {
      foreach ($names as $name) {
        $obj[$name] = null;
        session::del($name);
      }
    } else {
      foreach ($names as $name) {
        oprop::set($obj, $name, null);
        session::del($name);
      }
    }
  }
}
