<?php
// This file is part of mod_datalynx for Moodle - http://moodle.org/
//
// It 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.
//
// It 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/>.

/**
 *
 * @package datalynxfield_datalynxview
 * @subpackage datadformview
 * @copyright 2014 onwards by edulabs.org and associated programmers
 * @copyright based on the work by 2013 Itamar Tzadok
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
namespace datalynxfield_datalynxview;

use mod_datalynx\local\field\datalynxfield_renderer;
use MoodleQuickForm;
use stdClass;

/**
 * Renderer for the datalynxview field type.
 *
 * @package    datalynxfield_datalynxview
 * @copyright  2014 onwards by edulabs.org and associated programmers
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
class renderer extends datalynxfield_renderer {
    /**
     * Render this field in display mode.
     *
     * Called by the replacement function of datalynxfield_renderer.
     *
     * @param stdClass $entry The entry object which includes this field instance.
     * @param array $options The display options, e.g. empty for default.
     * @return string The rendered HTML output.
     */
    public function render_display_mode(stdClass $entry, array $options): string {

        // We don't export dlview to csv.
        if (optional_param('exportcsv', '', PARAM_ALPHA)) {
            return '';
        }

        $type = "";
        return $this->display_browse($entry, $type);
    }

    /**
     * Check, which type of display, and call the right display-function
     * stdClass @entry  the entry object which holds this field
     * string @type     the type of display
     *
     * @param stdClass $entry The entry object which holds this field.
     * @param ?string $type The type of display.
     * @return string
     */
    protected function display_browse($entry, $type = null) {
        $field = $this->field;

        if (empty($field->refdatalynx) || empty($field->refview)) {
            return '';
        }

        // Inline.
        if (empty($type)) {
            // MDL-0000 Including controls here seems to mess up the hosting view controls.
            $voptions = ['controls' => false];
            return $this->get_view_display_content($entry, $voptions);
        }

        return '';
    }

    /**
     * The default display-method, just inline html
     * stdClass @entry          the entry object this field belongs to
     * array string @options    array of options for the display-method of the view
     *
     * @param stdClass $entry The entry object this field belongs to.
     * @param array $options Array of options for the display-method of the view.
     * @return                 the display-method of the view
     */
    protected function get_view_display_content($entry, array $options = []) {
        $field = $this->field;

        $refdatalynx = $field->refdatalynx;
        $refview = $refdatalynx->get_view_from_id($field->refview);

        // Options for setting the filter.
        $foptions = [];

        // Search filter by entry author or group.
        $foptions = $this->get_filter_by_options($foptions, $entry);

        if (!isset($foptions['eids']) && !isset($foptions['users'])) {
            return "";
        }

        $refview->set_filter($foptions, true);

        // Set the ref datalynx.
        $params = ['js' => true, 'css' => true, 'modjs' => true, 'completion' => true,
                'comments' => true];

        // Ref datalynx page type defaults to external.
        $refdatalynx->set_page('external', $params, true);

        $refview->set_content(['filter' => $refview->get_filter()]);
        // Set to return html.
        $options['tohtml'] = true;
        $options['fieldview'] = true;
        $options['entryactions'] = false;
        return $refview->display($options);
    }

    /**
     * Get the filter options for this field instance
     * and add it to the options array
     * Should the entries be filtered:
     * - by the author of this entry object
     * - by the group to which this entry object belongs to
     * - by a value, which is stored in datalynx_contents for this field(fieldid) and this entry(entryid)
     *
     * @param array $options Current filter options array.
     * @param stdClass $entry The entry object used to derive filter values.
     * @param bool $urlquery Whether the filter is for a URL query string.
     * @return array Updated filter options array.
     */
    protected function get_filter_by_options(array $options, $entry, $urlquery = false) {
        global $DB;

        $field = $this->field;

        if (!empty($field->field->param6)) { // Param6: author,group.
            [$filterauthor, $filtergroup] = explode(',', $field->field->param6);
            // Entry author.
            if ($filterauthor) {
                if ($entry->id != -1) {
                    $users = $urlquery ? $entry->userid : [$entry->userid];
                    $options['users'] = $users;
                } else {
                    global $USER;
                    $users = $urlquery ? $USER->id : [$USER->id];
                    $options['users'] = $users;
                }
            }
            // Entry group.
            if ($filtergroup) {
                if ($entry->id != -1) {
                    $groups = $urlquery ? $entry->groupid : [$entry->groupid];
                    $options['groups'] = $groups;
                } else {
                    $allgroups = groups_get_user_groups($this->dl->course);
                    $groups = $urlquery ? implode(',', $allgroups[0]) : $allgroups[0];
                    $options['groups'] = $groups;
                }
            }
        }

        // With the ID of this datalynxview_field and the ID of the parent-entry we retrieve.
        // The content to search for.
        // Then we get the entry-IDs of the entries (eids) which match this content-value.
        // End add them to the options array.
        if ($fieldid = $field->field->param7) {  // Field-ID of the external field.
            if (
                $contents = $DB->get_fieldset_select(
                    'datalynx_contents',
                    'content',
                    'entryid = :entryid and fieldid = :fieldid',
                    ['entryid' => $entry->id, 'fieldid' => $field->field->id]
                )
            ) {
                $contentsarr = explode(",", $contents[0]);
                [$insql, $params] = $DB->get_in_or_equal($contentsarr, SQL_PARAMS_NAMED);
                $params['fieldid'] = $fieldid;
                $sql = 'SELECT  c.entryid
                        FROM    {datalynx_contents} c
                        WHERE   c.fieldid = :fieldid
                        AND     c.content ' . $insql;
                if ($eids = $DB->get_fieldset_sql($sql, $params)) {
                    $options['eids'] = implode(",", $eids);
                }
            }
        }

        return $options;
    }

    /**
     * Build sort options from the field's custom sort configuration.
     *
     * @return array Array of sort options keyed by ref-field id.
     */
    protected function get_sort_options() {
        $field = $this->field;

        $refview = $field->df->get_view_from_id($field->refview);

        $soptions = [];
        // Custom sort (ref-field-patten,ASC/DESC).
        if (!empty($field->field->param4)) {
            foreach (explode("\n", $field->field->param4) as $sorty) {
                [$pattern, $dir] = explode(',', $sorty);
                // Get the field id from pattern.
                if (!$rfieldid = $refview->get_pattern_fieldid($pattern)) {
                    continue;
                }
                // Convert direction to 0/1.
                $dir = $dir == 'DESC' ? 1 : 0;
                $soptions[$rfieldid] = $dir;
            }
        }
        return $soptions;
    }

    /**
     * Get search options based on current configuration.
     *
     * @param stdClass $entry
     * @return array
     */
    protected function get_search_options($entry) {
        $field = $this->field;
        $soptions = [];

        // Custom search (AND/OR,ref-field-patten,[NOT],OPT,local-field-pattern/value.
        if (empty($field->field->param5)) {
            return $soptions;
        }

        if (
            empty($field->refdatalynx) || !$refview = $field->refview || !$localview = $field->localview
        ) {
            return $soptions;
        }

        foreach (explode("\n", $field->field->param5) as $searchy) {
            [$andor, $refpattern, $not, $operator, $localpattern] = explode(',', $searchy);
            // Validate the and/or combinator value.
            if (empty($andor) || !in_array($andor, ['AND', 'OR'])) {
                continue;
            }
            // Get the ref field id from pattern.
            $refviewview = $field->df->get_view_from_id($refview);
            if (!$rfieldid = $refviewview->get_pattern_fieldid($refpattern)) {
                continue;
            }
            // Get value for local pattern or use as value.
            $value = '';
            $localviewview = $field->df->get_view_from_id($localview);
            if (!$localfieldid = $localviewview->get_pattern_fieldid($localpattern)) {
                $value = $localpattern;
            } else {
                if ($localfield = $field->df->get_field_from_id($localfieldid)) {
                    // Get the array of values for the patterns.
                    if ($replacements = $localfield->renderer()->replacements([$localpattern], $entry)) {
                        // Take the first: array('html', value).
                        $first = reset($replacements);
                        // Extract the value part.
                        $value = $first[1];
                        // Make sure this is the search value.
                        // Select fields search by key, not value.
                        $value = $localfield->get_search_value($value);
                    }
                }
            }

            // Add to the search options.
            if (empty($soptions[$rfieldid])) {
                $soptions[$rfieldid] = ['AND' => [], 'OR' => []];
            }
            $soptions[$rfieldid][$andor][] = [$not, $operator, $value];
        }

        return $soptions;
    }

    /**
     *
     * {@inheritDoc}
     * @see datalynxfield_renderer::patterns()
     */
    protected function patterns() {
        $fieldname = $this->field->name();

        $patterns = parent::patterns();
        $patterns["[[$fieldname]]"] = [true];

        return $patterns;
    }

    /**
     *
     * {@inheritdoc}
     *
     * @param MoodleQuickForm $mform The form object.
     * @param stdClass $entry The entry being edited.
     * @param ?array $options Rendering options.
     * @see datalynxfield_renderer::render_edit_mode()
     */
    public function render_edit_mode(MoodleQuickForm &$mform, stdClass $entry, ?array $options = null) {

        // Variable $field datalynxfield_datalynxview.
        $field = $this->field;
        // Do not show in edit mode, when nothing can be selected.
        if ($field->refdatalynx !== null && !empty($field->field->param7)) {
            $fieldid = $field->id();
            $entryid = $entry->id;
            $fieldname = "field_{$fieldid}_$entryid";
            $classname = "datalynxview_{$fieldid}_{$entryid}";
            $required = !empty($options['required']);

            $selected = !empty($entry->{"c{$fieldid}_content"}) ? $entry->{"c{$fieldid}_content"} : [];
            // A hidden field is added to autocomplete fields by parent Quickform element.
            // The value of the hidden field must be added as option in order to process an empty autocomplete field.
            $menu = ["_qf__force_multiselect_submission" => "..."];
            $menu = array_merge($menu, $field->refdatalynx->get_distinct_textfieldvalues_by_id($field->field->param7));

            $mform->addElement('autocomplete', $fieldname, null, $menu, [
                    "class" => "datalynxfield_datalynxview $classname",
                    "multiple" => "true",
            ]);
            $mform->setType($fieldname, PARAM_NOTAGS);
            $mform->setDefault($fieldname, $selected);
            if ($required) {
                $mform->addRule($fieldname, null, 'required');
            }
        }
    }

    /**
     *
     * {@inheritDoc}
     * @param int $entryid The entry ID being validated.
     * @param array $tags The pattern tags for this field.
     * @param stdClass $formdata The submitted form data.
     * @return array Array of validation errors keyed by field name.
     * @see datalynxfield_renderer::validate()
     */
    public function validate($entryid, $tags, $formdata) {
        $fieldid = $this->field->id();

        $formfieldname = "field_{$fieldid}_{$entryid}";

        $errors = [];
        foreach ($tags as $tag) {
            [, $behavior, ] = $this->process_tag($tag);
            // Variable $behavior datalynx_field_behavior.
            if ($behavior->is_required() && (!isset($formdata->$formfieldname))) {
                $errors[$formfieldname] = get_string('fieldrequired', 'datalynx');
            }
        }

        return $errors;
    }
}
