<?php
namespace nur\m\base;

use nur\A;
use nur\date;
use nur\func;
use nur\m\IRowIncarnation;
use nur\m\sql_date;

abstract class AbstractRowIncarnation implements IRowIncarnation {
  static function fix_date_binding(&$value) {
    if (date::is_datetime($value)) {
      $value = sql_date::from_datetime($value);
    } elseif (date::is_date($value)) {
      $value = sql_date::from_date($value);
    }
  }

  static function fix_date_col(&$value) {
    if (sql_date::is_date($value)) {
      $value = sql_date::to_date($value);
    } elseif (sql_date::is_datetime($value)) {
      $value = sql_date::to_datetime($value);
    }
  }

  /**
   * faut-il corriger les valeurs des champs avant de les fournir à la base de
   * données
   */
  protected function FIX_BINDINGS(): bool {
    return static::FIX_BINDINGS;
  } const FIX_BINDINGS = true;

  /** faut-il force la casse des noms des champs en minuscule? */
  protected function FORCE_LOWERCASE(): bool {
    return static::FORCE_LOWERCASE;
  } const FORCE_LOWERCASE = true;

  /**
   * faut-il corriger les valeurs des champs qui viennent de la base de données?
   */
  protected function FIX_ROWS(): bool {
    return static::FIX_ROWS;
  } const FIX_ROWS = true;

  /** retourner la classe des lignes */
  protected function ROW_CLASS(): ?string {
    return static::ROW_CLASS;
  } const ROW_CLASS = null;

  /**
   * nom d'une fonction permettant de construire un identifiant pour une ligne,
   * à retourner en tant que clé par l'itérateur ou dans un tableau.
   *
   * - utiliser la syntaxe ["static_method"] ou [null, "static_method"] pour
   * indiquer que la fonction est en réalité [$ROW_CLASS, "static_method"]
   * - utiliser la syntaxe ["->method"] ou [anything, "->method"] pour indiquer
   * qu'il faut d'abord instancier $ROW_CLASS($row) avant d'appeler la méthode
   */
  protected function GENKEY_FUNC(): ?array {
    return static::GENKEY_FUNC;
  } const GENKEY_FUNC = null;

  function createBindings(?array &$bindings, ?array $filter, $row=null, ?array $results=null): void {
    A::merge($bindings, $filter, $row, $results);
  }

  function loadResults(?array &$results, array $bindings): void {
    if ($results === null) return;
    foreach ($results as $key => &$value) {
      $value = A::get($bindings, $key);
    }; unset($value);
  }

  /** corriger une valeur avant de la fournir à la base de données */
  protected function fixBinding(&$value, $key=null): void {
  }

  function prepareBindings(?array &$bindings): void {
    if ($bindings === null || !$this->FIX_BINDINGS()) return;
    foreach ($bindings as $key => &$value) {
      $this->fixBinding($value, $key);
    }; unset($value);
  }

  /** corriger une valeur provenant de la base de données */
  protected function fixCol(&$value, $key=null): void {
  }

  function loadRow(&$row, &$rowKey): void {
    if (!is_array($row)) return;
    if ($this->FORCE_LOWERCASE()) {
      $row = array_change_key_case($row, CASE_LOWER);
    }
    if ($this->FIX_ROWS()) {
      foreach ($row as $key => &$value) {
        $this->fixCol($value, $key);
      }; unset($value);
    }
    $row_class = $this->ROW_CLASS();
    $genkey_func = $this->GENKEY_FUNC();
    if ($row_class !== null) {
      if ($genkey_func !== null) {
        func::fix_static($genkey_func, $row_class);
        if (func::is_method($genkey_func)) {
          $row = func::cons($row_class, $row);
          func::fix_method($genkey_func, $row);
          $rowKey = func::call($genkey_func);
        } else {
          $rowKey = func::call($genkey_func, $row);
          $row = func::cons($row_class, $row);
        }
      } else {
        $row = func::cons($row_class, $row);
      }
    } elseif ($genkey_func !== null) {
      $rowKey = func::call($genkey_func, $row);
    }
  }
}