<?php
// This file is part of Moodle - https://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 <https://www.gnu.org/licenses/>.

namespace mod_minilesson\local\itemform;

///////////////////////////////////////////////////////////////////////////
//
// This file is part of Moodle - http://moodle.org/                      //
// Moodle - Modular Object-Oriented Dynamic Learning Environment         //
//
// 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/>.       //
//
///////////////////////////////////////////////////////////////////////////

/**
 * Forms for minilesson Activity
 *
 * @package    mod_minilesson
 * @author     Justin Hunt <poodllsupport@gmail.com>
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL
 * @copyright  (C) 1999 onwards Justin Hunt  http://poodll.com
 */

// why do we need to include this?
require_once($CFG->libdir . '/formslib.php');

use mod_minilesson\constants;
use mod_minilesson\local\formelement\sentenceprompt;
use mod_minilesson\local\formelement\ttsaudio;
use mod_minilesson\local\itemtype\item;
use mod_minilesson\utils;

/**
 * Abstract class that item type's inherit from.
 *
 * This is the abstract class that add item type forms must extend.
 *
 * @abstract
 * @copyright  2014 Justin Hunt
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
abstract class baseform extends \moodleform {


    /**
     * The simple string that describes the item type e.g. audioitem, textitem
     * @var string
     */
    public $typestring;


    /**
     * An array of options used in the htmleditor
     * @var array
     */
    protected $editoroptions = [];

    /**
     * An array of options used in the filemanager
     * @var array
     */
    protected $filemanageroptions = [];

    /**
     * The module instance
     * @var array|object
     */
    protected $moduleinstance = null;

    /**
     * The context of the module
     * @var \context
     */
    protected $context = null;


    /**
     * True if this is a standard item of false if it does something special.
     * items are standard items
     * @var bool
     */
    protected $standard = true;

    public const ITEMCLASS = item::class;

    /**
     * Each item type can and should override this to add any custom elements to
     * the basic form that they want
     */
    public function custom_definition() {
    }

    /**
     * Used to determine if this is a standard item or a special item
     * @return bool
     */
    final public function is_standard() {
        return (bool) $this->standard;
    }

    /**
     * Add the required basic elements to the form.
     *
     * This method adds the basic elements to the form including title and contents
     * and then calls custom_definition();
     */
    final public function definition() {
        global $CFG, $OUTPUT;

        $m35 = $CFG->version >= 2018051700;
        $mform = $this->_form;
        $this->editoroptions = $this->_customdata['editoroptions'];
        $this->filemanageroptions = $this->_customdata['filemanageroptions'];
        $this->moduleinstance = $this->_customdata['moduleinstance'];
        $this->context = $this->_customdata['context'] ?? null;
        if (!$this->context && is_object($this->moduleinstance) && isset($this->moduleinstance->context)) {
            $this->context = $this->moduleinstance->context;
        }
        if (!$this->context && !empty($this->moduleinstance->id)) {
            $cm = get_coursemodule_from_instance('minilesson', $this->moduleinstance->id);
            if ($cm) {
                $this->context = \context_module::instance($cm->id);
            }
        }

        $mform->addElement('header', 'typeheading', get_string('createaitem', constants::M_COMPONENT, get_string($this->type, constants::M_COMPONENT)));

        $mform->addElement('hidden', 'id');
        $mform->setType('id', PARAM_INT);

        $mform->addElement('hidden', 'itemid');
        $mform->setType('itemid', PARAM_INT);

        if ($this->standard === true) {
            $mform->addElement('hidden', 'type');
            $mform->setType('type', PARAM_TEXT);

            $mform->addElement('hidden', 'itemorder');
            $mform->setType('itemorder', PARAM_INT);

            $mform->addElement('text', 'name', get_string('itemtitle', constants::M_COMPONENT), ['size' => 70]);
            $mform->setType('name', PARAM_TEXT);
            $mform->addRule('name', get_string('required'), 'required', null, 'client');
            $typelabel = get_string($this->type, constants::M_COMPONENT);
            $mform->setDefault('name', get_string('newitem', constants::M_COMPONENT, $typelabel));

            if ($this->moduleinstance->richtextprompt == constants::M_PROMPT_RICHTEXT) {
                $someid = \html_writer::random_id();
                $mform->addElement(
                    'editor',
                    constants::TEXTQUESTION . '_editor',
                    get_string('itemcontents', constants::M_COMPONENT),
                    ['id' => $someid, 'wrap' => 'virtual', 'style' => 'width: 100%;', 'rows' => '5'],
                    $this->editoroptions
                );
                $this->_form->setDefault(constants::TEXTQUESTION . '_editor', ['text' => '', 'format' => FORMAT_HTML]);
                $mform->setType(constants::TEXTQUESTION, PARAM_RAW);
            } else {
                // Question instructions
                $mform->addElement('text', constants::TEXTINSTRUCTIONS, get_string('iteminstructions', constants::M_COMPONENT), ['size' => 70]);
                $mform->setType(constants::TEXTINSTRUCTIONS, PARAM_RAW);

                // Question text
                $mform->addElement('textarea', constants::TEXTQUESTION, get_string('itemcontents', constants::M_COMPONENT), ['wrap' => 'virtual', 'style' => 'width: 100%;']);
                $mform->setType(constants::TEXTQUESTION, PARAM_RAW);
                // add layout
                $this->add_layoutoptions();

                // Add the media prompts chooser and fields.
                $mform->addElement('header', 'mediapromptsheading', get_string('mediaprompts', constants::M_COMPONENT));
                $this->add_media_prompts();
                $mform->setExpanded('mediapromptsheading', true);
            }// End of if richtextprompt or not.
        }// End of if standard = true.

        // Visibility.
        // $mform->addElement('selectyesno', 'visible', get_string('visible'));
        $mform->addElement('hidden', 'visible', 1);
        $mform->setType('visible', PARAM_INT);

        $this->custom_definition();

        // Add the action buttons.
        $mform->closeHeaderBefore('cancel');
        $this->add_action_buttons(get_string('cancel'), get_string('saveitem', constants::M_COMPONENT));
    }

    protected function add_itemsettings_heading() {
        // add the heading
        $this->_form->addElement('header', 'itemsettingsheading', get_string('itemsettingsheadings', constants::M_COMPONENT));
        $this->_form->setExpanded('itemsettingsheading');
    }

    final protected function add_static_text($name, $label = null, $text = '') {

        $this->_form->addElement('static', $name, $label, $text);
    }

    final protected function add_repeating_textboxes($name, $repeatno = 5) {
        global $DB;

        $additionalfields = 1;
        $repeatarray = [];
        $repeatarray[] = $this->_form->createElement('text', $name, get_string($name . 'no', constants::M_COMPONENT));
        // $repeatarray[] = $this->_form->createElement('text', 'limit', get_string('limitno', constants::M_COMPONENT));
        // $repeatarray[] = $this->_form->createElement('hidden', $name . 'id', 0);
        /*
        if ($this->_instance){
            $repeatno = $DB->count_records('choice_options', array('choiceid'=>$this->_instance));
            $repeatno += $additionalfields;
        }
        */

        $repeateloptions = [];
        $repeateloptions[$name]['default'] = '';
        // $repeateloptions[$name]['disabledif'] = array('limitanswers', 'eq', 0);
        // $repeateloptions[$name]['rule'] = 'numeric';
        $repeateloptions[$name]['type'] = PARAM_TEXT;

        $repeateloptions[$name]['helpbutton'] = [$name . '_help', constants::M_COMPONENT];
        $this->_form->setType($name, PARAM_CLEANHTML);

        // $this->_form->setType($name .'id', PARAM_INT);

        $this->repeat_elements(
            $repeatarray,
            $repeatno,
            $repeateloptions,
            $name . '_repeats',
            $name . '_add_fields',
            $additionalfields,
            "add",
            true
        );
    }

    final protected function add_showtextpromptoptions($name, $label, $default = constants::TEXTPROMPT_DOTS) {
        $options = utils::fetch_options_textprompt();
        return $this->add_dropdown($name, $label, $options, $default);
    }
    final protected function add_showignorepuncoptions($name, $label, $default = constants::TEXTPROMPT_DOTS) {
        $options = utils::fetch_options_yesno();
        return $this->add_dropdown($name, $label, $options, $default);
    }

    final protected function add_showlistorreadoptions($name, $label, $default = constants::LISTENORREAD_READ) {
        $options = utils::fetch_options_listenorread();
        return $this->add_dropdown($name, $label, $options, $default);
    }

    final protected function add_dropdown($name, $label, $options, $default = false) {

        $this->_form->addElement('select', $name, $label, $options);
        if ($default !== false) {
            $this->_form->setDefault($name, $default);
        }
    }

    protected function add_media_prompts() {
        global $CFG, $OUTPUT;
        $m35 = true;

        // cut down on the code by using media item types array to pre-prepare fieldsets and media prompt selector
        $mediaprompts = ['addmedia', 'addiframe', 'addttsaudio', 'addtextarea', 'addyoutubeclip', 'addttsdialog', 'addttspassage', 'addaudiostory', 'nativelangchooser'];
        $keyfields = [
            'addmedia' => constants::MEDIAQUESTION,
            'addiframe' => constants::MEDIAIFRAME,
            'addttsaudio' => constants::TTSQUESTION,
            'addtextarea' => constants::QUESTIONTEXTAREA,
            'addyoutubeclip' => constants::YTVIDEOID,
            'addttsdialog' => constants::TTSDIALOG,
            'addttspassage' => constants::TTSPASSAGE,
            'addaudiostory' => constants::AUDIOSTORY,
            'nativelangchooser' => constants::NATIVELANGCHOOSER,
        ];
        $fulloptions = [];
        $fieldsettops = [];
        $fieldsetbottom = "</fieldset>";
        foreach ($mediaprompts as $mediaprompt) {
            // dropdown options for media prompt selector
            $fulloptions[$mediaprompt] = get_string($mediaprompt, constants::M_COMPONENT);
            // fieldset
            $panelopts["mediatype"] = $mediaprompt;
            $panelopts["legend"] = get_string($mediaprompt, constants::M_COMPONENT);
            $panelopts["keyfield"] = $keyfields[$mediaprompt];
            $panelopts["instructions"] = get_string($mediaprompt . '_instructions', constants::M_COMPONENT);
            $fieldsettops[$mediaprompt] = $OUTPUT->render_from_template('mod_minilesson/mediapromptfieldset', $panelopts);
        }

        // lets make life easy with short access to $this->_form
        $mform = $this->_form;

        // add media prompt selector
        $useoptions = [0 => get_string('choosemediaprompt', constants::M_COMPONENT)] + $fulloptions;
        $mform->addElement('select', 'mediaprompts', get_string('mediaprompts', constants::M_COMPONENT), $useoptions);

        // Question media upload
        $mform->addElement('html', $fieldsettops['addmedia'], []);
        $this->add_media_upload(constants::MEDIAQUESTION, get_string('itemmedia', constants::M_COMPONENT));
        $mform->addElement('html', $fieldsetbottom, []);

        // Question media iframe
        $mform->addElement('html', $fieldsettops['addiframe'], []);
        $mform->addElement('text', constants::MEDIAIFRAME, get_string('itemiframe', constants::M_COMPONENT), ['size' => 100]);
        $mform->setType(constants::MEDIAIFRAME, PARAM_RAW);
        // close the fieldset
        $mform->addElement('html', $fieldsetbottom, []);

        // Question text to speech
        $mform->addElement('html', $fieldsettops['addttsaudio'], []);
        $mform->addElement('textarea', constants::TTSQUESTION, get_string('itemttsquestion', constants::M_COMPONENT), ['wrap' => 'virtual', 'style' => 'width: 100%;']);
        $mform->setType(constants::TTSQUESTION, PARAM_RAW);
        $this->add_ttsaudioselect(constants::TTSQUESTIONVOICE, get_string('itemttsquestionvoice', constants::M_COMPONENT));
        $this->add_voiceoptions(constants::TTSQUESTIONOPTION, get_string('choosevoiceoption', constants::M_COMPONENT));
        $mform->addElement('advcheckbox', constants::TTSAUTOPLAY, get_string('autoplay', constants::M_COMPONENT), '');
        $mform->addElement('html', $fieldsetbottom, []);

        // Question itemtextarea
        $mform->addElement('html', $fieldsettops['addtextarea'], []);
        $someid = \html_writer::random_id();
        $edoptions = constants::ITEMTEXTAREA_EDOPTIONS;
        // a bug prevents hideif working, but putting it in a group works dandy
        $groupelements = [];
        $groupelements[] = &$mform->createElement(
            'editor',
            constants::QUESTIONTEXTAREA . '_editor',
            get_string('itemtextarea', constants::M_COMPONENT),
            ['id' => $someid, 'wrap' => 'virtual', 'style' => 'width: 100%;', 'rows' => '5'],
            $edoptions
        );
        $this->_form->setDefault(constants::QUESTIONTEXTAREA . '_editor', ['text' => '', 'format' => FORMAT_HTML]);
        $mform->setType(constants::QUESTIONTEXTAREA, PARAM_RAW);
        $mform->addGroup($groupelements, 'groupelements', get_string('itemtextarea', constants::M_COMPONENT), [' '], false);
        $mform->addElement('html', $fieldsetbottom, []);

        // Question YouTube Clip
        $mform->addElement('html', $fieldsettops['addyoutubeclip'], []);
        $ytarray = [];
        $ytarray[] =& $mform->createElement('text', constants::YTVIDEOID, get_string('itemytid', constants::M_COMPONENT), ['size' => 15, 'placeholder' => "Video ID"]);
        $ytarray[] =& $mform->createElement('text', constants::YTVIDEOSTART, get_string('itemytstart', constants::M_COMPONENT), ['size' => 3, 'placeholder' => "Start"]);
        $ytarray[] =& $mform->createElement('html', 's - ');
        $ytarray[] =& $mform->createElement('text', constants::YTVIDEOEND, get_string('itemytend', constants::M_COMPONENT), ['size' => 3, 'placeholder' => "End"]);
        $ytarray[] =& $mform->createElement('html', 's');

        $mform->addGroup($ytarray, 'ytarray', get_string('ytclipdetails', constants::M_COMPONENT), [' '], false);
        $mform->setType(constants::YTVIDEOID, PARAM_RAW);
        $mform->setType(constants::YTVIDEOSTART, PARAM_INT);
        $mform->setType(constants::YTVIDEOEND, PARAM_INT);
        $mform->addElement('html', $fieldsetbottom, []);

        // Question TTS Dialog
        $mform->addElement('html', $fieldsettops['addttsdialog'], []);
        $ttsdialoginstructionsarray = [];
        $ttsdialoginstructionsarray[] =& $mform->createElement('static', 'ttsdialog_instructions', null, get_string('ttsdialoginstructions', constants::M_COMPONENT));
        $mform->addGroup($ttsdialoginstructionsarray, 'ttsdialog_grp', '', [' '], false);
        // Moodle cant hide static text elements with hideif (why?) , so we wrap it in a group
        // $this->add_static_text('ttsdialog_instructions',null,get_string('ttsdialoginstructions', constants::M_COMPONENT));

        $this->add_ttsaudioselect(constants::TTSDIALOGVOICEA, get_string('ttsdialogvoicea', constants::M_COMPONENT));
        $this->add_ttsaudioselect(constants::TTSDIALOGVOICEB, get_string('ttsdialogvoiceb', constants::M_COMPONENT));
        $this->add_ttsaudioselect(constants::TTSDIALOGVOICEC, get_string('ttsdialogvoicec', constants::M_COMPONENT));
        $mform->addElement('textarea', constants::TTSDIALOG, get_string('ttsdialog', constants::M_COMPONENT), ['wrap' => 'virtual', 'style' => 'width: 100%;', 'placeholder' => 'A) Hello&#10;B) Goodbye']);
        $mform->setType(constants::TTSDIALOG, PARAM_RAW);
        $mform->addElement('advcheckbox', constants::TTSDIALOGVISIBLE, get_string('ttsdialogvisible', constants::M_COMPONENT), get_string('ttsdialogvisible_desc', constants::M_COMPONENT));
        $mform->setDefault(constants::TTSDIALOGVISIBLE, 1);
        $mform->addElement('html', $fieldsetbottom, []);

        // Question TTS Passage
        $mform->addElement('html', $fieldsettops['addttspassage'], []);
        $ttspassageinstructionsarray = [];
        $ttspassageinstructionsarray[] =& $mform->createElement('static', 'ttspassage_instructions', null, get_string('ttspassageinstructions', constants::M_COMPONENT));
        $mform->addGroup($ttspassageinstructionsarray, 'ttspassage_grp', '', [' '], false);
        // Moodle cant hide static text elements with hideif (why?) , so we wrap it in a group
        // $this->add_static_text('ttspassage_instructions',null,get_string('ttspassageinstructions', constants::M_COMPONENT));

        $this->add_ttsaudioselect(constants::TTSPASSAGEVOICE, get_string('ttspassagevoice', constants::M_COMPONENT));
        $this->add_voiceoptions(constants::TTSPASSAGESPEED, get_string('ttspassagespeed', constants::M_COMPONENT));
        $mform->addElement('textarea', constants::TTSPASSAGE, get_string('ttspassage', constants::M_COMPONENT), ['wrap' => 'virtual', 'style' => 'width: 100%;', 'placeholder' => '']);
        $mform->setType(constants::TTSPASSAGE, PARAM_RAW);
        $mform->addElement('html', $fieldsetbottom, []);

        // Question Audio Story
        $mform->addElement('html', $fieldsettops['addaudiostory'], []);
        $mform->addElement('static', 'audiostory_instructions', null, '');
        sentenceprompt::register();
        $this->add_media_upload(constants::AUDIOSTORY, get_string('audiostoryfiles', constants::M_COMPONENT), false, '*', -1);
        $mform->addElement(
            sentenceprompt::ELNAME,
            constants::AUDIOSTORYMETA,
            get_string('audiostorytimes', constants::M_COMPONENT),
            ['rows' => '4', 'columns' => '140', 'style' => 'width: 600px']
        );
        $mform->setType(constants::AUDIOSTORYMETA, PARAM_TEXT);
        $mform->setDefault(constants::AUDIOSTORYMETA, '00:00:00');
        $this->add_zoomandpanoptions();
        $mform->addElement('html', $fieldsetbottom, []);

        // Native Language Chooser.
        $mform->addElement('html', $fieldsettops['nativelangchooser'], []);
        $mform->addElement('advcheckbox', constants::NATIVELANGCHOOSER, get_string('enablenativelanguage', constants::M_COMPONENT), get_string('enablenativelanguage_details', constants::M_COMPONENT));
        $mform->addElement('html', $fieldsetbottom, []);
    }

    /**
     * Convenience function: Adds virtual keyboard settings
     *
     * @param string $enablefield The name of the enable field
     * @param string $customkeysfield The name of the custom keys field
     * @return void
     */
    final protected function add_virtualkeyboard($enablefield, $customkeysfield) {
        $mform = $this->_form;
        // Virtual Keyboard
        $langoptions = utils::get_lang_options();
        $currentlang = $langoptions[$this->moduleinstance->ttslanguage];
        $vkeyboardoptions = [
            0 => get_string('no'),
            1 => $currentlang,
        ];
        if (utils::has_compact_layout($this->moduleinstance->ttslanguage)) {
            $vkeyboardoptions[2] = $currentlang . ' (' . get_string('compact', constants::M_COMPONENT) . ')';
        }
        $vkeyboardoptions[3] = get_string('customlayout', constants::M_COMPONENT);

        $this->add_dropdown($enablefield, get_string('enablevkeyboard', constants::M_COMPONENT), $vkeyboardoptions, 0);

        $mform->addElement('text', $customkeysfield, get_string('customkeys', constants::M_COMPONENT));
        $mform->addHelpButton($customkeysfield, 'customkeys', constants::M_COMPONENT);
        $mform->setType($customkeysfield, PARAM_TEXT);
        $mform->hideIf($customkeysfield, $enablefield, 'neq', 3);
    }

    final protected function add_media_upload($name, $label, $required = false, $accept = '', $maxfiles = 0) {
        global $CFG;

        // If accept is set, add it to the filemanager options
        $filemanageroptions = $this->filemanageroptions;
        if (!empty($accept)) {
            $filemanageroptions['accepted_types'] = $accept;
        }
        // If maxfiles is set, add it to the filemanager options
        if ($maxfiles !== 0) {
            $filemanageroptions['maxfiles'] = $maxfiles;
        }
        $this->_form->addElement(
            'filemanager',
            $name,
            $label,
            null,
            $filemanageroptions
        );
    }

    final protected function add_media_prompt_upload($label = null, $required = false) {
        $accept = '';
        return $this->add_media_upload(constants::AUDIOPROMPT, $label, $required, $accept);
    }

    /**
     * Convenience function: Adds an response editor
     *
     * @param int $count The count of the element to add
     * @param string $label, null means default
     * @param bool $required
     * @return void
     */
    final protected function add_editorarearesponse($count, $label = null, $required = false) {
        if ($label === null) {
            $label = get_string('response', constants::M_COMPONENT);
        }
        // edoptions = array('noclean'=>true)
        $this->_form->addElement('editor', constants::TEXTANSWER . $count . '_editor', $label, ['rows' => '4', 'columns' => '80'], $this->editoroptions);
        $this->_form->setDefault(constants::TEXTANSWER . $count . '_editor', ['text' => '', 'format' => FORMAT_MOODLE]);
        if ($required) {
            $this->_form->addRule(constants::TEXTANSWER . $count . '_editor', get_string('required'), 'required', null, 'client');
        }
    }

    /**
     * Convenience function: Adds a text area response
     *
     * @param int $name_or_count The name or count of the element to add
     * @param string $label, null means default
     * @param bool $required
     * @return void
     */
    final protected function add_textarearesponse($nameorcount, $label = null, $required = false, $fixedwidthfont = false) {
        if ($label === null) {
            $label = get_string('response', constants::M_COMPONENT);
        }

        // Set the form element name
        if (is_number($nameorcount) || empty($nameorcount)) {
            $element = constants::TEXTANSWER . $nameorcount;
        } else {
            $element = $nameorcount;
        }

        $attributes = ['rows' => '4', 'columns' => '140', 'style' => 'width: 600px;'];
        if ($fixedwidthfont) {
            $attributes['rows'] = '15';
            $attributes['style'] = 'width: 1000px;font-family: ui-monospace, \'Cascadia Code\', \'Source Code Pro\',' .
            'Menlo, Monaco, Consolas, \'DejaVu Sans Mono\', monospace; font-size: 14px; ' .
            'direction: ltr; unicode-bidi: plaintext;';
        }
        $this->_form->addElement('textarea', $element, $label, $attributes);
        if ($required) {
            $this->_form->addRule($element, get_string('required'), 'required', null, 'client');
        }
    }

    final protected function add_sentenceprompt($nameorcount, $label = null, $required = false) {
        if ($label === null) {
            $label = get_string('response', constants::M_COMPONENT);
        }

        // Set the form element name
        if (is_number($nameorcount) || empty($nameorcount)) {
            $element = constants::TEXTANSWER . $nameorcount;
        } else {
            $element = $nameorcount;
        }

        sentenceprompt::register();
        $this->_form->addElement(sentenceprompt::ELNAME, $element, $label, ['rows' => '4', 'columns' => '140', 'style' => 'width: 600px']);
        if ($required) {
            $this->_form->addRule($element, get_string('required'), 'required', null, 'client');
        }
    }

    final protected function add_sentenceimage($nameorcount, $label = null, $required = false) {
        if ($label === null) {
            $label = get_string('sentenceimage', constants::M_COMPONENT);
        }

        // Set the form element name
        if (is_number($nameorcount) || empty($nameorcount)) {
            $element = constants::FILEANSWER . $nameorcount;
        } else {
            $element = $nameorcount;
        }

        $filemanageroptions = $this->filemanageroptions;
        $filemanageroptions['accepted_types'] = 'image';
        $filemanageroptions['maxfiles'] = -1;
        $this->_form->addElement('filemanager', "{$element}_image", $label, [], $filemanageroptions);
        $this->_form->addHelpButton("{$element}_image", 'sentenceimage', constants::M_COMPONENT);
        if ($required) {
            $this->_form->addRule($element, get_string('required'), 'required', null, 'client');
        }
    }

    final protected function add_sentenceaudio($nameorcount, $label = null, $required = false, $maxfiles = -1) {
        if ($label === null) {
            $label = get_string('sentenceaudio', constants::M_COMPONENT);
        }

        // Set the form element name
        if (is_number($nameorcount) || empty($nameorcount)) {
            $element = constants::FILEANSWER . $nameorcount;
        } else {
            $element = $nameorcount;
        }

        $filemanageroptions = $this->filemanageroptions;
        $filemanageroptions['accepted_types'] = 'audio';
        $filemanageroptions['maxfiles'] = $maxfiles;
        $this->_form->addElement('filemanager', "{$element}_audio", $label, [], $filemanageroptions);
        $this->_form->addHelpButton("{$element}_audio", 'sentenceaudio', constants::M_COMPONENT);
        if ($required) {
            $this->_form->addRule($element, get_string('required'), 'required', null, 'client');
        }
    }

    /**
     * Convenience function: Adds a textbox
     *
     * @param int  $name_or_count The name or count of the element to add
     * @param string $label, null means default
     * @param bool $required
     * @return void
     */
    final protected function add_textboxresponse($nameorcount, $label = null, $required = false) {
        if ($label === null) {
            $label = get_string('response', constants::M_COMPONENT);
        }

        // Set the form element name
        if (is_number($nameorcount) || empty($nameorcount)) {
            $element = constants::TEXTANSWER . $nameorcount;
        } else {
            $element = $nameorcount;
        }

        $this->_form->addElement('text', $element, $label, ['size' => '60']);
        $this->_form->setType($element, PARAM_TEXT);
        if ($required) {
            $this->_form->addRule($element, get_string('required'), 'required', null, 'client');
        }
    }

    final protected function add_imageresponse_upload($nameorcount, $label = null, $required = false, $hideiffield = false, $hideifvalues = []) {
        global $CFG;

        if ($label === null) {
            $label = get_string('response', constants::M_COMPONENT);
        }

        // Set the form element name
        if (is_number($nameorcount) || empty($nameorcount)) {
            $element = constants::FILEANSWER . $nameorcount;
        } else {
            $element = $nameorcount;
        }

        $accept = 'image';
        $this->add_media_upload($element, $label, $required, $accept);

        if ($hideiffield !== false && !empty($hideifvalues)) {
            $m35 = $CFG->version >= 2018051700;
            if (!is_array($hideifvalues)) {
                $hideifvalues = [$hideifvalues];
            }
            foreach ($hideifvalues as $hideifvalue) {
                if ($m35) {
                    $this->_form->hideIf($element, $hideiffield, 'eq', $hideifvalue);
                } else {
                    $this->_form->disabledIf($element, $hideiffield, 'eq', $hideifvalue);
                }
            }
        }
    }

    /**
     * Convenience function: Adds a number only textbox
     *
     * @param int $name_or_count The name or count of the element to add
     * @param string $label, null means default
     * @param bool $required
     * @return void
     */
    final protected function add_numericboxresponse($nameorcount, $label = null, $required = false) {
        if ($label === null) {
            $label = get_string('response', constants::M_COMPONENT);
        }

        // Set the form element name
        if (is_number($nameorcount) || empty($nameorcount)) {
            $element = constants::CUSTOMINT . $nameorcount;
        } else {
            $element = $nameorcount;
        }

        $this->_form->addElement('text', $element, $label, ['size' => '8']);
        $this->_form->setType($element, PARAM_INT);
        $this->_form->setDefault($element, 0);
        $this->_form->addRule($element, get_string('numberonly', constants::M_COMPONENT), 'numeric', null, 'client');
        if ($required) {
            $this->_form->addRule($element, get_string('required'), 'required', null, 'client');
        }
    }

    /**
     * Convenience function: Adds layout hint. Width of a single answer
     *
     * @param string $label, null means default
     * @return void
     */
    final protected function add_correctanswer($label = null) {
        if ($label === null) {
            $label = get_string('correctanswer', constants::M_COMPONENT);
        }
        $options = [];
        $options['1'] = 1;
        $options['2'] = 2;
        $options['3'] = 3;
        $options['4'] = 4;
        $this->_form->addElement('select', constants::CORRECTANSWER, $label, $options);
        $this->_form->setDefault(constants::CORRECTANSWER, 1);
        $this->_form->setType(constants::CORRECTANSWER, PARAM_INT);
    }

    /**
     * Convenience function: Adds a dropdown list of voices
     *
     * @param string $label, null means default
     * @return void
     */
    final protected function add_layoutoptions() {
        $layoutoptions = [
            constants::LAYOUT_AUTO => get_string('layoutauto', constants::M_COMPONENT),
            constants::LAYOUT_HORIZONTAL => get_string('layouthorizontal', constants::M_COMPONENT),
            constants::LAYOUT_VERTICAL => get_string('layoutvertical', constants::M_COMPONENT),
            constants::LAYOUT_MAGAZINE => get_string('layoutmagazine', constants::M_COMPONENT),
        ];
        $name = constants::LAYOUT;
        $layout = get_config(constants::M_COMPONENT, $name);
        $defaultlayout = !empty($layout) ? $layout : constants::LAYOUT_VERTICAL;
        $this->add_dropdown($name, get_string('chooselayout', constants::M_COMPONENT), $layoutoptions, $defaultlayout);
    }

    /**
     * Convenience function: Adds a dropdown list of voices
     *
     * @param string $label, null means default
     * @return void
     */
    final protected function add_zoomandpanoptions() {
        $options = [
            constants::ZOOMANDPAN_NONE => get_string('zoomandpannone', constants::M_COMPONENT),
            constants::ZOOMANDPAN_LITE => get_string('zoomandpanlite', constants::M_COMPONENT),
            constants::ZOOMANDPAN_MEDIUM => get_string('zoomandpanmedium', constants::M_COMPONENT),
            constants::ZOOMANDPAN_MORE => get_string('zoomandpanmore', constants::M_COMPONENT),
        ];
        $name = constants::AUDIOSTORYZOOMANDPAN;
        $this->add_dropdown(
            $name,
            get_string('audiostoryzoomandpan', constants::M_COMPONENT),
            $options,
            constants::ZOOMANDPAN_LITE
        );
    }

    /**
     * Convenience function: Adds a dropdown list of voices
     *
     * @param string $label, null means default
     * @return void
     */
    final protected function add_voiceselect($name, $label = null, $hideiffield = false, $hideifvalues = []) {
        global $CFG;
        $showall = true;
        $allvoiceoptions = utils::get_tts_voices($this->moduleinstance->ttslanguage, $showall, $this->moduleinstance->region);
        $somevoiceoptions = utils::get_tts_voices($this->moduleinstance->ttslanguage, !$showall, $this->moduleinstance->region);
        $defaultvoice = array_pop($somevoiceoptions);
        $this->add_dropdown($name, $label, $allvoiceoptions, $defaultvoice);
        if ($hideiffield !== false && !empty($hideifvalues)) {
            $m35 = $CFG->version >= 2018051700;
            if (!is_array($hideifvalues)) {
                $hideifvalues = [$hideifvalues];
            }
            foreach ($hideifvalues as $hideifvalue) {
                if ($m35) {
                    $this->_form->hideIf($name, $hideiffield, 'eq', $hideifvalue);
                } else {
                    $this->_form->disabledIf($name, $hideiffield, 'eq', $hideifvalue);
                }
            }
        }
    }

    /**
     * Convenience function: Adds a dropdown list of voices
     *
     * @param string $label, null means default
     * @return void
     */
    final protected function add_ttsaudioselect($name, $label = null, $hideiffield = false, $hideifvalues = []) {
        global $CFG;

        ttsaudio::register();
        $this->_form->addElement(ttsaudio::ELNAME, $name, $label, [
            'region' => $this->moduleinstance->region,
            'langcode' => $this->moduleinstance->ttslanguage,
        ]);

        if ($hideiffield !== false && !empty($hideifvalues)) {
            $m35 = $CFG->version >= 2018051700;
            if (!is_array($hideifvalues)) {
                $hideifvalues = [$hideifvalues];
            }
            foreach ($hideifvalues as $hideifvalue) {
                if ($m35) {
                    $this->_form->hideIf($name, $hideiffield, 'eq', $hideifvalue);
                } else {
                    $this->_form->disabledIf($name, $hideiffield, 'eq', $hideifvalue);
                }
            }
        }
    }

    /**
     * Convenience function: Adds a dropdown list of voice options
     *
     * @param string $label, null means default
     * @return void
     */
    final protected function add_voiceoptions($name, $label = null, $hideiffield = false, $hideifvalues = [], $nossml = false, $nottsoption = false) {
        global $CFG;
        $voiceoptions = utils::get_tts_options($nossml, $nottsoption);
        $this->add_dropdown($name, $label, $voiceoptions);
        $m35 = $CFG->version >= 2018051700;
        if ($hideiffield !== false && !empty($hideifvalues)) {
            $m35 = $CFG->version >= 2018051700;
            if (!is_array($hideifvalues)) {
                $hideifvalues = [$hideifvalues];
            }
            foreach ($hideifvalues as $hideifvalue) {
                if ($m35) {
                    $this->_form->hideIf($name, $hideiffield, 'eq', $hideifvalue);
                } else {
                    $this->_form->disabledIf($name, $hideiffield, 'eq', $hideifvalue);
                }
            }
        }
    }

    final protected function add_relevanceoptions($name, $label, $default = false) {
        global $CFG;
        $relevanceoptions = utils::get_relevance_options();
        $this->add_dropdown($name, $label, $relevanceoptions, $default);
    }

    /**
     * Convenience function: Adds a yesno dropdown
     *
     * @param string $label, null means default
     * @return void
     */
    final protected function add_confirmchoice($name, $label = null) {
        global $CFG;
        if (empty($label)) {
            $label = get_string('confirmchoice_formlabel', constants::M_COMPONENT);
        }
        $this->_form->addElement('selectyesno', $name, $label);
        $this->_form->setDefault($name, 0);
    }

    /**
     * Convenience function: Adds a dropdown list of tts language
     *
     * @param string $label, null means default
     * @return void
     */
    final protected function add_languageselect($name, $label = null, $default = false) {
        $langoptions = utils::get_lang_options();
        $this->add_dropdown($name, $label, $langoptions, $default);
    }

    /**
     * A function that gets called upon init of this object by the calling script.
     *
     * This can be used to process an immediate action if required. Currently it
     * is only used in special cases by non-standard item types.
     *
     * @return bool
     */
    public function construction_override($itemid, $minilesson) {
        return true;
    }

    /**
     * Time limit element
     *
     * @param string $name
     * @param string $label
     * @param bool|int $default
     * @return void
     */
    final protected function add_timelimit($name, $label, $default = false) {
        $this->_form->addElement('duration', $name, $label, ['optional' => true, 'defaultunit' => 1]);
        if ($default !== false) {
            $this->_form->setDefault($name, $default);
        }
    }

    /**
     * Add checkbox element
     *
     * @param string $name
     * @param string $label
     * @param bool|int $default
     * @return void
     */
    final protected function add_checkbox($name, $label, $detailslabel = null, $default = 0) {
        $this->_form->addElement(
            'advcheckbox',
            $name,
            $label,
            $detailslabel,
            [],
            [0, 1]
        );
        if ($default !== 0) {
            $this->_form->setDefault($name, 1);
        }
    }

    /**
     * Allow retry element
     *
     * @param string $name
     * @param string $label
     * @param bool|int $default
     * @return void
     */
    final protected function add_allowretry($name, $detailslabel = null, $default = 0) {
        $this->_form->addElement(
            'advcheckbox',
            $name,
            get_string('allowretry', constants::M_COMPONENT),
            $detailslabel,
            [],
            [0, 1]
        );
        if ($default !== 0) {
            $this->_form->setDefault($name, 1);
        }
    }

    /**
     * Disable pasting in the textbox
     *
     * @param string $name
     * @param string $label
     * @param bool|int $default
     * @return void
     */
    final protected function add_nopasting($name, $detailslabel = null, $default = 1) {
        $this->_form->addElement(
            'advcheckbox',
            $name,
            get_string('nopasting', constants::M_COMPONENT),
            $detailslabel,
            [],
            [0, 1]
        );
        if ($default !== 0) {
            $this->_form->setDefault($name, 1);
        }
    }

    /**
     * Hide start page element.
     *
     * @param string $name
     * @param string $label
     * @param bool|int $default
     * @return void
     */
    final protected function add_hidestartpage($name, $detailslabel = null, $default = 0) {
        $this->_form->addElement(
            'advcheckbox',
            $name,
            get_string('hidestartpage', constants::M_COMPONENT),
            $detailslabel,
            [],
            [0, 1]
        );
        if ($default !== 0) {
            $this->_form->setDefault($name, 1);
        }
    }


    final protected function add_aliencount($name, $label, $default) {
        $alienoptions = [
            2 => 2,
            3 => 3,
            4 => 4,
            5 => 5,
            6 => 6,
            7 => 7,
            8 => 8,
        ];
        $this->add_dropdown($name, $label, $alienoptions, $default);
    }

    final public static function get_itemname($subpluginname = constants::SUBPLUGINTYPES['item']) {
        return ltrim(str_replace($subpluginname, '', static::get_component()), '_');
    }

    final public static function get_component() {
        return utils::get_component(static::class);
    }

    public function __get($name) {
        if ($name == 'type') {
            return static::get_itemname();
        }
        throw new \Exception('Not implemented');
    }
}
