<?php
namespace nur\v;

use nur\A;
use nur\config;
use nur\v\html5\Html5NavigablePageContainer;
use nur\v\model\IPage;
use nur\v\model\IPageContainer;

class page {
  const CONTAINER_CLASS = Html5NavigablePageContainer::class;

  /** @var string */
  protected static $container_class;

  /**
   * spécifier la classe à utiliser pour instancier le container.
   *
   * cette méthode *doit* être appelée avant d'appeler la méthode container()
   */
  static final function set_container_class(string $container_class) {
    self::$container_class = $container_class;
  }

  /** @var IPageContainer */
  protected static $container;

  /** obtenir l'instance globale de container */
  static final function container(): IPageContainer {
    if (self::$container === null) {
      $container_class = self::$container_class;
      if ($container_class === null) $container_class = static::CONTAINER_CLASS;
      self::$container = new $container_class();
    }
    return self::$container;
  }

  static final function render(?IPage $page=null): void {
    config::set_fact(config::FACT_WEB_APP);
    config::configure(config::CONFIGURE_INITIAL_ONLY);
    $pc = self::container();
    if ($page === null) {
      config::configure(config::CONFIGURE_ROUTES_ONLY);
      $page = route::get_page();
    }
    $pc->setPage($page);
    $pc->print();
  }

  /** @var IPage */
  protected static $page;

  /** spécifier la page courante. cette opération est automatique */
  static final function set_current_page(IPage $page) {
    self::$page = $page;
  }

  private static function _qsbu(string $url, array $params): string {
    if (count($params) > 0 && $params[0] === true) {
      $query_string = $_SERVER["QUERY_STRING"];
      if ($query_string) $url .= "?$query_string";
      $params = array_slice($params, 1);
    }
    return html::bu($url, ...$params);
  }

  /**
   * retourner le chemin de la page courante depuis la racine.
   *
   * cette méthode existe parce que l'url "" renvoie vers la page courante mais
   * avec tous ses paramètres. on veut parfois rediriger vers la page courante
   * sans aucun paramètre.
   *
   * si le premier argument de cette méthode est true, retourner le chemin de la
   * page courante suivie de la valeur de $_SERVER["QUERY_STRING"]
   *
   * dans tous les cas, les arguments de params sont rajoutés
   *
   * @see prefix::get_default_self() pour une information sur l'exactitude de la
   * valeur retournée
   */
  static final function self(...$params): string {
    $self = $_SERVER["SCRIPT_NAME"];
    return self::_qsbu($self, $params);
  }

  /** comme {@link self()} mais avec un chemin absolu */
  static final function origself(...$params): string {
    $self = prefix::get_default_self();
    return self::_qsbu($self, $params);
  }

  /**
   * construire un url avec les paramètres spécifiés. le cas échéant, corriger
   * l'url en fonction de self
   *
   * cette méthode sera utilisée notamment pour des liens vers des resources
   * statiques
   */
  static final function resu(string $url, ...$params): string {
    $prefix = self::$page->getSelfRelativePrefix();
    return html::bu(prefix::add($url, $prefix), ...$params);
  }

  /**
   * construire un url vers le composant destination avec les paramètres
   * spécifiés. le cas échéant, corriger l'url en fonction de self
   *
   * @param string|IPage $dest composant destination
   */
  static final function dynu($dest, ...$params): string {
    $prefix = self::$page->getSelfRelativePrefix();
    $url = route::get_path($dest);
    return html::bu(prefix::add($url, $prefix), ...$params);
  }

  const BU_STATIC_PARTS = ["scheme", "host", "port", "query", "fragment"];

  /**
   * méthode générique pour construire une url. utiliser des heuristiques pour
   * déterminer si la destination est une url statique ou si c'est un composant
   */
  static final function bu($dest, ...$params): string {
    $dyn = true;
    if (is_object($dest)) $dest = get_class($dest);
    if (is_string($dest)) {
      # heuristiques pour déterminer si c'est une url statique
      $parts = parse_url($dest);
      foreach (self::BU_STATIC_PARTS as $part) {
        if (A::has($parts, $part)) {
          # y a-t-il au moins une partie d'une url statique?
          $dyn = false;
          break;
        }
      }
      if ($dyn) {
        # s'il n'y a qu'un chemin, tester si ça ressemble à une url
        $path = A::get($parts, "path", "");
        if ($path === "" || $path === ".") $dyn = false;
        elseif (strpos($path, "/") !== false) $dyn = false;
        elseif (preg_match('/\.(php|html)\b/', $path)) $dyn = false;
      }
    }
    if ($dyn) {
      # tenir compte du préfixe, puisque les chemins des composants sont
      # toujours exprimés depuis la racine
      $prefix = self::$page->getSelfRelativePrefix();
      if ($dest !== null) {
        $url = route::get_path($dest);
        $url = prefix::add($url, $prefix);
      } else {
        $url = $prefix;
      }
      return html::bu($url, ...$params);
    } else {
      # ne pas corriger une url statique: on fait confiance à l'utilisateur pour
      # utiliser des chemins relatifs
      return html::bu($dest, ...$params);
    }
  }

  static final function no_cache(): void {
    http::no_cache();
  }

  static final function content_type(string $content_type=null, string $charset=null): void {
    http::content_type($content_type, $charset);
  }

  static final function download_as($filename, string $disposition=null): void {
    http::download_as($filename, $disposition);
  }

  private static final function _get_url($dest): string {
    if ($dest === null) $url = self::self();
    elseif ($dest === true || is_array($dest)) $url = self::self($dest);
    else $url = self::bu($dest);
    return $url;
  }

  static final function redirect($dest=null, bool $exit_now=true): void {
    http::redirect(self::_get_url($dest), $exit_now);
  }

  static final function refresh($dest=null, int $delay=1, bool $exit_now=true): void {
    http::refresh(self::_get_url($dest), $delay, $exit_now);
  }

  static final function send_no_content(bool $exit_now=true): void {
    http::send_no_content($exit_now);
  }

  static final function send_file(string $file, bool $delete=false, bool $close_session=true, bool $add_content_length=true, bool $exit_now=true): bool {
    return http::send_file($file, $delete, $close_session, $add_content_length, $exit_now);
  }


  static function more_time(?int $time=600) {
    if ($time !== null) set_time_limit($time);
  }
  static function more_memory(?string $memory="256M") {
    if ($memory !== null) ini_set("memory_limit", $memory);
  }
  static function more_resources(?int $time=600, ?string $memory="256M") {
    self::more_time($time);
    self::more_memory($memory);
  }
}