<?php
/***************************************************************
 *  Copyright notice
 *
 *  (c) 2010 Glowbase GmbH
 *
 *  This script is part of the TYPO3 project. The TYPO3 project is
 *  free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  The GNU General Public License can be found at
 *  http://www.gnu.org/copyleft/gpl.html.
 *
 *  This script is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  This copyright notice MUST APPEAR in all copies of the script!
 ***************************************************************/

namespace IMATHUZH\Qfq\Core\Report;

use IMATHUZH\Qfq\Core\Evaluate;
use IMATHUZH\Qfq\Core\Helper\OnString;
use IMATHUZH\Qfq\Core\Store\Store;
use IMATHUZH\Qfq\Core\Store\T3Info;
use IMATHUZH\Qfq\Core\Typo3\T3Handler;


/**
 * Class Variables
 * @package qfq
 */
class Variables {

    public $resultArray = array();

    // TODO to explain
    private $tt_content_uid = 0;

    /**
     * @var Evaluate
     */
    private $eval;

    /**
     * @param Evaluate $eval
     * @param int $tt_content_uid
     */
    public function __construct(Evaluate $eval, $tt_content_uid = 0) {

        $this->eval = $eval;

        // specified once during initialisation.
        $this->tt_content_uid = $tt_content_uid;

    }

    /**
     * Matches on the variables {{10.20.name}} and substitutes them with the values
     *
     * @param string $text
     *
     * @return mixed
     * @throws \CodeException
     * @throws \DbException
     * @throws \UserFormException
     * @throws \UserReportException
     */
    public function doVariables($text, $frCmd = '') {

        if ($text == '') {
            return '';
        }

//        $str = preg_replace_callback("/{{(([a-zA-Z0-9.:_])*)}}/", array($this, 'replaceVariables'), $text);
        // Process all {{x[.x].name}}
//        $str = preg_replace_callback('/{{\s*(([0-9]+.)+[a-zA-Z0-9_.]+)\s*}}/', 'self::replaceVariables', $text);
//        $str = preg_replace_callback('/{{\s*(([0-9]+.)+[a-zA-Z0-9_.]+)(:.*)*\s*}}/', 'self::replaceVariables', $text);

        // Report notation 'numeric'
        // E.g. {{10.line.count}}, {{10.20.line.count}}
        // $str = preg_replace_callback('/{{\s*(([0-9]+.)+[a-zA-Z0-9_.]+)(:[a-zA-Z-]*)*\s*}}/', 'self::replaceVariables', $text);

        // Report notation 'alias'
        // E.g. {{myAlias.line.count}}, {{myAlias10.line.count}}, {{10myAlias.line.count}}
        $str = preg_replace_callback('/{{\s*(([a-zA-Z0-9_-]*[0-9.]*.)[.][a-zA-Z0-9_.]+)+(:[a-zA-Z-]*)*\s*}}/', [self::class, 'replaceVariables'], $text);

        // Try the Stores
        return $this->eval->parse($str, null, null, $dummyStack, $dummyStore, $frCmd);
    }

    /**
     * Callbackfunction called by variableSQL()
     * Replaces the variablenames with the value from the resultArray.
     * a) 10.<colname>
     * b) 10.line.total|count|insertId|content
     *
     * @param $matches
     *
     * @return string The content that is displayed on the website
     */
    public function replaceVariables($matches): string {

        // Report notation 'numeric
        //  $matches[0]: {{10.20.<columnname>::u}}
        //  $matches[1]: 10.20.<columnname>
        //  $matches[2]: 10.20
        //  $matches[3]: :u

        // Report notation 'alias'
        //  $matches[0]: {{myAlias.<columnname>::u}}
        //  $matches[1]: myAlias.<columnname>
        //  $matches[2]: myAlias
        //  $matches[3]: :u
        $data = $matches[0];

        // Isolate first token as possible alias
        $alias = strtok($matches[2], ".");

        // No numeric value implies that an alias was used
        if (!is_numeric($alias)) {
            // Get typo3 store
            // Aliases are saved like [alias.1 => "myAlias", alias.1.2 => "mySecondAlias", ...]
            $storeT3 = Store::getStore(STORE_TYPO3);
            // Check for matching value of $alias
            $match = array_search($alias, $storeT3, true);

            // Replacement only if matching alias was found
            if (!empty($match)) {
                // Extract level from key
                $level = substr($match, strpos($match, '.') + 1);

                $matches[0] = str_replace($alias, $level, $matches[0]);
                $matches[1] = str_replace($alias, $level, $matches[1]);
                $matches[2] = $level . '.';
            }
        }

        // index of last '.'
        $pos = strrpos($matches[1], ".");
        if ($pos !== false) {

            $fullLevel = substr($matches[1], 0, $pos + 1);
            $varName = substr($matches[1], $pos + 1, strlen($matches[1]));

            if (isset($this->resultArray[$fullLevel][$varName])) {
                //
                $data = $this->resultArray[$fullLevel][$varName];
                if (isset($matches[3])) {
                    $arr = explode(':', $matches[3]);
                    $data = OnString::escape($arr[1] ?? '', $this->resultArray[$fullLevel][$varName], $rcFlagWipe);
                }
                // This is for the specific case, that the variable references its own level
                // E.g. myAlias { \n sql = SELECT '{{myAlias.line.count}}' \n }
                // Note: This is only used for line.count and line.total, because non-existing variables must stay unchanged
                // E.g. myAlias { \n sql = SELECT 1 \n } \n { \n sql = SELECT '{{myAlias.varName}}' \n }
                // '{{myAlias.varName}}' will not be changed to '{{1.varName}}'
            } elseif ($varName === LINE_COUNT || $varName === LINE_TOTAL) {
                // myAlias needs to be replaced by the level
                // E.g. {{1.2.line.count}}
                $data = $matches[0];
            }
        }

        return $data;
    }

    /**
     * Collect Global Variables
     *
     * @param void
     *
     * @return    array with global variables which might be replaced
     */
    public function collectGlobalVariables(): array {
        $arr = array();

        if (isset($_SERVER["REMOTE_ADDR"])) {
            //TODO: Variablen sollten vom STORE_TYPO3 genommen werden
            $arr["REMOTE_ADDR"] = $_SERVER["REMOTE_ADDR"] ?? '';
            $arr["HTTP_HOST"] = $_SERVER["HTTP_HOST"] ?? '';
            $arr["REQUEST_URI"] = $_SERVER["REQUEST_URI"] ?? '';

            $protocol = 'http';
            if (isset($_SERVER['HTTPS'])) {
                if ($_SERVER["HTTPS"] != "off")
                    $protocol = 'https';
            }
            $arr["url"] = $protocol . "://" . $_SERVER["HTTP_HOST"];
            if ($_SERVER["SERVER_PORT"] != 80)
                $arr["url"] .= ":" . $_SERVER["SERVER_PORT"];
            $arr["url"] .= $arr["REQUEST_URI"];
        }

        $arr["fe_user_uid"] = T3Handler::getFeUserProperty('uid', '-');
        $arr["fe_user"] = T3Handler::getFeUserProperty('username', '-');

        $arr["be_user_uid"] = T3Handler::getBeUserProperty('uid', '-');
        $arr["ttcontent_uid"] = $this->tt_content_uid;

        if (isset($GLOBALS['TYPO3_REQUEST'])) {
            $arr["page_id"] = T3Handler::getPageId();
            $arr["page_type"] = T3Handler::getPageType();
            $arr["page_language_uid"] = T3Info::getLanguageId();
        }

        return ($arr);
    }

    /**
     * @param        $arr
     * @param        $return
     * @param string $keyPath
     */
    public function linearizeArray($arr, &$return, $keyPath = "") {
        if (is_array($arr)) {
            foreach ($arr as $key => $value) {
                $this->linearizeArray($value, $return, $keyPath . "_" . $key);
            }
        } else {
            $return[ltrim($keyPath, "_")] = $arr;
        }
    }
}
