<?php

declare(strict_types=1);

namespace mirzaev\huesos\views;

// Files of the project
use mirzaev\huesos\models\session,
	mirzaev\huesos\models\account,
	mirzaev\huesos\models\settings,
	mirzaev\huesos\models\enumerations\language,
	mirzaev\huesos\models\enumerations\currency;

// Framework for PHP
use mirzaev\minimal\controller;

// Templater of views
use Twig\Loader\FilesystemLoader,
	Twig\Environment as twig,
	Twig\Extra\Intl\IntlExtension as intl,
	Twig\TwigFilter,
	Twig\TwigFunction;


// Built-in libraries
use ArrayAccess as array_access,
	Error as error;

/**
 * Templater
 *
 * @package mirzaev\huesos\views
 *
 * @param 
 *
 * @method
 *
 * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 */
final class templater extends controller implements array_access
{
	/**
	 * Registry of global variables of view
	 */
	public array $variables = [];

	/**
	 * Instance of twig templater 
	 */
	readonly public twig $twig;

	/**
	 * Constructor of an instance
	 *
	 * @param session|null $session The object implementing a session instance from ArangoDB
	 * @param account|null $account The object implementing an account instance from ArangoDB
	 * @param settings|null $settings The object implementing a settings instance from ArangoDB
	 *
	 * @return void
	 */
	public function __construct(
		?session $session = null,
		?account $account = null,
		?settings $settings = null,
	) {
		// Initializing an instance of twig
		$this->twig = new twig(new FilesystemLoader(VIEWS));

		// Declaring buffers for initializinf global variables
		$language = $currency = null;

		// Initializing global variables
		$this->twig->addGlobal('theme', 'default');
		$this->twig->addGlobal('server', $_SERVER);
		$this->twig->addGlobal('cookies', $_COOKIE);
		$this->twig->addGlobal('settings', $settings);
		if (!empty($session?->status())) $this->twig->addGlobal('session', $session);
		if (!empty($account?->status())) $this->twig->addGlobal('account', $account);
		$this->twig->addGlobal('language', $language = $account?->language ?? $session?->buffer['language'] ?? $settings?->language ?? language::en);
		$this->twig->addGlobal('currency', $currency = $account?->currency ?? $session?->buffer['currency'] ?? $settings?->currency ?? currency::usd);

		// FUCKING SAFARI
		/* $this->twig->addGlobal('safari', str_contains(strtolower($_SERVER['HTTP_USER_AGENT']), 'safari')); */

		// @todo move functions into controllers

		// Initializing function of dimensions formatting
		$this->twig->addFunction(
			new TwigFunction(
				'format_dimensions',
				function (
					string|int|float|null $x = null,
					string|int|float|null $y = null,
					string|int|float|null $z = null,
					string|null $before = null,
					string|null $after = null
				) {
					// Initialzing the buffer of result
					$result = '';

					// Generating
					if (!empty($x)) $result .= $x;
					if (!empty($y))
						if (empty($result)) $result = "$y";
						else $result .= "x$y";
					if (!empty($z))
						if (empty($result)) $result = "$z";
						else $result .= "x$z";
					if (!empty($result)) $result = "$before$result$after";

					// Exit (success)
					return $result;
				}
			)
		);

		// Initializing filter for formatting string to numbers only 
		$this->twig->addFilter(
			new TwigFilter(
				'numbers_only',
				function (string|null $string = null) {
					// Universalizing SIM-number
					preg_match_all('/\d/', $string, $numbers);
					$universalized = implode($numbers[0]);

					// Deinitializing unnecessary variables(
					unset($numbers);

					// Exit (success)
					return $universalized;
				}
			)
		);

		// Initializing filter for formatting SIM-numbers string to E.164
		$this->twig->addFilter(
			new TwigFilter(
				'sim_format',
				function (string|int|float|null $sim = null) {
					// Universalizing SIM-number
					preg_match_all('/\d/', $sim, $numbers);
					$universalized = implode($numbers[0]);

					// Deinitializing unnecessary variables(
					unset($numbers);

					// Parsing parts of SIM-number
					preg_match('/^(\d)(\d{3})(\d{1,11})$/', $universalized, $parsed);
					unset($parsed[0]);

					// Deinitializing unnecessary variables(
					unset($universalized);

					try {
						// Generating formatted SIM-number by E.164
						$formatted = sprintf('+%u %s %s', ...$parsed);

						// Exit (success)
						return $formatted;
					} catch (error $e) {
						// Exit (fail)
						return $sim;
					}
				}
			)
		);

		// Initializing of twig extensions
		$this->twig->addExtension(new intl());
	}

	/**
	 * Render a HTML-document
	 *
	 * @param string $file Related path to a HTML-document
	 * @param array $variables Registry of variables to push into registry of global variables
	 *
	 * @return ?string HTML-документ
	 */
	public function render(string $file, array $variables = []): ?string
	{
		// Generation and exit (success)
		return $this->twig->render('themes' . DIRECTORY_SEPARATOR . $this->twig->getGlobals()['theme'] . DIRECTORY_SEPARATOR . $file, $variables + $this->variables);
	}

	/**
	 * Write
	 *
	 * Write a variable into registry of global variables
	 *
	 * @param string $name Name of the variable
	 * @param mixed $value Value of the variable
	 *
	 * @return void
	 */
	public function __set(string $name, mixed $value = null): void
	{
		// Write the variable and exit (success)
		$this->variables[$name] = $value;
	}

	/**
	 * Read
	 *
	 * Read a variable from registry of global variables
	 *
	 * @param string $name Name of the variable
	 *
	 * @return mixed Content of the variable, if they are found
	 */
	public function __get(string $name): mixed
	{
		// Read the variable and exit (success)
		return $this->variables[$name];
	}

	/**
	 * Delete
	 *
	 * Delete a variable from the registry of global variables
	 *
	 * @param string $name Name of the variable 
	 *
	 * @return void
	 */
	public function __unset(string $name): void
	{
		// Delete the variable and exit (success)
		unset($this->variables[$name]);
	}

	/**
	 * Check of initialization
	 *
	 * Check of initialization in registry of global variables
	 *
	 * @param string $name Name of the variable
	 *
	 * @return bool The variable is initialized?
	 */
	public function __isset(string $name): bool
	{
		// Check of initialization of the variable and exit (success)
		return isset($this->variables[$name]);
	}

	/**
	 * Write
	 *
	 * Write a variable into registry of global variables
	 *
	 * @param mixed $name Name of an offset of the variable
	 * @param mixed $value Value of the variable
	 *
	 * @return void
	 */
	public function offsetSet(mixed $name, mixed $value): void
	{
		// Write the variable and exit (success)
		$this->variables[$name] = $value;
	}

	/**
	 * Read
	 *
	 * Read a variable from registry of global variables
	 *
	 * @param mixed $name Name of the variable
	 *
	 * @return mixed Content of the variable, if they are found
	 */
	public function offsetGet(mixed $name): mixed
	{
		// Read the variable and exit (success)
		return $this->variables[$name];
	}

	/**
	 * Delete
	 *
	 * Delete a variable from the registry of global variables
	 *
	 * @param mixed $name Name of the variable 
	 *
	 * @return void
	 */
	public function offsetUnset(mixed $name): void
	{
		// Delete the variable and exit (success)
		unset($this->variables[$name]);
	}

	/**
	 * Check of initialization
	 *
	 * Check of initialization in registry of global variables
	 *
	 * @param mixed $name Name of the variable
	 *
	 * @return bool The variable is initialized?
	 */
	public function offsetExists(mixed $name): bool
	{
		// Check of initialization of the variable and exit (success)
		return isset($this->variables[$name]);
	}
}
