<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle 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 3 of the License, or
// (at your option) any later version.
//
// Moodle 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.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.

/**
 * Defines the editing form for the calculated question type.
 *
 * @package    qtype
 * @subpackage calculated
 * @copyright  2007 Jamie Pratt me@jamiep.org
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */


defined('MOODLE_INTERNAL') || die();

require_once($CFG->dirroot . '/question/type/numerical/edit_numerical_form.php');


/**
 * Calculated question type editing form definition.
 *
 * @copyright  2007 Jamie Pratt me@jamiep.org
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
class qtype_calculated_edit_form extends qtype_numerical_edit_form {
    /**
     * Handle to the question type for this question.
     *
     * @var qtype_calculated
     */
    public $qtypeobj;
    public $questiondisplay;
    public $activecategory;
    public $categorychanged = false;
    public $initialname = '';
    public $reload = false;

    public function __construct($submiturl, $question, $category, $contexts,
            $formeditable = true) {
        global $CFG, $DB;
        $this->question = $question;
        $this->reload = optional_param('reload', false, PARAM_BOOL);

        if (!$this->reload) { // Use database data as this is first pass.
            if (isset($this->question->id)) {
                // Remove prefix #{..}# if exists.
                $this->initialname = $question->name;
                $question->name = question_bank::get_qtype($this->qtype())
                        ->clean_technical_prefix_from_question_name($question->name);
            }
        }
        parent::__construct($submiturl, $question, $category, $contexts, $formeditable);
    }

    public function get_per_answer_fields($mform, $label, $gradeoptions,
            &$repeatedoptions, &$answersoption) {
        $repeated = parent::get_per_answer_fields($mform, $label, $gradeoptions,
                $repeatedoptions, $answersoption);

        // Reorganise answer options group. 0 is the answer. 1 is tolerance. 2 is Grade.
        $answeroptions = $repeated[0]->getElements();
        // Tolerance field will be part of its own group.
        $tolerance = $answeroptions[1];

        // Update Answer options group to contain only answer and grade fields.
        $answeroptions[0]->setSize(55);
        $answeroptions = array($answeroptions[0], $answeroptions[2]);
        $repeated[0]->setElements($answeroptions);

        // Update answer field and group label.
        $repeated[0]->setLabel(get_string('answerformula', 'qtype_calculated', '{no}') . ' =');
        $answeroptions[0]->setLabel(get_string('answerformula', 'qtype_calculated', '{no}') . ' =');

        // Get feedback field to re append later.
        $feedback = array_pop($repeated);

        // Create tolerance group.
        $answertolerance = array();
        $tolerance->setLabel(get_string('tolerance', 'qtype_calculated') . '=');
        $answertolerance[] = $tolerance;
        $answertolerance[] = $mform->createElement('select', 'tolerancetype',
                get_string('tolerancetype', 'qtype_calculated'), $this->qtypeobj->tolerance_types());
        $repeated[] = $mform->createElement('group', 'answertolerance',
                 get_string('tolerance', 'qtype_calculated'), $answertolerance, null, false);
        $repeatedoptions['tolerance']['default'] = 0.01;

        // Create display group.
        $answerdisplay = array();
        $answerdisplay[] = $mform->createElement('select', 'correctanswerlength',
                get_string('answerdisplay', 'qtype_calculated'), range(0, 9));
        $repeatedoptions['correctanswerlength']['default'] = 2;

        $answerlengthformats = array(
            '1' => get_string('decimalformat', 'qtype_numerical'),
            '2' => get_string('significantfiguresformat', 'qtype_calculated')
        );
        $answerdisplay[] = $mform->createElement('select', 'correctanswerformat',
                get_string('correctanswershowsformat', 'qtype_calculated'), $answerlengthformats);
        $repeated[] = $mform->createElement('group', 'answerdisplay',
                 get_string('answerdisplay', 'qtype_calculated'), $answerdisplay, null, false);

        // Add feedback.
        $repeated[] = $feedback;

        return $repeated;
    }

    /**
     * Add question-type specific form fields.
     *
     * @param MoodleQuickForm $mform the form being built.
     */
    protected function definition_inner($mform) {
        $this->qtypeobj = question_bank::get_qtype($this->qtype());
        $label = get_string('sharedwildcards', 'qtype_calculated');
        $mform->addElement('hidden', 'initialcategory', 1);
        $mform->addElement('hidden', 'reload', 1);
        $mform->setType('initialcategory', PARAM_INT);
        $mform->setType('reload', PARAM_BOOL);
        $html2 = $this->qtypeobj->print_dataset_definitions_category($this->question);
        $mform->insertElementBefore(
                $mform->createElement('static', 'listcategory', $label, $html2), 'name');
        if (isset($this->question->id)) {
            $mform->insertElementBefore($mform->createElement('static', 'initialname',
                    get_string('questionstoredname', 'qtype_calculated'),
                    format_string($this->initialname)), 'name');
        };
        $addfieldsname = 'updatecategory';
        $addstring = get_string('updatecategory', 'qtype_calculated');
        $mform->registerNoSubmitButton($addfieldsname);

        $mform->insertElementBefore(
                $mform->createElement('submit', $addfieldsname, $addstring), 'listcategory');
        $mform->registerNoSubmitButton('createoptionbutton');

        // Editing as regular question.
        $mform->setType('single', PARAM_INT);

        $mform->addElement('hidden', 'shuffleanswers', '1');
        $mform->setType('shuffleanswers', PARAM_INT);
        $mform->addElement('hidden', 'answernumbering', 'abc');
        $mform->setType('answernumbering', PARAM_SAFEDIR);

        $this->add_per_answer_fields($mform, get_string('answerhdr', 'qtype_calculated', '{no}'),
                question_bank::fraction_options(), 1, 1);

        $repeated = array();

        $this->add_unit_options($mform, $this);
        $this->add_unit_fields($mform, $this);
        $this->add_interactive_settings();

        // Hidden elements.
        $mform->addElement('hidden', 'synchronize', '');
        $mform->setType('synchronize', PARAM_INT);
        $mform->addElement('hidden', 'wizard', 'datasetdefinitions');
        $mform->setType('wizard', PARAM_ALPHA);
    }

    protected function can_preview() {
        return false; // Generally not possible for calculated questions on this page.
    }

    public function data_preprocessing($question) {
        $question = parent::data_preprocessing($question);
        $question = $this->data_preprocessing_answers($question);
        $question = $this->data_preprocessing_hints($question);
        $question = $this->data_preprocessing_units($question);
        $question = $this->data_preprocessing_unit_options($question);

        if (isset($question->options->synchronize)) {
            $question->synchronize = $question->options->synchronize;
        }

        return $question;
    }

    protected function data_preprocessing_answers($question, $withanswerfiles = false) {
        $question = parent::data_preprocessing_answers($question, $withanswerfiles);
        if (empty($question->options->answers)) {
            return $question;
        }

        $key = 0;
        foreach ($question->options->answers as $answer) {
            // See comment in the parent method about this hack.
            unset($this->_form->_defaultValues["tolerancetype[{$key}]"]);
            unset($this->_form->_defaultValues["correctanswerlength[{$key}]"]);
            unset($this->_form->_defaultValues["correctanswerformat[{$key}]"]);

            $question->tolerancetype[$key]       = $answer->tolerancetype;
            $question->correctanswerlength[$key] = $answer->correctanswerlength;
            $question->correctanswerformat[$key] = $answer->correctanswerformat;
            $key++;
        }

        return $question;
    }

    public function qtype() {
        return 'calculated';
    }

    /**
     * Validate the equations in the some question content.
     * @param array $errors where errors are being accumulated.
     * @param string $field the field being validated.
     * @param string $text the content of that field.
     * @return array the updated $errors array.
     */
    protected function validate_text($errors, $field, $text) {
        $problems = qtype_calculated_find_formula_errors_in_text($text);
        if ($problems) {
            $errors[$field] = $problems;
        }
        return $errors;
    }

    public function validation($data, $files) {
        $errors = parent::validation($data, $files);

        // Verifying for errors in {=...} in question text.
        $errors = $this->validate_text($errors, 'questiontext', $data['questiontext']['text']);
        $errors = $this->validate_text($errors, 'generalfeedback', $data['generalfeedback']['text']);

        // Check that the answers use datasets.
        $answers = $data['answer'];
        $mandatorydatasets = array();
        foreach ($answers as $key => $answer) {
            $problems = qtype_calculated_find_formula_errors($answer);
            if ($problems) {
                $errors['answeroptions['.$key.']'] = $problems;
            }
            $mandatorydatasets += $this->qtypeobj->find_dataset_names($answer);
            $errors = $this->validate_text($errors, 'feedback[' . $key . ']',
                    $data['feedback'][$key]['text']);
        }
        if (empty($mandatorydatasets)) {
            foreach ($answers as $key => $answer) {
                $errors['answeroptions['.$key.']'] =
                        get_string('atleastonewildcard', 'qtype_calculated');
            }
        }

        // Validate the answer format.
        foreach ($answers as $key => $answer) {
            $trimmedanswer = trim($answer);
            if (trim($answer)) {
                if ($data['correctanswerformat'][$key] == 2 &&
                        $data['correctanswerlength'][$key] == '0') {
                    $errors['answerdisplay['.$key.']'] =
                            get_string('zerosignificantfiguresnotallowed', 'qtype_calculated');
                }
            }
        }

        return $errors;
    }

    protected function is_valid_answer($answer, $data) {
        return !qtype_calculated_find_formula_errors($answer);
    }

    protected function valid_answer_message($answer) {
        if (!$answer) {
            return get_string('mustenteraformulaorstar', 'qtype_calculated');
        } else {
            return qtype_calculated_find_formula_errors($answer);
        }
    }
}
