<?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/>.

/**
 * The main jitsi configuration form
 *
 * It uses the standard core Moodle formslib. For more info about them, please
 * visit: http://docs.moodle.org/en/Development:lib/formslib.php
 *
 * @package    mod_jitsi
 * @copyright  2019 Sergio Comerón Sánchez-Paniagua <sergiocomeron@icloud.com>
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

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

require_once($CFG->dirroot . '/course/moodleform_mod.php');

/**
 * Jitsi settings form.
 *
 * @package   mod_jitsi
 * @copyright  2019 Sergio Comerón Sánchez-Paniagua <sergiocomeron@icloud.com>
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
class mod_jitsi_mod_form extends moodleform_mod {
    /**
     * Defines forms elements
     */
    public function definition() {
        global $CFG, $PAGE;
        $mform = $this->_form;
        $mform->addElement('header', 'general', get_string('general', 'form'));
        $mform->addElement('text', 'name', get_string('jitsiname', 'jitsi'), ['size' => '64']);
        if (!empty($CFG->formatstringstriptags)) {
            $mform->setType('name', PARAM_TEXT);
        } else {
            $mform->setType('name', PARAM_CLEANHTML);
        }
        $mform->addRule('name', null, 'required', null, 'client');
        $mform->addRule('name', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
        $mform->addHelpButton('name', 'modulename', 'jitsi');

        $mform->addElement('advcheckbox', 'sessionwithtoken', get_string('sharedsessionwithtoken', 'jitsi'));
        $mform->setDefault('sessionwithtoken', 0);

        if (!isset($this->current->tokeninterno)) {
            $tokeninterno = bin2hex(random_bytes(32));
        } else {
            $tokeninterno = $this->current->tokeninterno;
        }
        $mform->addElement('hidden', 'tokeninterno', $tokeninterno);
        $mform->setDefault('tokeninterno', $tokeninterno);
        $mform->setType('tokeninterno', PARAM_TEXT);

        // Data carrier read by session_picker.js to exclude the current session.
        $mform->addElement(
            'html',
            '<div id="jitsi-session-picker-data" data-exclude-token="' .
            s($tokeninterno) . '" style="display:none"></div>'
        );

        // Show the token display with copy button only when editing an existing activity.
        if (isset($this->current->id) && $this->current->id > 0) {
            $copyjs = 'navigator.clipboard.writeText(this.dataset.token).then(()=>{'
                . 'var i=this.querySelector("i");'
                . 'i.className="fa fa-check fa-lg text-success";'
                . 'setTimeout(()=>{i.className="fa fa-copy fa-lg";},2000)'
                . '})';
            $tokendisplay = '<div class="form-group row fitem">'
                . '<div class="col-md-3 col-form-label d-flex pb-0 pr-md-0">'
                . get_string('yoursessiontoken', 'jitsi')
                . '</div>'
                . '<div class="col-md-9 form-inline align-items-start felement">'
                . '<code class="mr-2">' . s($tokeninterno) . '</code>'
                . '<button type="button" class="btn btn-link p-0 ml-2" '
                . 'title="' . s(get_string('copytoken', 'jitsi')) . '" '
                . 'data-token="' . s($tokeninterno) . '" '
                . 'onclick="' . s($copyjs) . '">'
                . '<i class="fa fa-copy fa-lg" aria-hidden="true"></i>'
                . '</button>'
                . '</div></div>';
            $mform->addElement('html', $tokendisplay);
        }

        $autocompleteopts = [
            'ajax'              => 'mod_jitsi/session_picker',
            'multiple'          => false,
            'noselectionstring' => get_string('searchsession', 'jitsi'),
            'showsuggestions'   => true,
        ];
        $mform->addElement(
            'autocomplete',
            'tokeninvitacion',
            get_string('tokeninvitacion', 'jitsi'),
            [],
            $autocompleteopts
        );
        $mform->hideIf('tokeninvitacion', 'sessionwithtoken', 'notchecked');
        $mform->addHelpButton('tokeninvitacion', 'tokeninvitacion', 'jitsi');
        $mform->setType('tokeninvitacion', PARAM_TEXT);

        // Preserve the existing token so get_data() can restore it if the user
        // saves without making a new selection in the autocomplete.
        $existingtoken = $this->current->tokeninvitacion ?? '';
        $mform->addElement('hidden', 'tokeninvitacion_original', $existingtoken);
        $mform->setType('tokeninvitacion_original', PARAM_TEXT);

        $this->standard_intro_elements();

        $mform->addElement('header', 'availability', get_string('availability', 'assign'));
        $name = get_string('allow', 'jitsi');
        $options = ['optional' => true];

        $mform->addElement('date_time_selector', 'timeopen', $name, $options);
        $mform->disabledIf('timeopen', 'sessionwithtoken', 'checked');
        $mform->disabledIf('timeclose', 'sessionwithtoken', 'checked');

        $name = get_string('close', 'jitsi');
        $options = ['optional' => true];
        $mform->addElement('date_time_selector', 'timeclose', $name, $options);

        $choicesminspre = [
            5 => 5,
            10 => 10,
            15 => 15,
            20 => 20,
            30 => 30,
        ];
        $mform->addElement('select', 'minpretime', get_string('minpretime', 'jitsi'), $choicesminspre);
        $mform->disabledIf('minpretime', 'timeopen[enabled]');
        $mform->addHelpButton('minpretime', 'minpretime', 'jitsi');

        if (get_config('mod_jitsi', 'invitebuttons') == 1) {
            $optionsinvitation = ['defaulttime' => time() + 86400, 'optional' => true];
            $mform->addElement('header', 'invitations', get_string('externalinvitations', 'jitsi'));
            $mform->addElement('static', 'linkexplication', '', get_string('staticinvitationlinkcapabilityex', 'jitsi'));
            $mform->addElement(
                'date_time_selector',
                'validitytime',
                get_string('finishinvitation', 'jitsi'),
                $optionsinvitation,
            );
            if (!has_capability('mod/jitsi:createlink', $PAGE->context)) {
                $mform->hardFreeze('validitytime');
            }

            if ($mform->getElementValue('validitytime') < time()) {
                $mform->addElement('static', 'linkexpired', '', 'linkExpired2');
            }
            if (!isset($this->current->token)) {
                $token = bin2hex(random_bytes(32));
            } else {
                $token = $this->current->token;
            }
            $mform->addElement('hidden', 'token', $token);
            $mform->setDefault('token', $token);

            $urlinvitacion = $CFG->wwwroot . '/mod/jitsi/formuniversal.php?t=' . $token;
            $copyurljs = 'navigator.clipboard.writeText(this.dataset.url).then(()=>{'
                . 'var i=this.querySelector("i");'
                . 'i.className="fa fa-check fa-lg text-success";'
                . 'setTimeout(()=>{i.className="fa fa-copy fa-lg";},2000)'
                . '})';
            $copyurltitle = s(get_string('copyurl', 'jitsi'));
            $urldisplay = '<div class="form-group row fitem">'
                . '<div class="col-md-3 col-form-label d-flex pb-0 pr-md-0">'
                . get_string('urlinvitacion', 'jitsi')
                . '</div>'
                . '<div class="col-md-9 form-inline align-items-start felement">'
                . '<code class="mr-2" style="word-break:break-all">' . s($urlinvitacion) . '</code>'
                . '<button type="button" class="btn btn-link p-0 ml-2" '
                . 'title="' . $copyurltitle . '" '
                . 'data-url="' . s($urlinvitacion) . '" '
                . 'onclick="' . s($copyurljs) . '">'
                . '<i class="fa fa-copy fa-lg" aria-hidden="true"></i>'
                . '</button>'
                . '</div></div>';
            $mform->addElement('html', $urldisplay);
            if (get_config('mod_jitsi', 'sharestream')) {
                $urlinvitacionrecord = $CFG->wwwroot . '/mod/jitsi/recordun.php?t=' . $token;
                $urldisplayrecord = '<div class="form-group row fitem">'
                    . '<div class="col-md-3 col-form-label d-flex pb-0 pr-md-0">'
                    . get_string('urlinvitacionrecord', 'jitsi')
                    . '</div>'
                    . '<div class="col-md-9 form-inline align-items-start felement">'
                    . '<code class="mr-2" style="word-break:break-all">' . s($urlinvitacionrecord) . '</code>'
                    . '<button type="button" class="btn btn-link p-0 ml-2" '
                    . 'title="' . $copyurltitle . '" '
                    . 'data-url="' . s($urlinvitacionrecord) . '" '
                    . 'onclick="' . s($copyurljs) . '">'
                    . '<i class="fa fa-copy fa-lg" aria-hidden="true"></i>'
                    . '</button>'
                    . '</div></div>';
                $mform->addElement('html', $urldisplayrecord);
            }
            $mform->setType('token', PARAM_TEXT);
        } else {
            if (!isset($this->current->token)) {
                $token = bin2hex(random_bytes(32));
            } else {
                $token = $this->current->token;
            }
            $mform->addElement('hidden', 'token', $token);
            $mform->setDefault('token', $token);
            $mform->setType('token', PARAM_TEXT);
        }

        $this->standard_coursemodule_elements();
        $this->add_action_buttons();
    }

    /**
     * Add elements for setting the custom completion rules.
     *
     * @category completion
     * @return array List of added element names, or names of wrapping group elements.
     */
    public function add_completion_rules() {
        global $CFG;
        if ($CFG->branch < 403) {
            $suffix = '';
        } else {
            $suffix = $this->get_suffix();
        }

        $mform =&  $this->_form;

        $group = [
            $mform->createElement(
                'checkbox',
                'completionminutesenabled' . $suffix,
                ' ',
                get_string('completionminutesex', 'jitsi')
            ),
            $mform->createElement('text', 'completionminutes' . $suffix, ' ', ['size' => 3]),
        ];
        $mform->setType('completionminutes' . $suffix, PARAM_INT);
        $mform->addGroup($group, 'completionminutesgroup' . $suffix, get_string('completionminutes', 'jitsi'), [' '], false);
        $mform->addHelpButton('completionminutesgroup' . $suffix, 'completionminutes', 'jitsi');
        $mform->disabledIf('completionminutes' . $suffix, 'completionminutesenabled', 'notchecked');

        return ['completionminutesgroup' . $suffix];
    }

    /**
     * Called during validation to see whether some module-specific completion rules are selected.
     *
     * @param array $data Input data not yet validated.
     * @return bool True if one or more rules is enabled, false if none are.
     */
    public function completion_rule_enabled($data) {
        global $CFG;
        if ($CFG->branch < 403) {
            $suffix = '';
        } else {
            $suffix = $this->get_suffix();
        }

        return (!empty($data['completionminutesenabled' . $suffix]) && $data['completionminutes' . $suffix] != 0);
    }

    /**
     * Get Data
     */
    public function get_data() {
        global $DB;
        $data = parent::get_data();
        if (!$data) {
            return $data;
        }
        if (!empty($data->completionunlocked)) {
            $autocompletion = !empty($data->completion) && $data->completion == COMPLETION_TRACKING_AUTOMATIC;
            if (empty($data->completionminutesenabled) || !$autocompletion) {
                $data->completionminutes = 0;
            }
        }

        // Autocomplete returns an array even when multiple=false; normalize to string.
        if (is_array($data->tokeninvitacion)) {
            $data->tokeninvitacion = reset($data->tokeninvitacion) ?: '';
        }

        // If nothing was selected in the autocomplete, preserve the original token.
        if (empty($data->tokeninvitacion) && !empty($data->tokeninvitacion_original)) {
            $data->tokeninvitacion = $data->tokeninvitacion_original;
        }

        if (!empty($data->tokeninvitacion)) {
            $sql = "SELECT * FROM {jitsi} WHERE " . $DB->sql_compare_text('tokeninterno') . " = " .
                $DB->sql_compare_text(':tokeninvitacion');
            $params = ['tokeninvitacion' => $data->tokeninvitacion];
            $principal = $DB->get_record_sql($sql, $params);
            if ($principal) {
                $data->timeopen = $principal->timeopen;
                $data->timeclose = $principal->timeclose;
            }
        }

        $sql = "SELECT * FROM {jitsi} WHERE " . $DB->sql_compare_text('tokeninvitacion') . " = :tokeninterno";
        $params = ['tokeninterno' => $data->tokeninterno];
        $secundarias = $DB->get_records_sql($sql, $params);
        foreach ($secundarias as $secundaria) {
            $secundaria->timeopen = $data->timeopen;
            $secundaria->timeclose = $data->timeclose;
            $DB->update_record('jitsi', $secundaria);
            rebuild_course_cache($secundaria->course, true);
        }

        return $data;
    }

    /**
     * Data validation
     *
     * @param array $data Input data to validated.
     * @param array $files Files uploaded.
     * @return String error message, if any.
     */
    public function validation($data, $files) {
        global $DB;
        $errors = parent::validation($data, $files);

        // Check open and close times are consistent.
        if (
            $data['timeopen'] != 0 && $data['timeclose'] != 0 &&
            $data['timeclose'] < $data['timeopen']
        ) {
            $errors['timeclose'] = get_string('closebeforeopen', 'jitsi');
        }

        // Check validitity time is consistent with open and close times.
        if (
            isset($data['validitytime']) &&
            $data['validitytime'] != 0
        ) {
            if (
                ($data['timeopen'] != 0 && $data['validitytime'] < $data['timeopen']) ||
                ($data['timeclose'] != 0 && $data['validitytime'] > $data['timeclose'])
            ) {
                $errors['validitytime'] = get_string('validitytimevalidation', 'jitsi');
            }
        }
        if (isset($data['validitytime']) && $data['validitytime'] <= time() && $data['validitytime'] != 0) {
            $errors['validitytime'] = get_string('tokeninvitationnotvalid', 'jitsi');
        }
        if ($data['sessionwithtoken'] == 1) {
            // Autocomplete returns an array even when multiple=false; normalize to string.
            $tokeninvitacion = is_array($data['tokeninvitacion'])
                ? (reset($data['tokeninvitacion']) ?: '')
                : $data['tokeninvitacion'];
            if (empty($tokeninvitacion)) {
                $errors['tokeninvitacion'] = get_string('tokeninvitationvalidation', 'jitsi');
            } else {
                $sql = "SELECT * FROM {jitsi} WHERE " . $DB->sql_compare_text('tokeninterno') .
                    " = " . $DB->sql_compare_text(':tok');
                if ($DB->get_record_sql($sql, ['tok' => $tokeninvitacion]) == null) {
                    $errors['tokeninvitacion'] = get_string('tokeninvitationvalidation', 'jitsi');
                }
            }
        }

        return $errors;
    }

    /**
     * Processing data
     * @param array $defaultvalues - default values
     */
    public function data_preprocessing(&$defaultvalues) {
        $defaultvalues['completionminutesenabled'] =
            !empty($defaultvalues['completionminutes']) ? 1 : 0;
        if (empty($defaultvalues['completionminutes'])) {
            $defaultvalues['completionminutes'] = 1;
        }
        // Always start the session picker empty — the raw token is not useful to display.
        $defaultvalues['tokeninvitacion'] = '';
    }
}
