<?php

/**
 * abstract Class QuestionTypeRoot
 * The aFieldArray Array contains the following
 *  0 => string qid
 *  1 => string sgqa
 *  2 => string questioncode
 *  3 => string question
 *  4 => string type
 *  5 => string gid
 *  6 => string mandatory,
 *  7 => string conditionsexist,
 *  8 => string usedinconditions
 *  0 => string used in group.php for question count
 * 10 => string new group id for question in randomization group (GroupbyGroup Mode)
 *
 * {@inheritdoc}
 */
abstract class QuestionBaseRenderer extends StaticModel
{
    public $oQuestion;
    public $sSGQA;
    public $sHtml;
    public $bRenderDirect;
    public $bPreview;
    public $sCoreClass;
    public $checkconditionFunction = "checkconditions";

    protected $aFieldArray;
    protected $aQuestionAttributes;
    protected $aSurveySessionArray;
    protected $mSessionValue;
    protected $sLanguage;

    protected $aSubQuestions = [];
    protected $aAnswerOptions = [];

    protected $aPackages = [];
    protected $aScripts = [];
    protected $aScriptFiles = [];
    protected $aStyles = [];
    protected $questionOrderingService;

    public function __construct($aFieldArray, $bRenderDirect = false)
    {
        $this->aFieldArray = $aFieldArray;
        $this->sSGQA = $this->aFieldArray[1];
        $this->oQuestion = Question::model()->findByPk($aFieldArray[0]);
        $this->bRenderDirect = $bRenderDirect;
        $this->sLanguage = $this->setDefaultIfEmpty(@$aFieldArray['language'], @$_SESSION['survey_' . $this->oQuestion->sid]['s_lang']);
        if (!$this->sLanguage) {
                $this->sLanguage = $this->oQuestion->survey->language;
        }

        $this->aQuestionAttributes = QuestionAttribute::model()->getQuestionAttributes($this->oQuestion->qid);
        $this->aSurveySessionArray = @$_SESSION['survey_' . $this->oQuestion->sid];
        $this->mSessionValue = @$this->setDefaultIfEmpty($this->aSurveySessionArray[$this->sSGQA], '');

        $oQuestionTemplate = QuestionTemplate::getNewInstance($this->oQuestion);
        $oQuestionTemplate->registerAssets(); // Register the custom assets of the question template, if needed

        $questionScript = $this->getQuestionScript();
        if (!empty($questionScript)) {
            /* Replace Expression Manager */
            $sScriptRendered = LimeExpressionManager::ProcessString(
                $questionScript,
                $this->oQuestion->qid,
                [
                    'QID' => $this->oQuestion->qid,
                    'GID' => $this->oQuestion->gid,
                    'SGQ' => $this->oQuestion->sid . "X" . $this->oQuestion->gid . "X" . $this->oQuestion->qid,
                ]
            );
            $this->addScript('QuestionStoredScript-' . $this->oQuestion->qid, $sScriptRendered, LSYii_ClientScript::POS_POSTSCRIPT);
            /* Reset needed replacement */
            LimeExpressionManager::updateReplacementFields(array(
                'QID' => null,
                'GID' => null,
                'SGQ' => null,
            ));
        }
        $diContainer = \LimeSurvey\DI::getContainer();
        $this->questionOrderingService = $diContainer->get(
            \LimeSurvey\Models\Services\QuestionOrderingService\QuestionOrderingService::class
        );
    }

    protected function getTimeSettingRender()
    {
        $oQuestion = $this->oQuestion;
        $oSurvey = $this->oQuestion->survey;
        $time_limit = intval($oQuestion->questionattributes['time_limit']['value']);
        if ($time_limit <= 0) {
            return;
        }
        Yii::app()->getClientScript()->registerScriptFile(Yii::app()->getConfig("generalscripts") . 'coookies.js', CClientScript::POS_BEGIN);
        Yii::app()->getClientScript()->registerPackage('timer-addition');

        $surveyId = App()->getConfig('surveyID');
        $langTimer = array(
            'hours' => gT("hours"),
            'mins' => gT("mins"),
            'seconds' => gT("seconds"),
        );
        /* Registering script : don't go to EM : no need usage of ls_json_encode */
        App()->getClientScript()->registerScript("LSVarLangTimer", "LSvar.lang.timer=" . json_encode($langTimer) . ";", CClientScript::POS_BEGIN);
        /**
         * The following lines cover for previewing questions, because no $_SESSION['survey_'.$surveyId]['fieldarray'] exists.
         * This just stops error messages occuring
         */
        if (!isset($_SESSION['survey_' . $surveyId]['fieldarray'])) {
            $_SESSION['survey_' . $surveyId]['fieldarray'] = [];
        }
        /* End */

        //Used to count how many timer questions in a page, and ensure scripts only load once
        $_SESSION['survey_' . $oSurvey->sid]['timercount'] = (isset($_SESSION['survey_' . $oSurvey->sid]['timercount'])) ? $_SESSION['survey_' . $oSurvey->sid]['timercount']++ : 1;

        /* Work in all mode system : why disable it ? */
        //~ if ($thissurvey['format'] != "S")
        //~ {
        //~ if ($thissurvey['format'] != "G")
        //~ {
        //~ return "\n\n<!-- TIMER MODE DISABLED DUE TO INCORRECT SURVEY FORMAT -->\n\n";
        //~ //We don't do the timer in any format other than question-by-question
        //~ }
        //~ }

        //Render timer
        $disable_next = $this->setDefaultIfEmpty($oQuestion->questionattributes['time_limit_disable_next']['value'], 0);
        $disable_prev = $this->setDefaultIfEmpty($oQuestion->questionattributes['time_limit_disable_prev']['value'], 0);
        $time_limit_action = $this->setDefaultIfEmpty($oQuestion->questionattributes['time_limit_action']['value'], 1);
        $time_limit_message = $this->setDefaultIfEmpty($oQuestion->questionattributes['time_limit_message']['value'], gT("Your time to answer this question has expired"));
        $time_limit_warning = intval($this->setDefaultIfEmpty($oQuestion->questionattributes['time_limit_warning']['value'], 0));
        $time_limit_warning_2 = intval($this->setDefaultIfEmpty($oQuestion->questionattributes['time_limit_warning_2']['value'], 0));
        $time_limit_countdown_message = $this->setDefaultIfEmpty($oQuestion->questionattributes['time_limit_countdown_message']['value'], gT("Time remaining"));
        $time_limit_warning_message = $this->setDefaultIfEmpty($oQuestion->questionattributes['time_limit_warning_message']['value'], gT("Your time to answer this question has nearly expired. You have {TIME} remaining."));
        $time_limit_warning_display_time = intval($this->setDefaultIfEmpty($oQuestion->questionattributes['time_limit_warning_display_time']['value'], 0));
        $time_limit_warning_2_message = $this->setDefaultIfEmpty($oQuestion->questionattributes['time_limit_warning_2_message']['value'], gT("Your time to answer this question has nearly expired. You have {TIME} remaining."));

        $time_limit_message_delay = intval($this->setDefaultIfEmpty($oQuestion->questionattributes['time_limit_message_delay']['value'], 1)) * 1000;
        $time_limit_warning_2_display_time = intval($this->setDefaultIfEmpty($oQuestion->questionattributes['time_limit_warning_2_display_time']['value'], 0));
        $time_limit_message_style = $this->setDefaultIfEmpty($oQuestion->questionattributes['time_limit_message_style']['value'], '');
        $time_limit_message_class = "d-none ls-timer-content ls-timer-message ls-no-js-hidden";
        $time_limit_warning_style = $this->setDefaultIfEmpty($oQuestion->questionattributes['time_limit_warning_style']['value'], '');
        $time_limit_warning_class = "d-none ls-timer-content ls-timer-warning ls-no-js-hidden";
        $time_limit_warning_2_style = $this->setDefaultIfEmpty($oQuestion->questionattributes['time_limit_warning_2_style']['value'], '');
        $time_limit_warning_2_class = "d-none ls-timer-content ls-timer-warning2 ls-no-js-hidden";
        $time_limit_timer_style = $this->setDefaultIfEmpty($oQuestion->questionattributes['time_limit_timer_style']['value'], '');
        $time_limit_timer_class = "ls-timer-content ls-timer-countdown ls-no-js-hidden";

        // Escape messages to avoid XSS attacks, based on how LS3 does this (see return_timer_script() on qanda_helper)
        $time_limit_message = htmlspecialchars((string) $time_limit_message, ENT_QUOTES);
        $time_limit_countdown_message = htmlspecialchars((string) $time_limit_countdown_message, ENT_QUOTES);
        $time_limit_warning_message = htmlspecialchars((string) $time_limit_warning_message, ENT_QUOTES);
        $time_limit_warning_2_message = htmlspecialchars((string) $time_limit_warning_2_message, ENT_QUOTES);

        // The {TIME} placeholder is replaced by the HTML that will contain the actual time.
        // This is done after applying 'htmlspecialchars' to avoid encoding the HTML part.
        $timer_html = Yii::app()->twigRenderer->renderQuestion(
            '/survey/questions/question_timer/timer',
            ['iQid' => $oQuestion->qid, 'sWarnId' => ''],
            true
        );
        $time_limit_warning_message = str_replace("{TIME}", $timer_html, $time_limit_warning_message);
        $timer_html = Yii::app()->twigRenderer->renderQuestion(
            '/survey/questions/question_timer/timer',
            ['iQid' => $oQuestion->qid, 'sWarnId' => '_2'],
            true
        );
        $time_limit_warning_2_message = str_replace("{TIME}", $timer_html, $time_limit_warning_2_message);

        $timersessionname = "timer_question_" . $oQuestion->qid;
        if (isset($_SESSION['survey_' . $surveyId][$timersessionname])) {
            $time_limit = $_SESSION['survey_' . $surveyId][$timersessionname];
        }

        $disable = null;
        App()->getClientScript()->registerScript(
            "TimerQuestion" . $oQuestion->qid,
            "countdown($oQuestion->qid, $surveyId, $time_limit, $time_limit_action, $time_limit_warning, $time_limit_warning_2, $time_limit_warning_display_time, $time_limit_warning_2_display_time, '$disable');",
            LSYii_ClientScript::POS_POSTSCRIPT
        );

        $output = Yii::app()->twigRenderer->renderQuestion(
            '/survey/questions/question_timer/timer_header',
            array('timersessionname' => $timersessionname, 'time_limit' => $time_limit),
            true
        );

        if ($_SESSION['survey_' . $oSurvey->sid]['timercount'] < 2) {
            $iAction = '';
            if ($oSurvey->format == "G") {
                $qcount = 0;
                foreach ($_SESSION['survey_' . $oSurvey->sid]['fieldarray'] as $ib) {
                    if ($ib[5] == $oQuestion->gid) {
                        $qcount++;
                    }
                }
                // Override all other options and just allow freezing, survey is presented in group by group mode
                // Why don't allow submit in Group by group mode, this surely broke 'mandatory' question, but this remove a great system for user (Denis 140224)
                if ($qcount > 1) {
                    $iAction = '3';
                }
            }

            /* If this is a preview, don't allow the page to submit/reload */
            $thisaction = returnglobal('action');
            if ($thisaction == "previewquestion" || $thisaction == "previewgroup" || $this->bPreview == true) {
                $iAction = '3';
            }

            $output .= Yii::app()->twigRenderer->renderQuestion('/survey/questions/question_timer/timer_javascript', array(
                'timersessionname' => $timersessionname,
                'time_limit' => $time_limit,
                'iAction' => $iAction,
                'disable_next' => $disable_next,
                'disable_prev' => $disable_prev,
                'time_limit_countdown_message' => $time_limit_countdown_message,
                'time_limit_message_delay' => $time_limit_message_delay
                ), true);
        }

        $output .= Yii::app()->twigRenderer->renderQuestion(
            '/survey/questions/question_timer/timer_content',
            array(
                'iQid' => $oQuestion->qid,
                'time_limit_message_style' => $time_limit_message_style,
                'time_limit_message_class' => $time_limit_message_class,
                'time_limit_message' => $time_limit_message,
                'time_limit_warning_style' => $time_limit_warning_style,
                'time_limit_warning_class' => $time_limit_warning_class,
                'time_limit_warning_message' => $time_limit_warning_message,
                'time_limit_warning_2_style' => $time_limit_warning_2_style,
                'time_limit_warning_2_class' => $time_limit_warning_2_class,
                'time_limit_warning_2_message' => $time_limit_warning_2_message,
                'time_limit_timer_style' => $time_limit_timer_style,
                'time_limit_timer_class' => $time_limit_timer_class,
            ),
            true
        );

        $output .= "</div>";
        return $output;
    }

    protected function getQuestionAttribute($key1, $key2 = null)
    {
        $result =  $this->aQuestionAttributes[$key1] ?? null;
        if ($key2 !== null && $result !== null) {
            $result =  $result[$key2] ?? null;
        }
        return $result;
    }

    protected function setSubquestions($scaleId = null)
    {
        $this->aSubQuestions = $this->questionOrderingService->getOrderedSubQuestions(
            $this->oQuestion,
            $scaleId,
            $this->sLanguage
        );
    }

    protected function setAnsweroptions($scaleId = null)
    {
        $this->aAnswerOptions = $this->questionOrderingService->getOrderedAnswers(
            $this->oQuestion,
            $scaleId,
            $this->sLanguage
        );
    }

    protected function getAnswerCount($iScaleId = 0)
    {
        return count($this->aAnswerOptions[$iScaleId]);
    }

    protected function getQuestionCount($iScaleId = 0)
    {
        return count($this->aSubQuestions[$iScaleId]);
    }

    protected function getFromSurveySession($sIndex, $default = "")
    {
        return $_SESSION['survey_' . $this->oQuestion->sid][$sIndex] ?? $default;
    }

    protected function applyPackages()
    {
        foreach ($this->aPackages as $sPackage) {
            Yii::app()->getClientScript()->registerPackage($sPackage);
        }
    }

    protected function addScript($name, $content, $position = LSYii_ClientScript::POS_BEGIN, $appendId = false)
    {
        $this->aScripts[] = [
            'name' => $name . ($appendId ? '_' . $this->oQuestion->qid : ''),
            'content' => $content,
            'position' => $position
        ];
    }

    protected function applyScripts()
    {
        foreach ($this->aScripts as $aScript) {
            Yii::app()->getClientScript()->registerScript($aScript['name'], $aScript['content'], $aScript['position']);
        }
    }
    protected function applyScriptfiles()
    {
        foreach ($this->aScriptFiles as $aScriptFile) {
            Yii::app()->getClientScript()->registerScriptFile($aScriptFile['path'], $aScriptFile['position']);
        }
    }

    protected function applyStyles()
    {
        foreach ($this->aStyles as $aStyle) {
            Yii::app()->getClientScript()->registerCss($aStyle['name'], $aStyle['content']);
        }
    }

    protected function setDefaultIfEmpty($value, $default)
    {
        if (is_null($value)) {
            return $default;
        }
        return trim((string) $value) == '' ? $default : $value;
    }

    protected function registerAssets()
    {
        $this->applyPackages();
        $this->applyScripts();
        $this->applyScriptfiles();
        $this->applyStyles();
    }

    /**
    * Return class of a specific row (hidden by relevance)
    * @param string $myfname The name of the question/row to test
    * @return string
    */
    public function getCurrentRelevecanceClass($myfname)
    {
        $aSurveySessionArray = $_SESSION["survey_{$this->oQuestion->sid}"];
        $relevanceStatus = !isset($aSurveySessionArray['relevanceStatus'][$myfname]) || $aSurveySessionArray['relevanceStatus'][$myfname];
        if ($relevanceStatus) {
            return "";
        }

        $sExcludeAllOther = $this->setDefaultIfEmpty($this->getQuestionAttribute('exclude_all_others'), false);
        /* EM don't set difference between relevance in session, if exclude_all_others is set , just ls-disabled */
        if ($sExcludeAllOther !== false) {
            foreach (explode(';', (string) $sExcludeAllOther) as $sExclude) {
                $sExclude = $this->sSGQA . $sExclude;
                if (
                    (!isset($aSurveySessionArray['relevanceStatus'][$sExclude]) || $aSurveySessionArray['relevanceStatus'][$sExclude])
                    && (isset($aSurveySessionArray[$sExclude]) && $aSurveySessionArray[$sExclude] == "Y")
                ) {
                    return "ls-irrelevant ls-disabled";
                }
            }
        }

        // Currently null/0/false=> hidden , 1 : disabled
        $filterStyle = !empty($this->aQuestionAttributes['array_filter_style']);
        return ($filterStyle) ?  "ls-irrelevant ls-disabled" : "ls-irrelevant ls-hidden";
    }
    /**
    * Find the label / input width
    * @param string|int $labelAttributeWidth label width from attribute
    * @param string|int $inputAttributeWidth input width from attribute
    * @return array labelWidth as integer,inputWidth as integer,defaultWidth as boolean
    */
    public function getLabelInputWidth()
    {
        $labelAttributeWidth = trim((string) $this->getQuestionAttribute('label_input_columns'));
        $inputAttributeWidth = trim((string) $this->getQuestionAttribute('text_input_columns'));

        if ($inputAttributeWidth === "") {
            $inputAttributeWidth = trim((string) $this->getQuestionAttribute('text_input_width'));
        }

        $attributeInputContainerWidth = intval($inputAttributeWidth);
        if ($attributeInputContainerWidth < 1 || $attributeInputContainerWidth > 12) {
            $attributeInputContainerWidth = null;
        }

        $attributeLabelWidth =  ($labelAttributeWidth === 'hidden')
            ? 0
            : (
                ($labelAttributeWidth < 1 || $labelAttributeWidth > 12)
                ? null
                : intval($labelAttributeWidth)
            );

        if ($attributeInputContainerWidth === null && $attributeLabelWidth === null) {
            $sInputContainerWidth = 8;
            $sLabelWidth = 4;
            $defaultWidth = true;
        } else {
            if ($attributeInputContainerWidth !== null) {
                $sInputContainerWidth = $attributeInputContainerWidth;
            } elseif ($attributeLabelWidth == 12) {
                $sInputContainerWidth = 12;
            } else {
                $sInputContainerWidth = 12 - $attributeLabelWidth;
            }

            if (!is_null($attributeLabelWidth)) {
                $sLabelWidth = $attributeLabelWidth;
            } elseif ($attributeInputContainerWidth == 12) {
                $sLabelWidth = 12;
            } else {
                $sLabelWidth = 12 - $attributeInputContainerWidth;
            }

            $defaultWidth = false;
        }
        return array(
            'sLabelWidth' => $sLabelWidth,
            'sInputContainerWidth' => $sInputContainerWidth,
            'defaultWidth' => $defaultWidth,
        );
    }

    /**
    * Include Keypad headers
    */
    public function includeKeypad()
    {
        Yii::app()->getClientScript()->registerCssFile(Yii::app()->getConfig('vendor') . "jquery-keypad/jquery.keypad.alt.css");

        $this->aScriptFiles[] = ['path' => Yii::app()->getConfig('vendor') . 'jquery-keypad/jquery.plugin.min.js', 'position' => LSYii_ClientScript::POS_BEGIN];
        $this->aScriptFiles[] = ['path' => Yii::app()->getConfig('vendor') . 'jquery-keypad/jquery.keypad.min.js', 'position' => LSYii_ClientScript::POS_BEGIN];
        $localefile = Yii::app()->getConfig('vendor') . 'jquery-keypad/jquery.keypad-' . App()->language . '.js';
        if (App()->language != 'en' && file_exists($localefile)) {
            $this->aScriptFiles[] = ['path' => Yii::app()->getConfig('vendor') . 'jquery-keypad/jquery.keypad-' . App()->language . '.js', 'position' => LSYii_ClientScript::POS_BEGIN];
        }
    }

    /**
     * Returns the question script to render depending on the language.
     * If "Use for all languages" is set, the base language's script is used.
     * @return string|null
     */
    protected function getQuestionScript()
    {
        $language = $this->oQuestion->same_script ? $this->oQuestion->survey->language : $this->sLanguage;
        $script = null;
        if (!empty($this->oQuestion->questionl10ns[$language]->script)) {
            $script = $this->oQuestion->questionl10ns[$language]->script;
        }
        return $script;
    }

    abstract public function getMainView();
    abstract public function getRows();
    abstract public function render();
}
