<?php
// This file is part of mod_offlinequiz for 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/>.

/**
 * Define the steps used by the restore_offlinequiz_activity_task
 *
 * @package       mod_offlinequiz
 * @subpackage    offlinequiz
 * @author        Juergen Zimmer <zimmerj7@univie.ac.at>
 * @copyright     2015 Academic Moodle Cooperation {@link http://www.academic-moodle-cooperation.org}
 * @since         Moodle 2.2+
 * @license       http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 *
 **/

/**
 * Structure step to restore one offlinequiz activity
 *
 * @copyright     2015 Academic Moodle Cooperation {@link http://www.academic-moodle-cooperation.org}
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
class restore_offlinequiz_activity_structure_step extends restore_questions_activity_structure_step {
    /**
     * placeholder value for the current offlinequiz_results entry being processed
     * @var stdClass
     */
    private $currentofflinequizresult = null;
    /**
     * placeholder value for the current offlinequiz_groups entry being processed
     * @var stdClass
     */
    private $currentofflinegroup = null;
    /**
     * qtype plugin restorers
     * @var array
     */
    private $qtypeplugins = [];
    /**
     * Define the structure of the restore.
     */
    protected function define_structure() {

        $paths = [];
        $userinfo = $this->get_setting_value('userinfo');

        $offlinequiz = new restore_path_element('offlinequiz', '/activity/offlinequiz');
        $paths[] = $offlinequiz;

        $paths[] = new restore_path_element(
            'offlinequiz_plist',
            '/activity/offlinequiz/plists/plist'
        );

        // Handle offlinequiz groups.
        // We need to identify this path to add the question usages.
        $offlinequizgroup = new restore_path_element(
            'offlinequiz_group',
            '/activity/offlinequiz/groups/group'
        );
        $paths[] = $offlinequizgroup;

        // Add template question usages for offline groups.
        $this->add_question_usages($offlinequizgroup, $paths, 'group_');

        $groupquestion = new restore_path_element(
            'offlinequiz_groupquestion',
            '/activity/offlinequiz/groups/group/groupquestions/groupquestion'
        );
        $paths[] = $groupquestion;
        if ($this->task->get_old_moduleversion() >= 2024071203) {
            $this->add_question_references($groupquestion, $paths);
        }

        // We only add the results if userinfo was activated.
        if ($userinfo) {
            // Scanned pages and their choices and corners.
            $paths[] = new restore_path_element('offlinequiz_scannedpage', '/activity/offlinequiz/scannedpages/scannedpage');
            $paths[] = new restore_path_element(
                'offlinequiz_choice',
                '/activity/offlinequiz/scannedpages/scannedpage/choices/choice'
            );
            $paths[] = new restore_path_element(
                'offlinequiz_corner',
                '/activity/offlinequiz/scannedpages/scannedpage/corners/corner'
            );

            // Lists of participants and their scanned pages.
            $paths[] = new restore_path_element(
                'offlinequiz_participant',
                '/activity/offlinequiz/plists/plist/participants/participant'
            );
            $paths[] = new restore_path_element(
                'offlinequiz_scannedppage',
                '/activity/offlinequiz/scannedppages/scannedppage'
            );
            $paths[] = new restore_path_element(
                'offlinequiz_pchoice',
                '/activity/offlinequiz/scannedppages/scannedppage/pchoices/pchoice'
            );
            $offlinequizresult = new restore_path_element(
                'offlinequiz_result',
                '/activity/offlinequiz/results/result'
            );
            $paths[] = $offlinequizresult;

            // Add the results' question usages.
            $this->add_question_usages($offlinequizresult, $paths, 'result_');
        }

        // Return the paths wrapped into standard activity structure.
        return $this->prepare_activity_structure($paths);
    }

    /**
     * templates for documents
     * @param mixed $data
     * @return void
     */
    public function process_group_question_usage($data) {
        $this->restore_question_usage_worker($data, 'group_');
    }

    /**
     * process attempt
     * @param mixed $data
     * @return void
     */
    public function process_group_question_attempt($data) {
        $this->restore_question_attempt_worker($data, 'group_');
    }
    /**
     * process group question attempt
     * @param mixed $data
     * @return void
     */
    public function process_group_question_attempt_step($data) {
        global $DB;
        $groupvariable = $data["group_response"]["group_variable"];
        $this->restore_question_attempt_step_worker($data, 'group_');
        $oldquestionid = $DB->get_field(
            'question_attempts',
            'questionid',
            ['id' => $this->elementsoldid["group_question_attempt"]]
        );
        $newquestionid = $DB->get_field(
            'question_attempts',
            'questionid',
            ['id' => $this->elementsnewid["group_question_attempt"]]
        );
        if ($oldquestionid == $newquestionid) { // Duplicate.
            $stepid = $DB->get_field(
                'question_attempt_steps',
                'id',
                ['questionattemptid' => $this->elementsnewid["group_question_attempt"]]
            );
            if ($stepid) {
                $steporder = $DB->get_record('question_attempt_step_data', ['attemptstepid' => $stepid, 'name' => "_order"]);
                if ($steporder && $steporder->value == '') {
                    foreach ($groupvariable as $key => $variable) {
                        if ($variable['name'] == '_order') {
                            $steporder->value = $variable['value'];
                            $DB->update_record('question_attempt_step_data', $steporder);
                        }
                    }
                }
            }
        }
    }

    /**
     * This method does the actual work for process_question_attempt or
     * process_{nameprefix}_question_attempt.
     * @param array $data the data from the XML file.
     * @param string $nameprefix the element name prefix.
     */
    protected function restore_question_attempt_worker($data, $nameprefix) {
        global $DB;

        $data = (object)$data;
        $oldid = $data->id;

        $question = $this->get_mapping('question', $data->questionid);
        $data->questionid = $question->newitemid;

        $data->questionusageid = $this->get_new_parentid($nameprefix . 'question_usage');

        if (!property_exists($data, 'variant')) {
            $data->variant = 1;
        }

        if (!property_exists($data, 'maxfraction')) {
            $data->maxfraction = 1;
        }

        $newitemid = $DB->insert_record('question_attempts', $data);

        $this->set_mapping($nameprefix . 'question_attempt', $oldid, $newitemid);
        if (isset($question->info->qtype)) {
            $qtype = $question->info->qtype;
        } else {
            $qtype = $DB->get_record('question', ['id' => $data->questionid])->qtype;
        }
        $this->qtypes[$newitemid] = $qtype;
        $this->newquestionids[$newitemid] = $data->questionid;
    }

    /**
     * Process question references which replaces the direct connection to quiz slots to question.
     *
     * @param array $data the data from the XML file.
     */
    public function process_question_reference($data) {
        global $DB;
        $data = (object) $data;
        $data->usingcontextid = $this->get_mappingid('context', $data->usingcontextid);
        $data->itemid = $this->get_new_parentid('offlinequiz_groupquestion');
        if ($entry = $this->get_mappingid('question_bank_entry', $data->questionbankentryid)) {
            $data->questionbankentryid = $entry;
        }
        $DB->insert_record('question_references', $data);
    }
    /**
     * process result question usages
     * @param mixed $data
     * @return void
     */
    public function process_result_question_usage($data) {
        $this->restore_question_usage_worker($data, 'result_');
    }

    /**
     * process result question attempt
     * @param mixed $data
     * @return void
     */
    public function process_result_question_attempt($data) {
        $this->restore_question_attempt_worker($data, 'result_');
    }

    /**
     * process result question attempt steps
     * @param mixed $data
     * @return void
     */
    public function process_result_question_attempt_step($data) {
        $this->restore_question_attempt_step_worker($data, 'result_');
    }

    /**
     * Restore method for the activity.
     * @param mixed $data
     * @return void
     */
    protected function process_offlinequiz($data) {
        global $CFG, $DB;

        $data = (object) $data;
        $oldid = $data->id;
        $data->course = $this->get_courseid();

        $data->timeopen = $this->apply_date_offset($data->timeopen);
        $data->timeclose = $this->apply_date_offset($data->timeclose);
        $data->timecreated = $this->apply_date_offset($data->timecreated);
        $data->timemodified = $this->apply_date_offset($data->timemodified);
        $data->questions = '';

        // The offlinequiz->results can come both in data->results and
        // data->results_number, handle both. MDL-26229.
        if (isset($data->results_number)) {
            $data->results = $data->results_number;
            unset($data->results_number);
        }

        // Insert the offlinequiz record.
        $newitemid = $DB->insert_record('offlinequiz', $data);
        // Immediately after inserting "activity" record, call this.
        $this->apply_activity_instance($newitemid);
    }

    /**
     * Restore method for offline groups.
     * @param mixed $data
     * @return void
     */
    protected function process_offlinequiz_group($data) {
        global $DB;

        $data = (object) $data;
        $oldid = $data->id;

        $data->offlinequizid = $this->get_new_parentid('offlinequiz');

        if (empty($data->templateusageid)) {
            $newitemid = $DB->insert_record('offlinequiz_groups', $data);
            // Save offlinequiz_group->id mapping, because logs use it.
            $this->set_mapping('offlinequiz_group', $oldid, $newitemid, false);
        } else {
            // The data is actually inserted into the database later in inform_new_usage_id.
            $this->currentofflinegroup = clone($data);
        }
    }

    /**
     * Restore method for offlinequiz group questions.
     * @param mixed $data
     * @return void
     */
    protected function process_offlinequiz_groupquestion($data) {
        global $DB;

        $data = (object) $data;
        $oldid = $data->id;

        // Backward compatibility for old field names prior to Moodle 2.8.5.
        if (isset($data->usageslot) && !isset($data->slot)) {
            $data->slot = $data->usageslot;
        }
        if (isset($data->pagenumber) && !isset($data->page)) {
            $data->page = $data->pagenumber;
        }

        $data->offlinequizid = $this->get_new_parentid('offlinequiz');
        $data->offlinegroupid = $this->get_new_parentid('offlinequiz_group');
        if ($newid = $this->get_mappingid('question', $data->questionid)) {
            $data->questionid = $newid;
        }
        $newitemid = $DB->insert_record('offlinequiz_group_questions', $data);
        $this->set_mapping('offlinequiz_groupquestion', $oldid, $newitemid);
        if ($this->task->get_old_moduleversion() < 2022071500 || $newid == false) {
            $data = (object) $data;
            $data->usingcontextid = $this->task->get_contextid();
            $data->itemid = $newitemid;
            $this->get_new_parentid('offlinequiz_group_question');
            $data->component = 'mod_offlinequiz';
            $data->questionarea = 'slot';
            // Fill in the selected version form question_version.
            if ($entry = $DB->get_field('question_versions', 'questionbankentryid', ['questionid' => $data->questionid])) {
                $data->questionbankentryid = $entry;
            }
            $data->version = $DB->get_field('question_versions', 'version', ['questionid' => $data->questionid]);
            $DB->insert_record('question_references', $data);
        }
    }

    /**
     * Restore method for scanned pages.
     * @param mixed $data
     * @return void
     */
    protected function process_offlinequiz_scannedpage($data) {
        global $DB;

        $data = (object) $data;
        $oldid = $data->id;

        $data->offlinequizid = $this->get_new_parentid('offlinequiz');
        $data->resultid = $this->get_mappingid('offlinequiz_result', $data->resultid);

        $newitemid = $DB->insert_record('offlinequiz_scanned_pages', $data);
        $this->set_mapping('offlinequiz_scannedpage', $oldid, $newitemid, true);
    }

    /**
     * Restore method for choices on scanned pages.
     * @param mixed $data
     * @return void
     */
    protected function process_offlinequiz_choice($data) {
        global $DB;

        $data = (object) $data;
        $oldid = $data->id;

        $data->scannedpageid = $this->get_new_parentid('offlinequiz_scannedpage');

        $newitemid = $DB->insert_record('offlinequiz_choices', $data);
    }

    /**
     *  Restore method for corners of scanned pages.
     * @param mixed $data
     * @return void
     */
    protected function process_offlinequiz_corner($data) {
        global $DB;

        $data = (object) $data;
        $oldid = $data->id;

        $data->scannedpageid = $this->get_new_parentid('offlinequiz_scannedpage');

        $newitemid = $DB->insert_record('offlinequiz_page_corners', $data);
    }

    /**
     * Restore method for scanned participants pages.
     * @param mixed $data
     * @return void
     */
    protected function process_offlinequiz_scannedppage($data) {
        global $DB;

        $data = (object) $data;
        $oldid = $data->id;

        $data->offlinequizid = $this->get_new_parentid('offlinequiz');

        $newitemid = $DB->insert_record('offlinequiz_scanned_p_pages', $data);
        $this->set_mapping('offlinequiz_scannedppage', $oldid, $newitemid, true);
    }

    /**
     * Restore method for choices on scanned participants pages.
     * @param mixed $data
     * @return void
     */
    protected function process_offlinequiz_pchoice($data) {
        global $DB;

        $data = (object) $data;
        $oldid = $data->id;

        $data->scannedppageid = $this->get_new_parentid('offlinequiz_scannedppage');

        $newitemid = $DB->insert_record('offlinequiz_p_choices', $data);
    }

    /**
     * Restore method for lists of participants.
     * @param mixed $data
     * @return void
     */
    protected function process_offlinequiz_plist($data) {
        global $DB;

        $data = (object) $data;
        $oldid = $data->id;

        $data->offlinequizid = $this->get_new_parentid('offlinequiz');

        $newitemid = $DB->insert_record('offlinequiz_p_lists', $data);
        $this->set_mapping('offlinequiz_plist', $oldid, $newitemid, true);
    }

    /**
     * Restore method for a participant.
     * @param mixed $data
     * @return void
     */
    protected function process_offlinequiz_participant($data) {
        global $DB;

        $data = (object) $data;
        $oldid = $data->id;

        $data->listid = $this->get_new_parentid('offlinequiz_plist');

        $newitemid = $DB->insert_record('offlinequiz_participants', $data);
    }

    /**
     * Restore method for offlinequiz results (attempts).
     * @param mixed $data
     * @return void
     */
    protected function process_offlinequiz_result($data) {
        global $DB;

        $data = (object) $data;

        $data->offlinequizid = $this->get_new_parentid('offlinequiz');

        $data->offlinegroupid = $this->get_mappingid('offlinequiz_group', $data->offlinegroupid);
        $data->userid = $this->get_mappingid('user', $data->userid);
        $data->teacherid = $this->get_mappingid('user', $data->teacherid);
        // The usageid is set in the function below.

        $data->timestart = $this->apply_date_offset($data->timestart);
        $data->timefinish = $this->apply_date_offset($data->timefinish);
        $data->timemodified = $this->apply_date_offset($data->timemodified);

        // The data is actually inserted into the database later in inform_new_usage_id.
        $this->currentofflinequizresult = clone($data);
    }

    /**
     * Restore the usage id after it has been created.
     * @param mixed $newusageid
     * @return void
     */
    protected function inform_new_usage_id($newusageid) {
        global $DB;

        // We might be dealing with a result.
        $data = $this->currentofflinequizresult;
        if ($data) {
            $this->currentofflinequizresult = null;
            $oldid = $data->id;
            $data->usageid = $newusageid;

            $newitemid = $DB->insert_record('offlinequiz_results', $data);

            // Save offlinequiz_result->id mapping, because scanned pages use it.
            $this->set_mapping('offlinequiz_result', $oldid, $newitemid, false);
        } else {
            // Or we might be dealing with an offlinequiz group.
            $data = $this->currentofflinegroup;
            if ($data) {
                $this->currentofflinegroup = null;
                $oldid = $data->id;
                $data->templateusageid = $newusageid;

                $newitemid = $DB->insert_record('offlinequiz_groups', $data);

                // Save offlinequiz_group->id mapping, because offlinequiz_results use it.
                $this->set_mapping('offlinequiz_group', $oldid, $newitemid, false);
            }
        }
    }
    /**
     * add files at the end
     * @return void
     */
    protected function after_execute() {
        parent::after_execute();
        $userinfo = $this->get_setting_value('userinfo');
        // Add offlinequiz related files, no need to match by itemname (just internally handled context).
        $this->add_related_files('mod_offlinequiz', 'intro', null);
        if ($userinfo) {
            $this->add_related_files('mod_offlinequiz', 'imagefiles', null);
            $this->add_related_files('mod_offlinequiz', 'queue', 'offlinequiz_queue');
            $this->add_related_files('mod_offlinequiz', 'queuedata', 'offlinequiz_queue_data');
        }
        $this->add_related_files('mod_offlinequiz', 'pdfs', null);
    }

    /**
     * Get the restore_qtype_plugin subclass for a specific question type.
     * @param string $qtype e.g. multichoice.
     * @return restore_qtype_plugin instance.
     */
    protected function get_qtype_restorer($qtype) {
        if (!isset($this->qtypeplugins[$qtype])) {
            $classname = 'restore_qtype_' . $qtype . '_plugin';
            if (class_exists($classname)) {
                $this->qtypeplugins[$qtype] = new $classname('qtype', $qtype, $this);
            } else {
                $this->qtypeplugins[$qtype] = null;
            }
        }
        return $this->qtypeplugins[$qtype];
    }
}
