<?php

use Twig\Environment;
use Twig\Loader\FilesystemLoader;

/**
 * This file is part of an ADDON for use with LEPTON Core.
 * This ADDON is released under the GNU GPL.
 * Additional license terms can be seen in the info.php of this module.
 *
 * @module          Twig Template Engine
 * @author          LEPTON Project
 * @copyright       2010-2026 LEPTON  
 * @link            https://lepton-cms.org
 * @license         https://gnu.org/licenses/gpl.html
 * @license_terms   please see info.php of this module
 *
 */

class lib_twig_box extends lib_twig
{
    /**
     *  Public var that holds the instance of the TWIG-loader.
     *
     */
    public ?object $loader = null;
    
    /**
     *  Public var that holds the instance of the TWIG-parser.
     *
     */
    public ?object $parser = null;
    
    /**
     *  @var object The reference to the "singleton" instance of this class.
     *
     */
    public static $instance;

    /**
     * Initialize some basic (LEPTON-CMS specific) values.
     *
     */
    public function initialize(): void
    {
        global $TEXT;

        self::register();
        static::$instance->loader = new FilesystemLoader( LEPTON_PATH.'/' );

        static::$instance->registerPath(LEPTON_PATH . "/templates/" . DEFAULT_THEME . "/templates/", "theme");
        static::$instance->registerPath(LEPTON_PATH . "/templates/" . DEFAULT_TEMPLATE . "/templates/", "frontend");
        
        static::$instance->parser = new Environment(
            static::$instance->loader,
            [
                'cache' => false,
                'debug' => true
            ]
        );
        
        static::$instance->parser->addExtension(new Twig\Extension\DebugExtension());
        static::$instance->parser->addGlobal("LEPTON_PATH", LEPTON_PATH);
        static::$instance->parser->addGlobal("LEPTON_URL", LEPTON_URL);
        static::$instance->parser->addGlobal("ADMIN_URL", ADMIN_URL);
        static::$instance->parser->addGlobal("THEME_PATH", THEME_PATH);
        static::$instance->parser->addGlobal("THEME_URL", THEME_URL);
        static::$instance->parser->addGlobal("MEDIA_DIRECTORY", MEDIA_DIRECTORY);
        static::$instance->parser->addGlobal("PAGES_DIRECTORY", PAGES_DIRECTORY);   // L* 7.3.0
        static::$instance->parser->addGlobal("PAGE_EXTENSION", PAGE_EXTENSION);     // L* 7.3.0
        
        static::$instance->parser->addGlobal("AUTH_MIN_LOGIN_LENGTH", AUTH_MIN_LOGIN_LENGTH); // L* 7.1.0
        static::$instance->parser->addGlobal("AUTH_MAX_LOGIN_LENGTH", AUTH_MAX_LOGIN_LENGTH); // L* 7.1.0
        
        static::$instance->parser->addGlobal("AUTH_MIN_PASS_LENGTH", AUTH_MIN_PASS_LENGTH); // L* 7.1.0
        static::$instance->parser->addGlobal("AUTH_MAX_PASS_LENGTH", AUTH_MAX_PASS_LENGTH); // L* 7.1.0

        if (defined("TEMPLATE_DIR"))
        {
           static::$instance->parser->addGlobal( "TEMPLATE_DIR", TEMPLATE_DIR );
        } 
        else 
        {
        
            $oLEPTON = LEPTON_core::getGlobal("oLEPTON");
            
            if ( (isset($oLEPTON)) && (isset($oLEPTON->page['template'])) && ($oLEPTON->page['template'] != ""))
            { 
                static::$instance->parser->addGlobal("TEMPLATE_DIR", LEPTON_URL."/templates/".$oLEPTON->page['template']);
            
            } else {

                static::$instance->parser->addGlobal("TEMPLATE_DIR", LEPTON_URL . "/templates/" . DEFAULT_TEMPLATE);
            }
        }

        static::$instance->parser->addGlobal("DEFAULT_TEMPLATE_PATH", LEPTON_PATH."/templates/".DEFAULT_TEMPLATE); // L* 7.4.0
        static::$instance->parser->addGlobal("DEFAULT_TEMPLATE_URL", LEPTON_URL."/templates/".DEFAULT_TEMPLATE);   // L* 7.4.0

        // [3] Get global language arrays
        // read code expires from ini_file
        $ini_file_name = LEPTON_PATH."/config/lepton.ini.php";
        $config = parse_ini_string(";" . file_get_contents($ini_file_name), true);
        if (($config['custom_vars']['tfa_code_expires'] ?? '') != '')
        {
            // override default text with value from ini file
            $iCodeExpires = number_format(intval($config['custom_vars']['tfa_code_expires']) / 60, 0);
            $TEXT['TFA_NOTICE_VII'] = sprintf($TEXT['TFA_NOTICE_VII'], $iCodeExpires);
        }
        
        $languageKeyNames = ["MENU", "TEXT", "HEADING", "MESSAGE", "OVERVIEW", "THEME"];
        foreach ($languageKeyNames as $tempKey)
        {
            static::$instance->parser->addGlobal($tempKey, LEPTON_core::getGlobal($tempKey) ?? []);
        }

        // [3.1] Add existing $THEME from current theme template
        $themeLanguageFile = LEPTON_PATH . '/templates/' . DEFAULT_THEME . '/languages/' . DEFAULT_LANGUAGE . '.php';
        if (file_exists($themeLanguageFile))
        {
            global $THEME;
            require $themeLanguageFile;
            static::$instance->parser->addGlobal( "THEME", $THEME ?? []);
        }

        // [4] Last edit section in the backend.
        if (isset($_SESSION['last_edit_section']))
        {
            static::$instance->parser->addGlobal("last_edit_section", $_SESSION['last_edit_section']);
        }

        // [5] Extensions
        static::$instance->parser->addExtension( new lib_twig_operators() );
        // [5.1] The holy grale! L*7.4.0
        static::$instance->parser->addExtension( new lib_twig_extensions() );
        
        // [6] Functions
        static::$instance->parser->addFunction(lib_twig_functions::getPasswordPattern());
        static::$instance->parser->addFunction(lib_twig_functions::getEmailPattern());
        static::$instance->parser->addFunction(lib_twig_functions::getDisplayPattern());
        static::$instance->parser->addFunction(lib_twig_functions::getPattern());       // L*7.2.0
        static::$instance->parser->addFunction(lib_twig_functions::fileExists());

        // [7] Filters
        static::$instance->parser->addFilter(lib_twig_filters::getFilterDisplay());
        static::$instance->parser->addFilter(lib_twig_filters::CharsDecodeOutput());
        static::$instance->parser->addFilter(lib_twig_filters::getFilterIntersects());
        static::$instance->parser->addFilter(lib_twig_filters::getFilterTimeF());
        
    }

    /**
     * Public function to register a path to the current instance.
     * If the path doesn't exist he will not be added to avoid Twig-internal warnings.
     *
     * @param  string  $sPath A path to any local template directory.
     * @param  string  $sNamespace An optional namespace (-identifier),
     *                  by default "__main__", normal e.g. the namespace of a module.
     *                  See the Twig documentation for details about using "template" namespace.
     * @return bool    True if success, false if file doesn't exist or the first param is empty.
     *
     */    
    public function registerPath(string $sPath = "", string $sNamespace="__main__"): bool
    {
        if ($sPath === "")
        {
            return false;
        }

        if (true === file_exists($sPath))
        {
            $current_paths = static::$instance->loader->getPaths($sNamespace);
            if (!in_array($sPath, $current_paths))
            {
                static::$instance->loader->prependPath($sPath, $sNamespace);
                return true;
                
            } 
            else 
            {
                return false;
            }
        }

        return false;
    }

    /**
     *  Register one or more values global to the instance via an assoc. array.
     *
     *  @param  array   $aArray An associative array with the values to be registered as globals.
     *
     */
    public function registerGlobals(array $aArray ): void
    {
        foreach ($aArray as $key => $value)
        {
            static::$instance->parser->AddGlobal($key, $value);
        }
    }
    
    /**
     *  Public shortcut to the internal loader->render method.
     *
     *  @param  string  $sTemplateName  A valid template-name (to use) incl. the namespace.
     *  @param  array   $aMixed         The values to parse.
     *  @return string  The parsed template string.
     *
     */
    public function render(string $sTemplateName, array $aMixed): string
    {
        $sTemplateName = str_replace("/frontend/", "/", $sTemplateName);
        return static::$instance->parser->render($sTemplateName, $aMixed);
    }
    
    /**
     *  Public function to "register" all module specific paths at once
     *    
     *  @param  string  $sModuleDir     A valid module-directory (also used as namespace).
     *
     *  @IMPORTANT @namespace means in this case that twig is always searching in : "LEPTON_PATH/modules/$module_directory/templates/"
     */
    public function registerModule(string $sModuleDir): void
    {
        
        $basePath = LEPTON_PATH."/modules/".$sModuleDir;
        static::$instance->registerPath( $basePath."/templates/", $sModuleDir);
        static::$instance->registerPath( $basePath."/templates/backend", $sModuleDir);
        static::$instance->registerPath( $basePath."/templates/frontend", $sModuleDir);
        
        static::$instance->registerPath( LEPTON_PATH."/templates/".DEFAULT_THEME."/backend/".$sModuleDir."/", $sModuleDir);	

        // for the frontend
        if (defined("PAGE_ID"))
        {
            $page_template = LEPTON_database::getInstance()->get_one("SELECT `template` FROM `".TABLE_PREFIX."pages` WHERE `page_id`=".PAGE_ID);
            static::$instance->registerPath( LEPTON_PATH."/templates/".( $page_template == "" ? DEFAULT_TEMPLATE : $page_template)."/frontend/".$sModuleDir."/", $sModuleDir);
        }
    }

    /**
     *  Public function to "register" all (frontend-)templates specific paths at once
     *    
     *  @param  string $sTemplateDir A valid template-directory (namespace = "frontend").
     *
     */
    public function registerFETemplate(string $sTemplateDir = DEFAULT_TEMPLATE, string $namespace = "frontend" )
    {
        if (defined("PAGE_ID"))
        {
            $page_template = LEPTON_database::getInstance()->get_one("SELECT `template` FROM `".TABLE_PREFIX."pages` WHERE `page_id`=".PAGE_ID);
            static::$instance->registerPath( LEPTON_PATH."/templates/".( $page_template == "" ? $sTemplateDir : $page_template)."/templates/", $namespace);
        } 
        else
        {
            echo(LEPTON_tools::display('This method is only for use with frontend templates','pre','ui red message'));	
        }
    }

    /**
     *  Public function for buffering function-calls within "wild" echo/print.
     *
     *  @param  string  $aJobStr    Any valid function within params.
     *  @return string              The captured result.
     */
    public function capture_echo(string $aJobStr = "")
    {
        ob_start();
            global $oLEPTON;  // for required calls
            global $database; // for required calls
            global $TEXT;     // for required calls

            eval ($aJobStr);
        return ob_get_clean();
    }

    /**
     *  Public function for page_content with buffering to avoid double echo/print
     *
     *  @param  string  $page_content Any valid function within params.
     *  @return string  The result.
     */
    public function simple_echo(int $page_content = 1)
    {
        ob_start();
            global $oLEPTON;  // for required calls
            global $database; // for required calls
            global $TEXT;     // for required calls
            page_content($page_content);
        return ob_get_clean();
    }
}
