<?php

namespace engine\skin;

use engine\lang\Strings;
use engine\skin\TwigAddons\SkinTwigExtension;
use Twig\Loader\LoaderInterface;

abstract class BaseSkin
{
    protected array $vars = [];
    protected array $globalVars = [];
    protected bool $globalsApplied = false;

    public readonly Strings $strings;

    protected \Twig\Environment $twig;

    public function __construct() {
        global $config;

        $cache_dir = $this->getCacheDir();
        if (!file_exists($cache_dir)) {
            if (mkdir($cache_dir, $config['dirs_mode'], true))
                setperm($cache_dir);
        }

        $this->twig = new \Twig\Environment($this->getTwigLoader(), [
            'cache' => $cache_dir,
            'auto_reload' => isDev()
        ]);
        $this->twig->addExtension(new SkinTwigExtension($this));

        $this->strings = Strings::getInstance(); // why singleton here?
        $this->strings->load('common');
    }

    abstract protected function getTwigLoader(): LoaderInterface;
    abstract protected function getCacheDir(): string;

    public function set($arg1, $arg2 = null) {
        if (is_array($arg1)) {
            foreach ($arg1 as $key => $value)
                $this->vars[$key] = $value;
        } elseif ($arg2 !== null) {
            $this->vars[$arg1] = $arg2;
        }
    }

    public function setGlobal($arg1, $arg2 = null): void {
        if ($this->globalsApplied)
            logError(__METHOD__.': WARNING: globals were already applied, your change will not be visible');

        if (is_array($arg1)) {
            foreach ($arg1 as $key => $value)
                $this->globalVars[$key] = $value;
        } elseif ($arg2 !== null) {
            $this->globalVars[$arg1] = $arg2;
        }
    }

    public function applyGlobals(): void {
        if (!empty($this->globalVars) && !$this->globalsApplied) {
            foreach ($this->globalVars as $key => $value)
                $this->twig->addGlobal($key, $value);
            $this->globalsApplied = true;
        }
    }

    public function render($template, array $vars = []): string {
        $this->applyGlobals();
        return $this->doRender($template, $this->vars + $vars);
    }

    protected function doRender(string $template, array $vars = []): string {
        $s = '';
        try {
            $s = $this->twig->render($template, $vars);
        } catch (\Twig\Error\Error $e) {
            $error = get_class($e).": failed to render";
            $source_ctx = $e->getSourceContext();
            if ($source_ctx) {
                $path = $source_ctx->getPath();
                if (str_starts_with($path, APP_ROOT))
                    $path = substr($path, strlen(APP_ROOT) + 1);
                $error .= " ".$source_ctx->getName()." (".$path.") at line ".$e->getTemplateLine();
            }
            $error .= ": ";
            $error .= $e->getMessage();
            logError($error);
            if (isDev()) {
                $s = $error."\n<br><br>\n";
                $s .= nl2br(htmlescape($e->getTraceAsString()));
            }
        }
        return $s;
    }
}
