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

/**
 * Forum summary report filters renderable.
 *
 * @package    forumreport_summary
 * @copyright  2019 Michael Hawkins <michaelh@moodle.com>
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

namespace forumreport_summary\output;

use context_course;
use moodle_url;
use renderable;
use renderer_base;
use stdClass;
use templatable;
use forumreport_summary;

/**
 * Forum summary report filters renderable.
 *
 * @copyright  2019 Michael Hawkins <michaelh@moodle.com>
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
class filters implements renderable, templatable {

    /**
     * Course modules the report relates to.
     * Array of stdClass objects
     *
     * @var array $cms
     */
    protected $cms;

    /**
     * Course ID where the report is being generated.
     *
     * @var int $courseid
     */
    protected $courseid;

    /**
     * Moodle URL used as the form action on the generate button.
     *
     * @var moodle_url $actionurl
     */
    protected $actionurl;

    /**
     * Details of groups available for filtering.
     * Stored in the format groupid => groupname.
     *
     * @var array $groupsavailable
     */
    protected $groupsavailable = [];

    /**
     * IDs of groups selected for filtering.
     *
     * @var array $groupsselected
     */
    protected $groupsselected = [];

    /**
     * IDs of discussions required for export links.
     * If a subset of groups available are selected, this will include the discussion IDs
     * within that group in the forum.
     * If all groups are selected, or no groups mode is enabled, this will be empty as
     * no discussion filtering is required in the export.
     *
     * @var array $discussionids
     */
    protected $discussionids = [];

    /**
     * HTML for dates filter.
     *
     * @var array $datesdata
     */
    protected $datesdata = [];

    /**
     * Text to display on the dates filter button.
     *
     * @var string $datesbuttontext
     */
    protected $datesbuttontext;

    /**
     * Builds renderable filter data.
     *
     * @param stdClass $course The course object.
     * @param array $cms Array of course module objects.
     * @param moodle_url $actionurl The form action URL.
     * @param array $filterdata (optional) Associative array of data that has been set on available filters, if any,
     *                                     in the format filtertype => [values]
     */
    public function __construct(stdClass $course, array $cms, moodle_url $actionurl, array $filterdata = []) {
        $this->cms = $cms;
        $this->courseid = $course->id;
        $this->actionurl = $actionurl;

        // Prepare groups filter data.
        $groupsdata = $filterdata['groups'] ?? [];
        $this->prepare_groups_data($groupsdata);

        // Prepare dates filter data.
        $datefromdata = $filterdata['datefrom'] ?? [];
        $datetodata = $filterdata['dateto'] ?? [];
        $this->prepare_dates_data($datefromdata, $datetodata);
    }

    /**
     * Prepares groups data and sets relevant property values.
     *
     * @param array $groupsdata Groups selected for filtering.
     * @return void.
     */
    protected function prepare_groups_data(array $groupsdata): void {
        global $DB, $USER;

        $groupsavailable = [];
        $allowedgroupsobj = [];

        $usergroups = groups_get_all_groups($this->courseid, $USER->id);
        $coursegroups = groups_get_all_groups($this->courseid);
        $forumids = [];
        $allgroups = false;
        $hasgroups = false;

        // Check if any forum gives the user access to all groups and no groups.
        foreach ($this->cms as $cm) {
            $forumids[] = $cm->instance;

            // Only need to check for all groups access if not confirmed by a previous check.
            if (!$allgroups) {
                $groupmode = groups_get_activity_groupmode($cm);

                // If no groups mode enabled on the forum, nothing to prepare.
                if (!in_array($groupmode, [VISIBLEGROUPS, SEPARATEGROUPS])) {
                    continue;
                }

                $hasgroups = true;

                // Fetch for the current cm's forum.
                $context = \context_module::instance($cm->id);
                $aag = has_capability('moodle/site:accessallgroups', $context);

                if ($groupmode == VISIBLEGROUPS || $aag) {
                    $allgroups = true;
                }
            }
        }

        // If no groups mode enabled, nothing to prepare.
        if (!$hasgroups) {
            return;
        }

        // Any groups, and no groups.
        if ($allgroups) {
            $nogroups = new stdClass();
            $nogroups->id = -1;
            $nogroups->name = get_string('groupsnone');

            $allowedgroupsobj = $coursegroups + [$nogroups];
        } else {
            $allowedgroupsobj = $usergroups;
        }

        $contextcourse = context_course::instance($this->courseid);
        foreach ($allowedgroupsobj as $group) {
            $groupsavailable[$group->id] = format_string($group->name, true, ['context' => $contextcourse]);
        }

        // Set valid groups selected.
        $groupsselected = array_intersect($groupsdata, array_keys($groupsavailable));

        // Overwrite groups properties.
        $this->groupsavailable = $groupsavailable;
        $this->groupsselected = $groupsselected;

        $groupsselectedcount = count($groupsselected);
        if ($groupsselectedcount > 0 && $groupsselectedcount < count($groupsavailable)) {
            list($forumidin, $forumidparams) = $DB->get_in_or_equal($forumids, SQL_PARAMS_NAMED);
            list($groupidin, $groupidparams) = $DB->get_in_or_equal($groupsselected, SQL_PARAMS_NAMED);

            $discussionswhere = "course = :courseid AND forum {$forumidin} AND groupid {$groupidin}";
            $discussionsparams = ['courseid' => $this->courseid];
            $discussionsparams += $forumidparams + $groupidparams;

            $discussionids = $DB->get_fieldset_select('forum_discussions', 'DISTINCT id', $discussionswhere, $discussionsparams);

            foreach ($discussionids as $discussionid) {
                $this->discussionids[] = ['discid' => $discussionid];
            }
        }
    }

    /**
     * Prepares from date, to date and button text.
     * Empty data will default to a disabled filter with today's date.
     *
     * @param array $datefromdata From date selected for filtering, and whether the filter is enabled.
     * @param array $datetodata To date selected for filtering, and whether the filter is enabled.
     * @return void.
     */
    private function prepare_dates_data(array $datefromdata, array $datetodata): void {
        $timezone = \core_date::get_user_timezone_object();
        $calendartype = \core_calendar\type_factory::get_calendar_instance();
        $timestamptoday = time();
        $datetoday  = $calendartype->timestamp_to_date_array($timestamptoday, $timezone);

        // Prepare date/enabled data.
        if (empty($datefromdata['enabled'])) {
            $fromdate = $datetoday;
            $fromtimestamp = $timestamptoday;
            $fromenabled = false;
        } else {
            $fromdate = $calendartype->timestamp_to_date_array($datefromdata['timestamp'], $timezone);
            $fromtimestamp = $datefromdata['timestamp'];
            $fromenabled = true;
        }

        if (empty($datetodata['enabled'])) {
            $todate = $datetoday;
            $totimestamp = $timestamptoday;
            $toenabled = false;
        } else {
            $todate = $calendartype->timestamp_to_date_array($datetodata['timestamp'], $timezone);
            $totimestamp = $datetodata['timestamp'];
            $toenabled = true;
        }

        $this->datesdata = [
            'from' => [
                'day'       => $fromdate['mday'],
                'month'     => $fromdate['mon'],
                'year'      => $fromdate['year'],
                'timestamp' => $fromtimestamp,
                'enabled'   => $fromenabled,
            ],
            'to' => [
                'day'       => $todate['mday'],
                'month'     => $todate['mon'],
                'year'      => $todate['year'],
                'timestamp' => $totimestamp,
                'enabled'   => $toenabled,
            ],
        ];

        // Prepare button string data.
        $displayformat = get_string('strftimedatemonthabbr', 'langconfig');
        $fromdatestring = $calendartype->timestamp_to_date_string($fromtimestamp, $displayformat, $timezone, true, true);
        $todatestring = $calendartype->timestamp_to_date_string($totimestamp, $displayformat, $timezone, true, true);

        if ($fromenabled && $toenabled) {
            $datestrings = [
                'datefrom' => $fromdatestring,
                'dateto'   => $todatestring,
            ];
            $this->datesbuttontext = get_string('filter:datesfromto', 'forumreport_summary', $datestrings);
        } else if ($fromenabled) {
            $this->datesbuttontext = get_string('filter:datesfrom', 'forumreport_summary', $fromdatestring);
        } else if ($toenabled) {
            $this->datesbuttontext = get_string('filter:datesto', 'forumreport_summary', $todatestring);
        } else {
            $this->datesbuttontext = get_string('filter:datesname', 'forumreport_summary');
        }
    }

    /**
     * Export data for use as the context of a mustache template.
     *
     * @param renderer_base $renderer The renderer to be used to display report filters.
     * @return array Data in a format compatible with a mustache template.
     */
    public function export_for_template(renderer_base $renderer): stdClass {
        $output = new stdClass();

        // Set formaction URL.
        $output->actionurl = $this->actionurl->out(false);

        // Set groups filter data.
        if (!empty($this->groupsavailable)) {
            $output->hasgroups = true;

            $groupscount = count($this->groupsselected);

            if (count($this->groupsavailable) <= $groupscount) {
                $output->filtergroupsname = get_string('filter:groupscountall', 'forumreport_summary');
            } else if (!empty($this->groupsselected)) {
                $output->filtergroupsname = get_string('filter:groupscountnumber', 'forumreport_summary', $groupscount);
            } else {
                $output->filtergroupsname = get_string('filter:groupsname', 'forumreport_summary');
            }

            // Set groups filter.
            $groupsdata = [];

            foreach ($this->groupsavailable as $groupid => $groupname) {
                $groupsdata[] = [
                    'groupid' => $groupid,
                    'groupname' => $groupname,
                    'checked' => in_array($groupid, $this->groupsselected),
                ];
            }

            $output->filtergroups = $groupsdata;
        } else {
            $output->hasgroups = false;
        }

        // Set discussion IDs for use by export links (always included, as it will be empty if not required).
        $output->discussionids = $this->discussionids;

        // Set date button and generate dates popover mform.
        $datesformdata = [];

        if ($this->datesdata['from']['enabled']) {
            $datesformdata['filterdatefrompopover'] = $this->datesdata['from'];
        }

        if ($this->datesdata['to']['enabled']) {
            $datesformdata['filterdatetopopover'] = $this->datesdata['to'];
        }

        $output->filterdatesname = $this->datesbuttontext;
        $datesform = new forumreport_summary\form\dates_filter_form();
        $datesform->set_data($datesformdata);
        $output->filterdatesform = $datesform->render();

         // Set dates filter data within filters form.
        $disableddate = [
            'day' => '',
            'month' => '',
            'year' => '',
            'enabled' => '0',
        ];
        $datefromdata = ['type' => 'from'] + ($this->datesdata['from']['enabled'] ? $this->datesdata['from'] : $disableddate);
        $datetodata = ['type' => 'to'] + ($this->datesdata['to']['enabled'] ? $this->datesdata['to'] : $disableddate);
        $output->filterdatesdata = [$datefromdata, $datetodata];

        return $output;
    }
}
