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

/**
 * Internal library of functions for module pcast
 *
 * All the pcast specific functions, needed to implement the module
 * logic, should go here. Never include this file from your lib.php!
 *
 * @package   mod_pcast
 * @copyright 2010 Stephen Bourget
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

defined('MOODLE_INTERNAL') || die();
use core\url;
require_once($CFG->libdir . '/filelib.php');

/**
 * Function to get information from media files
 * used for <itunes::duration>
 * @param string $filename
 * @return array
 */
function pcast_get_media_information($filename) {
    global $CFG;
    include_once($CFG->dirroot . '/mod/pcast/lib/getid3/getid3/getid3.php');

    // Initialize getID3 engine.
    $getid3 = new getID3();

    // Analyze file and store returned data in $ThisFileInfo.
    $mp3info = $getid3->analyze($filename);
    return $mp3info;  // Playtime in minutes:seconds, formatted string.
}

/**
 * Prints the approval menu
 * @param object $cm
 * @param object $pcast
 * @param string $mode
 * @param string $hook
 * @param string $sortkey
 * @param string $sortorder
 */
function pcast_print_approval_menu($cm, $pcast, $mode, $hook, $sortkey = '', $sortorder = '') {

    echo html_writer::start_tag('div', ['class' => 'pcast-links']) . "\n";
    echo html_writer::tag('div', get_string("explainalphabet", "pcast"), ['class' => 'pcastexplain']);
    echo html_writer::empty_tag('br');

    pcast_print_special_links($cm, $pcast, $mode, $hook);
    pcast_print_alphabet_links($cm, $pcast, $mode, $hook, $sortkey, $sortorder);
    pcast_print_all_links($cm, $pcast, $mode, $hook);
    pcast_print_sorting_links($cm, $mode, $sortkey, $sortorder, $hook);
    echo html_writer::end_tag('div') . "\n";
}

/**
 * Prints the alphabet menu
 * @param object $cm
 * @param object $pcast
 * @param string $mode
 * @param string $hook
 * @param string $sortkey
 * @param string $sortorder
 */
function pcast_print_alphabet_menu($cm, $pcast, $mode, $hook, $sortkey = '', $sortorder = '') {

    echo html_writer::start_tag('div', ['class' => 'pcast-links']) . "\n";
    echo html_writer::tag('div', get_string("explainalphabet", "pcast"), ['class' => 'pcastexplain']);
    echo html_writer::empty_tag('br');

    pcast_print_special_links($cm, $pcast, $mode, $hook);
    pcast_print_alphabet_links($cm, $pcast, $mode, $hook, $sortkey, $sortorder);
    pcast_print_all_links($cm, $pcast, $mode, $hook);
    echo html_writer::end_tag('div') . "\n";
}

/**
 * Prints the date menu
 * @param object $cm
 * @param object $pcast
 * @param string $mode
 * @param string $hook
 * @param string $sortkey
 * @param string $sortorder
 */
function pcast_print_date_menu($cm, $pcast, $mode, $hook, $sortkey = '', $sortorder = '') {
    pcast_print_sorting_links($cm, $mode, $sortkey, $sortorder, $hook);
}

/**
 * Prints the author menu link
 * @param object $cm
 * @param object $pcast
 * @param string $mode
 * @param string $hook
 * @param string $sortkey
 * @param string $sortorder
 */
function pcast_print_author_menu($cm, $pcast, $mode, $hook, $sortkey = '', $sortorder = '') {

    echo html_writer::start_tag('div', ['class' => 'pcast-links']) . "\n";
    echo html_writer::tag('div', get_string("explainalphabet", "pcast"), ['class' => 'pcastexplain']);
    echo html_writer::empty_tag('br');

    if (empty($sortkey)) {
        $sortkey = PCAST_AUTHOR_LNAME;
    }
    if (empty($sortorder)) {
        $sortkey = 'asc';
    }
    pcast_print_alphabet_links($cm, $pcast, $mode, $hook, $sortkey, $sortorder);
    pcast_print_all_links($cm, $pcast, $mode, $hook);
    pcast_print_sorting_links($cm, $mode, $sortkey, $sortorder, $hook);
    echo html_writer::end_tag('div') . "\n";
}

/**
 * Prints the category menu
 * @todo This should use html_writer::tag()
 * @todo These styles should not be hard coded
 * @param object $cm
 * @param object $pcast
 * @param string $hook
 */
function pcast_print_categories_menu($cm, $pcast, $hook = PCAST_SHOW_ALL_CATEGORIES) {
     global $DB, $OUTPUT;

     echo '<table border="0" width="100%">';
     echo '<tr>';

     echo '<td class="pcast-menu20">';

     echo '</td>';

     echo '<td class="pcast-menu60">';
     echo '<span class="pcast-bold">';

     $menu = [];
     $menu[PCAST_SHOW_ALL_CATEGORIES] = get_string("allcategories", "pcast");
     $menu[PCAST_SHOW_NOT_CATEGORISED] = get_string("notcategorised", "pcast");

    // Generate Top Categories.
    if ($topcategories = $DB->get_records("pcast_itunes_categories")) {
        foreach ($topcategories as $topcategory) {
            $value = (int)$topcategory->id * 1000;
            $menu[(int)$value] = $topcategory->name;
        }
    }

    // Generate Secondary Category.
    if ($nestedcategories = $DB->get_records("pcast_itunes_nested_cat")) {
        foreach ($nestedcategories as $nestedcategory) {
            $value = (int)$nestedcategory->topcategoryid * 1000;
            $value = $value + (int)$nestedcategory->id;
            $menu[(int)$value] = '&nbsp;&nbsp;' . $nestedcategory->name;
        }
    }
    ksort($menu);

    // Print the category name.
    if ($hook == PCAST_SHOW_NOT_CATEGORISED) {
        echo get_string("episodeswithoutcategory", "pcast");
    } else if ($hook == PCAST_SHOW_ALL_CATEGORIES) {
        echo get_string("allcategories", "pcast");
    } else {
        // Lookup the category name by 4 digit ID.
        $category = new stdClass();
        $category->category = $hook;
        $category = pcast_get_itunes_categories($category, $pcast);

        // Print the category names in the format top: nested.
        if ($category->nestedcategory == 0) {
            echo $menu[(int)$hook];
        } else {
            // TODO: convert to lang file later.
            echo $menu[(int)$category->topcategory * 1000] . ': ' . $menu[(int)$hook];
        }
    }

    echo '</span></td>';
    echo '<td class="pcast-menu20">';

    $select = new single_select(
        new moodle_url("/mod/pcast/view.php", ['id' => $cm->id, 'mode' => PCAST_CATEGORY_VIEW]),
        'hook',
        $menu,
        $hook,
        null,
        "catmenu"
    );
    $select->set_label(get_string('categories', 'pcast'), ['class' => 'accesshide']);

    echo $OUTPUT->render($select);
    echo '</td>';
    echo '</tr>';
    echo '</table>';
}

/**
 * Prints the link to display all episodes.
 * @param object $cm
 * @param object $pcast
 * @param string $mode
 * @param string $hook
 */
function pcast_print_all_links($cm, $pcast, $mode, $hook) {
    $strallentries = get_string("allentries", "pcast");
    if ($hook == 'ALL') {
        echo html_writer::tag('span', $strallentries, ['class' => 'pcast-bold']);
    } else {
        $strexplainall = strip_tags(get_string("explainall", "pcast"));
        $url = new moodle_url('/mod/pcast/view.php', ['id' => $cm->id, 'mode' => $mode, 'hook' => 'ALL']);
        echo html_writer::tag('a', $strallentries, ['title' => $strexplainall, 'href' => $url]);
    }
}

/**
 * Prints the symbols links used to sort the episodes.
 * @param object $cm
 * @param object $pcast
 * @param string $mode
 * @param string $hook
 */
function pcast_print_special_links($cm, $pcast, $mode, $hook) {
    $strspecial = get_string("special", "pcast");
    if ($hook == 'SPECIAL') {
        echo html_writer::tag('span', $strspecial, ['class' => 'pcast-bold']);
        echo "  | ";
    } else {
        $strexplainspecial = strip_tags(get_string("explainspecial", "pcast"));
        $url = new moodle_url('/mod/pcast/view.php', ['id' => $cm->id, 'mode' => $mode, 'hook' => 'SPECIAL']);
        echo html_writer::tag('a', $strspecial, ['title' => $strexplainspecial, 'href' => $url]);
        echo "  | ";
    }
}

/**
 * Prints the individual letter links used to sort the episodes.
 * @param object $cm
 * @param object $pcast
 * @param string $mode
 * @param string $hook
 * @param string $sortkey
 * @param string $sortorder
 */
function pcast_print_alphabet_links($cm, $pcast, $mode, $hook, $sortkey, $sortorder) {

    $alphabet = explode(",", get_string('alphabet', 'langconfig'));
    $lettersbyline = 26;
    for ($i = 0; $i < count($alphabet); $i++) {
        if ($hook == $alphabet[$i] && $hook) {
            echo html_writer::tag('span', $alphabet[$i], ['class' => 'pcast-bold']);
        } else {
            $url = new moodle_url(
                '/mod/pcast/view.php',
                ['id' => $cm->id,
                'mode' => $mode,
                'hook' => urlencode($alphabet[$i]),
                'sortkey' => $sortkey,
                'sortorder' => $sortorder],
            );

            echo html_writer::tag('a', $alphabet[$i], ['href' => $url]);
        }
        if ((int) ($i % $lettersbyline) != 0 || $i == 0) {
            echo ' | ';
        } else {
            echo html_writer::empty_tag('br');
        }
    }
}

/**
 * Prints the sort by ASC / DSC links on the view page.
 * @param object $cm
 * @param string $mode
 * @param string $sortkey
 * @param string $sortorder
 * @param string $hook
 */
function pcast_print_sorting_links($cm, $mode, $sortkey = '', $sortorder = '', $hook = '') {
    global $OUTPUT;

    // Get our strings.
    $asc    = get_string("ascending", "pcast");
    $desc   = get_string("descending", "pcast");
    $strsortcreation = get_string("sortbycreation", "pcast");
    $strsortlastupdate = get_string("sortbylastupdate", "pcast");
    $strsortfname = get_string("firstname");
    $strsortlname = get_string("lastname");
    $strsortby = get_string("sortby", "pcast");
    $strsep = get_string('labelsep', 'langconfig');

    // Determine our sort order ASC or DESC.
    switch ($sortorder) {
        case 'desc':
            $currentorder = $desc;
            $neworder = 'asc';
            $strchangeto = get_string('changeto', 'pcast', $asc);
            $icon = " " . $OUTPUT->pix_icon($sortorder, $strchangeto, 'pcast');

            break;

        case 'asc':
            $currentorder = $asc;
            $neworder = 'desc';
            $strchangeto = get_string('changeto', 'pcast', $desc);
            $icon = " " . $OUTPUT->pix_icon($sortorder, $strchangeto, 'pcast');

            break;
        default:
            // Pick some reasonable defaults if sort order is not specified.
            switch ($sortkey) {
                case PCAST_DATE_UPDATED:
                case PCAST_DATE_CREATED:
                case PCAST_AUTHOR_FNAME:
                case PCAST_AUTHOR_LNAME:
                    $strchangeto = get_string('changeto', 'pcast', $asc);
                    $neworder = 'asc';
                    $icon = " " . $OUTPUT->pix_icon($neworder, $strchangeto, 'pcast');
                    $currentorder = '';
                    break;

                default:
                    $icon = "";
                    $neworder = '';
                    $currentorder = '';
                    $strchangeto = get_string('changeto', 'pcast', $asc);

                    break;
            }
    }

    switch ($sortkey) {
        case PCAST_DATE_UPDATED:
            // URLs.
            $url1 = new moodle_url(
                '/mod/pcast/view.php',
                ['id' => $cm->id, 'mode' => $mode, 'hook' => $hook, 'sortkey' => PCAST_DATE_UPDATED]
            );
            $url2 = new moodle_url(
                '/mod/pcast/view.php',
                ['id' => $cm->id, 'mode' => $mode, 'hook' => $hook, 'sortkey' => PCAST_DATE_CREATED]
            );

            if ($neworder != '') {
                $url1->param('sortorder', $neworder);
            }

            // Hyperlinks.
            $link1 = html_writer::tag(
                'a',
                $strsortlastupdate . $icon,
                ['href' => $url1, 'title' => $strsortlastupdate . ' ' . $strchangeto]
            );
            $link2 = html_writer::tag(
                'a',
                $strsortcreation,
                ['href' => $url2, 'title' => $strsortcreation . ' ' . $asc]
            );

            // Output.
            $html = html_writer::tag(
                'span',
                get_string('current', 'pcast', $strsortlastupdate . ' ' . $currentorder),
                ['class' => 'accesshide']
            );
            $html .= $strsortby . $strsep;
            $html .= $link1 . ' | ';
            $html .= html_writer::tag('span', $link2, ['class' => 'pcast-bold']);
            break;

        case PCAST_DATE_CREATED:
            // URLs.
            $url1 = new moodle_url(
                '/mod/pcast/view.php',
                ['id' => $cm->id, 'mode' => $mode, 'hook' => $hook, 'sortkey' => PCAST_DATE_UPDATED]
            );
            $url2 = new moodle_url(
                '/mod/pcast/view.php',
                ['id' => $cm->id, 'mode' => $mode, 'hook' => $hook, 'sortkey' => PCAST_DATE_CREATED]
            );

            if ($neworder != '') {
                $url2->param('sortorder', $neworder);
            }

            // Hyperlinks.
            $link1 = html_writer::tag('a', $strsortlastupdate, ['href' => $url1, 'title' => $strsortlastupdate . ' ' . $asc]);
            $link2 = html_writer::tag('a', $strsortcreation . $icon, ['href' => $url2, 'title' => $strsortcreation . ' ' . $strchangeto]);

            // Output.
            $html = html_writer::tag('span', get_string('current', 'pcast', $strsortcreation . ' ' . $currentorder), ['class' => 'accesshide']);
            $html .= $strsortby . $strsep;
            $html .= $link1 . ' | ';
            $html .= html_writer::tag('span', $link2, ['class' => 'pcast-bold']);

            break;

        case PCAST_AUTHOR_FNAME:
            // URLs.
            $url1 = new moodle_url(
                '/mod/pcast/view.php',
                ['id' => $cm->id, 'mode' => $mode, 'hook' => $hook, 'sortkey' => PCAST_AUTHOR_LNAME]
            );
            $url2 = new moodle_url(
                '/mod/pcast/view.php',
                ['id' => $cm->id, 'mode' => $mode, 'hook' => $hook, 'sortkey' => PCAST_AUTHOR_FNAME]
            );

            if ($neworder != '') {
                $url2->param('sortorder', $neworder);
            }

            // Hyperlinks.
            $link1 = html_writer::tag('a', $strsortlname, ['href' => $url1, 'title' => $strsortlname . ' ' . $asc]);
            $link2 = html_writer::tag('a', $strsortfname . $icon, ['href' => $url2, 'title' => $strsortfname . ' ' . $strchangeto]);

            // Output.
            $html = html_writer::tag('span', get_string('current', 'pcast', $strsortlname . ' ' .
                    $currentorder), ['class' => 'accesshide']);
            $html .= $strsortby . $strsep;
            $html .= $link1 . ' | ';
            $html .= html_writer::tag('span', $link2, ['class' => 'pcast-bold']);

            break;

        case PCAST_AUTHOR_LNAME:
            // URLs.
            $url1 = new moodle_url(
                '/mod/pcast/view.php',
                ['id' => $cm->id, 'mode' => $mode, 'hook' => $hook, 'sortkey' => PCAST_AUTHOR_LNAME]
            );
            $url2 = new moodle_url(
                '/mod/pcast/view.php',
                ['id' => $cm->id, 'mode' => $mode, 'hook' => $hook, 'sortkey' => PCAST_AUTHOR_FNAME]
            );

            if ($neworder != '') {
                $url1->param('sortorder', $neworder);
            }

            // Hyperlinks.
            $link1 = html_writer::tag(
                'a',
                $strsortlname . $icon,
                ['href' => $url1, 'title' => $strsortlname . ' ' . $strchangeto]
            );
            $link2 = html_writer::tag(
                'a',
                $strsortfname,
                ['href' => $url2, 'title' => $strsortfname . ' ' . $asc]
            );

            // Output.
            $html = html_writer::tag(
                'span',
                get_string('current', 'pcast', $strsortfname . ' ' . $currentorder),
                ['class' => 'accesshide']
            );

            $html .= $strsortby . $strsep;
            $html .= $link1 . ' | ';
            $html .= html_writer::tag('span', $link2, ['class' => 'pcast-bold']);

            break;

        default:
            $html = '';
    }

    // Display the links.
    echo html_writer::empty_tag('br') . $html . html_writer::empty_tag('br');
}

/**
 * Function to display Pcast episodes
 * @param object $pcast
 * @param object $cm
 * @param int $groupmode
 * @param string $hook
 * @param string $sortkey
 * @param string $sortorder
 * @param int $page
 * @return boolean
 */
function pcast_display_standard_episodes($pcast, $cm, $groupmode = 0, $hook = '', $sortkey = '', $sortorder = 'asc', $page = 0) {
    global $DB, $USER;

    $context = context_module::instance($cm->id);

    // Get the current group.
    if ($groupmode > 0) {
        $currentgroup = groups_get_activity_group($cm);
    } else {
        $currentgroup = 0;
    }

    // Get the episodes for this pcast.
    if (!empty($sortorder)) {
        $sort = 'p.name ' . $sortorder;
    } else {
        $sort = 'p.name ASC';
    }
    $sql = pcast_get_episode_sql();
    $sql .= " WHERE p.pcastid = ? AND (p.approved =? OR p.userid =? )";

    if (empty($hook) || ($hook == 'ALL')) {
        $sql .= " ORDER BY " . $sort;
        $episodes = $DB->get_records_sql($sql, [$pcast->id, '1', $USER->id]);
    } else if ($hook == 'SPECIAL') {
        // Match Other Characters.
        $sql .= " AND (" . $DB->sql_like('p.name', '?', false) . "
                 OR " . $DB->sql_like('p.name', '?', false) . "
                 OR " . $DB->sql_like('p.name', '?', false) . "
                 OR " . $DB->sql_like('p.name', '?', false) . "
                 OR " . $DB->sql_like('p.name', '?', false) . "
                 OR " . $DB->sql_like('p.name', '?', false) . "
                 OR " . $DB->sql_like('p.name', '?', false) . "
                 OR " . $DB->sql_like('p.name', '?', false) . "
                 OR " . $DB->sql_like('p.name', '?', false) . "
                 OR " . $DB->sql_like('p.name', '?', false) . "
                 )
                ORDER BY $sort";
        $episodes = $DB->get_records_sql($sql, [$pcast->id, '1', $USER->id,
                                         '1%', '2%', '3%', '4%', '5%',
                                         '6%', '7%', '8%', '9%', '0%',
                                         ]);
    } else {
        $sql .= " and " . $DB->sql_like('p.name', '?', false) . " ORDER BY $sort";
        $episodes = $DB->get_records_sql($sql, [$pcast->id, '1', $USER->id, $hook . '%']);
    }

    // Get Episode count.
    $count = 0;

    // Calculate starting episode.
    $start = $page * $pcast->episodesperpage;
    $end = ($page + 1) * $pcast->episodesperpage;
    // Get Group members.
    $members = get_enrolled_users($context, 'mod/pcast:write', $currentgroup, 'u.id', 'u.id ASC');
    foreach ($episodes as $episode) {
        if (isset($members[$episode->userid]->id) && ($members[$episode->userid]->id == $episode->userid)) {
            // Display this episode (User is in the group).
            if (($count >= $start) && ($count < $end)) {
                pcast_display_episode_brief($episode, $cm);
            }
            $count++;
        } else if ($currentgroup == 0) {
            // Display this episode (NO GROUPS USED or user is the author).
            if (($count >= $start) && ($count < $end)) {
                pcast_display_episode_brief($episode, $cm);
            }
            $count++;
        }
    }
    pcast_display_paging_bar($pcast, $cm, $count, $page, PCAST_STANDARD_VIEW, $hook, $sortkey, $sortorder);
    return true;
}

/**
 * Determine if the user is able to view the specified episode.
 * This takes in account group member access.
 * @param object $episode
 * @param object $cm
 * @param int $groupmode
 * @return bool (true if allowed, false if denied)
 */
function pcast_episode_allowed_viewing($episode, $cm, $groupmode) {
    global $USER;

    $context = context_module::instance($cm->id);
    $currentgroup = 0;

    // Does the user have the ability to view this episode?
    if (!has_capability('mod/pcast:view', $context)) {
        return false;
    }

    // Has the episode been approved? If not then should they be able to see it?
    if ($episode->userid !== $USER->id) {
        // Not the author, Is it approved?
        if ($episode->approved == PCAST_EPISODE_DISAPPROVE) {
            // Not Approved, Can they approve it?
            if (!has_capability('mod/pcast:approve', $context)) {
                // Cannot approve it, so they cannot see it.
                return false;
            }
        }
    }

    // Get the current group info.
    if ($groupmode > 0) {
        $currentgroup = groups_get_activity_group($cm);
    } else {
        // No groups.
        return true;
    }

    // See if user can view all groups.
    if (has_capability('moodle/site:accessallgroups', $context)) {
        return true;
    }

    // Get Group members.
    $members = get_enrolled_users($context, 'mod/pcast:write', $currentgroup, 'u.id', 'u.id ASC');

    // See if episode is created by a member of the group.
    if (isset($members[$episode->userid]->id) && ($members[$episode->userid]->id == $episode->userid)) {
        // Member of the group.
        return true;
    } else {
        // Not a member.
        return false;
    }

    // Not able to view episode (should never get here).
    return false;
}

/**
 * Function to display episodes by category
 * @param object $pcast
 * @param object $cm
 * @param int $groupmode
 * @param string $hook
 * @param int $page
 */
function pcast_display_category_episodes($pcast, $cm, $groupmode = 0, $hook = PCAST_SHOW_ALL_CATEGORIES, $page = 0) {
    global $DB, $USER;

    $context = context_module::instance($cm->id);

    // Get the current group.
    if ($groupmode > 0) {
        $currentgroup = groups_get_activity_group($cm);
    } else {
        $currentgroup = 0;
    }

    // Get the episodes for this pcast.
    $sql = pcast_get_episode_sql();
    $sql .= " WHERE p.pcastid = ? AND (p.approved =? OR p.userid =? )";

    if ($hook == PCAST_SHOW_ALL_CATEGORIES) {
        $sql .= " ORDER BY cat.name, ncat.name, p.name ASC";
        $episodes = $DB->get_records_sql($sql, [$pcast->id, '1', $USER->id]);
    } else if ($hook == PCAST_SHOW_NOT_CATEGORISED) {
        $sql .= " AND
                p.topcategory = ?
                ORDER BY p.name ASC";
        $episodes = $DB->get_records_sql($sql, [$pcast->id, '1', $USER->id, '0']);
    } else {
        $category = new stdClass();
        $category->category = $hook;
        $category = pcast_get_itunes_categories($category, $pcast);
        if ($category->nestedcategory == 0) {
            $sql .= " AND
                    p.topcategory = ?
                    ORDER BY cat.name, ncat.name, p.name ASC";
            $episodes = $DB->get_records_sql($sql, [$pcast->id, '1', $USER->id, $category->topcategory]);
        } else {
            $sql .= " AND
                    p.nestedcategory = ?
                    ORDER BY cat.name, ncat.name, p.name ASC";
            $episodes = $DB->get_records_sql($sql, [$pcast->id, '1', $USER->id, $category->nestedcategory]);
        }
    }

    // Get Episode count.
    $count = 0;

    // Calculate starting episode.
    $start = $page * $pcast->episodesperpage;
    $end = ($page + 1) * $pcast->episodesperpage;

    // Get Group members.
    $members = get_enrolled_users($context, 'mod/pcast:write', $currentgroup, 'u.id', 'u.id ASC');
    foreach ($episodes as $episode) {
        if (isset($members[$episode->userid]->id) && ($members[$episode->userid]->id == $episode->userid)) {
            // Display this episode (User is in the group).
            if (($count >= $start) && ($count < $end)) {
                pcast_display_episode_brief($episode, $cm);
            }
            $count++;
        } else if ($currentgroup == 0) {
            // Display this episode (NO GROUPS USED or user is the author).
            if (($count >= $start) && ($count < $end)) {
                pcast_display_episode_brief($episode, $cm);
            }
            $count++;
        }
    }

    pcast_display_paging_bar($pcast, $cm, $count, $page, PCAST_CATEGORY_VIEW, $hook);
}

/**
 * Display all episodes that sorted by date
 * @param object $pcast
 * @param object $cm
 * @param int $groupmode
 * @param string $hook
 * @param string $sortkey
 * @param string $sortorder
 * @param int $page
 */
function pcast_display_date_episodes($pcast, $cm, $groupmode = 0, $hook = '', $sortkey = PCAST_DATE_CREATED, $sortorder = 'desc', $page = 0) {
    global $DB, $USER;

    $context = context_module::instance($cm->id);

    // Get the current group.
    if ($groupmode > 0) {
        $currentgroup = groups_get_activity_group($cm);
    } else {
        $currentgroup = 0;
    }

    // Get the episodes for this pcast.
    $sql = pcast_get_episode_sql();
    $sql .= " WHERE p.pcastid = ? AND (p.approved =? OR p.userid =? )";

    switch ($sortkey) {
        case PCAST_DATE_UPDATED:
            $sql .= " ORDER BY p.timemodified";
            break;

        case PCAST_DATE_CREATED:
        default:
            $sql .= " ORDER BY p.timecreated";
            break;
    }

    switch ($sortorder) {
        case 'asc':
            $sql .= " ASC , p.name ASC";
            break;
        case 'desc':
        default:
            $sql .= " DESC, p.name ASC";
            break;
    }

    $episodes = $DB->get_records_sql($sql, [$pcast->id, '1', $USER->id]);

    // Get Episode count.
    $count = 0;

    // Calculate starting episode.
    $start = $page * $pcast->episodesperpage;
    $end = ($page + 1) * $pcast->episodesperpage;

    // Get Group members.
    $members = get_enrolled_users($context, 'mod/pcast:write', $currentgroup, 'u.id', 'u.id ASC');
    foreach ($episodes as $episode) {
        if (isset($members[$episode->userid]->id) && ($members[$episode->userid]->id == $episode->userid)) {
            // Display this episode (User is in the group).
            if (($count >= $start) && ($count < $end)) {
                pcast_display_episode_brief($episode, $cm);
            }
            $count++;
        } else if ($currentgroup == 0) {
            // Display this episode (NO GROUPS USED or user is the author).
            if (($count >= $start) && ($count < $end)) {
                pcast_display_episode_brief($episode, $cm);
            }
            $count++;
        }
    }

    pcast_display_paging_bar($pcast, $cm, $count, $page, PCAST_DATE_VIEW, $hook, $sortkey, $sortorder);
}

/**
 * Display all episodes that sorted by author
 * @param object $pcast
 * @param object $cm
 * @param int $groupmode
 * @param string $hook
 * @param string $sortkey
 * @param string $sortorder
 * @param int $page
 */
function pcast_display_author_episodes($pcast, $cm, $groupmode = 0, $hook = '', $sortkey = '', $sortorder = 'asc', $page = 0) {
    global $DB, $USER;

    $context = context_module::instance($cm->id);

    // Get the current group.
    if ($groupmode > 0) {
        $currentgroup = groups_get_activity_group($cm);
    } else {
        $currentgroup = 0;
    }

    // Get the episodes for this pcast.
    $sql = pcast_get_episode_sql();
    $sql .= " WHERE p.pcastid = ? AND (p.approved =? OR p.userid =? )";

    // Setup for ASC or DESC sorting.
    switch ($sortorder) {
        case 'asc':
            $sort = "ASC";
            break;
        case 'desc':
        default:
            $sort = "DESC";
            break;
    }

    // Construct lookups based on FNAME / LNAME and HOOK.
    switch ($sortkey) {
        case PCAST_AUTHOR_LNAME:
            // Order is constant for all LNAME sorts.
            $order = " ORDER BY u.lastname " . $sort . ", u.firstname " . $sort . ", p.name ASC";

            // Handle cases where you lookup by first letter of name (last / first).
            if (empty($hook) || ($hook == 'ALL')) {
                $sql .= $order;
                $episodes = $DB->get_records_sql($sql, [$pcast->id, '1', $USER->id]);
            } else {
                $sql .= " and " . $DB->sql_like('u.lastname', '?', false) . $order;
                $episodes = $DB->get_records_sql($sql, [$pcast->id, '1', $USER->id, $hook . '%']);
            }

            break;

        case PCAST_AUTHOR_FNAME:
        default:
            // Order is constant for all FNAME sorts.
            $order = " ORDER BY u.firstname " . $sort . ", u.lastname " . $sort . ", p.name ASC";

            // Handle cases where you lookup by first letter of name (last / first).
            if (empty($hook) || ($hook == 'ALL')) {
                $sql .= $order;
                $episodes = $DB->get_records_sql($sql, [$pcast->id, '1', $USER->id]);
            } else {
                $sql .= " and " . $DB->sql_like('u.firstname', '?', false) . $order;
                $episodes = $DB->get_records_sql($sql, [$pcast->id, '1', $USER->id, $hook . '%']);
            }

            break;
    }

    // Get Episode count.
    $count = 0;

    // Calculate starting episode.
    $start = $page * $pcast->episodesperpage;
    $end = ($page + 1) * $pcast->episodesperpage;

    // Get Group members.
    $members = get_enrolled_users($context, 'mod/pcast:write', $currentgroup, 'u.id', 'u.id ASC');
    foreach ($episodes as $episode) {
        if (isset($members[$episode->userid]->id) && ($members[$episode->userid]->id == $episode->userid)) {
            // Display this episode (User is in the group).
            if (($count >= $start) && ($count < $end)) {
                pcast_display_episode_brief($episode, $cm);
            }
            $count++;
        } else if ($currentgroup == 0) {
            // Display this episode (NO GROUPS USED or user is the author).
            if (($count >= $start) && ($count < $end)) {
                pcast_display_episode_brief($episode, $cm);
            }
            $count++;
        }
    }

    pcast_display_paging_bar($pcast, $cm, $count, $page, PCAST_AUTHOR_VIEW, $hook, $sortkey, $sortorder);
}

/**
 * Display all episodes that have not yet been approved
 * @param object $pcast
 * @param object $cm
 * @param int $groupmode
 * @param string $hook
 * @param string $sortkey
 * @param string $sortorder
 * @param int $page
 */
function pcast_display_approval_episodes($pcast, $cm, $groupmode = 0, $hook = '', $sortkey = '', $sortorder = 'asc', $page = 0) {
    global $DB;

    $context = context_module::instance($cm->id);

    // Get the current group.
    if ($groupmode > 0) {
        $currentgroup = groups_get_activity_group($cm);
    } else {
        $currentgroup = 0;
    }

    // Get the episodes for this pcast.
    $sql = pcast_get_episode_sql();
    $sql .= " WHERE p.pcastid = ? AND p.approved =?";

    if (!empty($sortorder)) {
        $sort = 'p.name ' . $sortorder;
    } else {
        $sort = 'p.name ASC';
    }

    if (empty($hook) || ($hook == 'ALL')) {
        $sql .= " ORDER BY " . $sort;
        $episodes = $DB->get_records_sql($sql, [$pcast->id, '0']);
    } else if ($hook == 'SPECIAL') {
        // Match Other Characters.
        $sql .= " AND (" . $DB->sql_like('p.name', '?', false) . "
                 OR " . $DB->sql_like('p.name', '?', false) . "
                 OR " . $DB->sql_like('p.name', '?', false) . "
                 OR " . $DB->sql_like('p.name', '?', false) . "
                 OR " . $DB->sql_like('p.name', '?', false) . "
                 OR " . $DB->sql_like('p.name', '?', false) . "
                 OR " . $DB->sql_like('p.name', '?', false) . "
                 OR " . $DB->sql_like('p.name', '?', false) . "
                 OR " . $DB->sql_like('p.name', '?', false) . "
                 OR " . $DB->sql_like('p.name', '?', false) . "
                 )
                ORDER BY $sort";
        $episodes = $DB->get_records_sql($sql, [$pcast->id, '0', '1%', '2%', '3%', '4%', '5%', '6%', '7%', '8%', '9%', '0%']);
    } else {
        $sql .= " and " . $DB->sql_like('p.name', '?', false) . " ORDER BY $sort";
        $episodes = $DB->get_records_sql($sql, [$pcast->id, '0', $hook . '%']);
    }

    // Get Episode count.
    $count = 0;

    // Calculate starting episode.
    $start = $page * $pcast->episodesperpage;
    $end = ($page + 1) * $pcast->episodesperpage;
    // Get Group members.

    $members = get_enrolled_users($context, 'mod/pcast:write', $currentgroup, 'u.id', 'u.id ASC');
    foreach ($episodes as $episode) {
        if (isset($members[$episode->userid]->id) && ($members[$episode->userid]->id == $episode->userid)) {
            // Display this episode (User is in the group).
            if (($count >= $start) && ($count < $end)) {
                pcast_display_episode_brief($episode, $cm);
            }
            $count++;
        } else if ($currentgroup == 0) {
            // Display this episode (NO GROUPS USED or user is the author).
            if (($count >= $start) && ($count < $end)) {
                pcast_display_episode_brief($episode, $cm);
            }
            $count++;
        }
    }

    pcast_display_paging_bar($pcast, $cm, $count, $page, PCAST_APPROVAL_VIEW, $hook, $sortkey, $sortorder);
}

/**
 * Generates the SQL needed to get all episodes belonging to a specific pcast.
 * @return string
 */
function pcast_get_episode_sql() {
       $sql = "SELECT p.id AS id,
                p.pcastid AS pcastid,
                p.course AS course,
                p.userid AS userid,
                p.name AS name,
                p.summary AS summary,
                p.summaryformat AS summaryformat,
                p.summarytrust AS summarytrust,
                p.mediafile AS mediafile,
                p.duration AS duration,
                p.explicit AS explicit,
                p.subtitle AS subtitle,
                p.keywords AS keywords,
                p.topcategory as topcatid,
                p.nestedcategory as nestedcatid,
                p.timecreated as timecreated,
                p.timemodified as timemodified,
                p.approved as approved,
                p.sequencenumber as sequencenumber,
                pcast.userscancomment as userscancomment,
                pcast.userscancategorize as userscancategorize,
                pcast.userscanpost as userscanpost,
                pcast.requireapproval as requireapproval,
                pcast.displayauthor as displayauthor,
                pcast.displayviews as displayviews,
                pcast.assessed as assessed,
                pcast.assesstimestart as assesstimestart,
                pcast.assesstimefinish as assesstimefinish,
                pcast.scale as scale,
                cat.name as topcategory,
                ncat.name as nestedcategory,
                u.firstname as firstname,
                u.lastname as lastname
            FROM {pcast_episodes} p
            LEFT JOIN
                {pcast} AS pcast ON
                p.pcastid = pcast.id
            LEFT JOIN
                {user} AS u ON
                p.userid = u.id
            LEFT JOIN
                {pcast_itunes_categories} AS cat ON
                p.topcategory = cat.id
            LEFT JOIN
                {pcast_itunes_nested_cat} AS ncat ON
                p.nestedcategory = ncat.id";

    return $sql;
}

/**
 * Function to print overview of the episode
 * @param object $episode
 * @param object $cm
 * @param bool $showmedia
 * @param bool $showlinks
 */
function pcast_display_episode_brief($episode, $cm, $showmedia = true, $showlinks = true) {
    global $CFG, $DB, $OUTPUT;

    $context = context_module::instance($cm->id);

    $strsep = get_string('labelsep', 'langconfig');
    $html = html_writer::start_tag('div', ['class' => 'no-overflow']) . "\n";
    $html .= html_writer::start_tag('div', ['class' => 'pcast-episode']) . "\n";

    $table = new html_table();
    $table->cellpadding = '5';
    $table->colclasses = ['pcast-header', 'pcast-data'];
    $table->width = '100%';
    $table->align = ["RIGHT", "LEFT"];
    // Name of episode.
    $table->data[] = [get_string("name", "pcast"), format_text($episode->name, FORMAT_HTML, ['context' => $context])];

    // Description.
    $summarytext = file_rewrite_pluginfile_urls(
        $episode->summary,
        'pluginfile.php',
        $context->id,
        'mod_pcast',
        'summary',
        $episode->id
    );

    $summarytext = format_text($summarytext, $episode->summaryformat, ['context' => $context]);

    $table->data[] = [get_string("summary", "pcast"), $summarytext];

    // Category -Display only if enabled.
    if ((isset($episode->userscancategorize)) && ($episode->userscancategorize != '0')) {
        if ((isset($episode->topcategory)) && ($episode->topcategory != '0')) {
            $episode->category = $episode->topcategory;

            if ((isset($episode->nestedcategory)) && ($episode->nestedcategory != '0')) {
                $episode->category .= $strsep . ' ' . $episode->nestedcategory;
            }
        }
        if (isset($episode->category)) {
            $table->data[] = [get_string("category", "pcast"), s($episode->category)];
        }
    }

    // Attachment.
    if ($showmedia) {
        $table->data[] = [get_string("pcastmediafile", "pcast"), pcast_display_mediafile_link($episode, $cm)];
    }
    // Author.
    // Only print author if allowed or has manage rights.
    if (((isset($episode->displayauthor)) && ($episode->displayauthor != '0')) || (has_capability('mod/pcast:manage', $context))) {
        $user = $DB->get_record("user", ["id" => $episode->userid]);
        $table->data[] = [get_string("author", "pcast"), fullname($user)];
    }

    // Created.
    $table->data[] = [get_string("created", "pcast"), userdate($episode->timecreated)];

    // Updated.
    $table->data[] = [get_string("updated", "pcast"), userdate($episode->timemodified)];

    // Tags.
    if (core_tag_tag::is_enabled('mod_pcast', 'pcast_episodes')) {
        $tags = $OUTPUT->tag_list(core_tag_tag::get_item_tags('mod_pcast', 'pcast_episodes', $episode->id), null, 'pcast-tags');
        $table->data[] = [get_string("tags", "tag"), $tags];
    }

    // Calculate editing period.
    $ineditingperiod = ((time() - $episode->timecreated < $CFG->maxeditingtime));
    $link = '';

    // Management Links.
    if ((has_capability('mod/pcast:manage', $context)) || ($ineditingperiod)) {
        // Edit Link.
        $url = new moodle_url('/mod/pcast/edit.php', ['cmid' => $cm->id, 'id' => $episode->id]);
        $link .= html_writer::tag('a', get_string('edit'), ['href' => $url, 'title' => get_string('editthisepisode', 'pcast')]);
        $link .= ' | ' . "\n";

        // Delete link.
        $url = new moodle_url('/mod/pcast/deleteepisode.php', ['id' => $cm->id, 'episode' => $episode->id, 'prevmode' => 0]);
        $link .= html_writer::tag('a', get_string('delete'), ['href' => $url, 'title' => get_string('deletethisepisode', 'pcast')]);
        $link .= ' | ' . "\n";
    }
        // View Link.
        $url = new moodle_url('/mod/pcast/showepisode.php', ['eid' => $episode->id]);
        $link .= html_writer::tag('a', get_string('view'), ['href' => $url, 'title' => get_string('viewepisode', 'pcast')]);

    // Approve / Disapprove Link.
    if ((has_capability('mod/pcast:approve', $context)) && ($episode->requireapproval)) {
        if (!$episode->approved) {
            // Approve link.
            $link .= ' | ' . "\n";
            $url = new moodle_url(
                '/mod/pcast/approveepisode.php',
                ['eid' => $episode->id, 'mode' => PCAST_APPROVAL_VIEW, 'sesskey' => sesskey()]
            );
            $link .= html_writer::tag(
                'a',
                get_string('approve'),
                ['href' => $url, 'title' => get_string('approvethisepisode', 'pcast')]
            );
        } else {
            // Disapprove link.
            $link .= ' | ' . "\n";
            $url = new moodle_url(
                '/mod/pcast/approveepisode.php',
                ['eid' => $episode->id, 'newstate' => PCAST_EPISODE_DISAPPROVE, 'sesskey' => sesskey()]
            );
            $link .= html_writer::tag(
                'a',
                get_string('disapprove', 'pcast'),
                ['href' => $url, 'title' => get_string('disapprovethisepisode', 'pcast')]
            );
        }
    }

    // Construct links.
    if ($showlinks) {
        $table->data[] = ['', $link];
    }

    echo $html;
    echo html_writer::table($table);
    echo html_writer::end_tag('div') . "\n";
    echo html_writer::end_tag('div') . "\n";
}

/**
 * Display the full pcast episode
 * @param object $episode
 * @param object $cm
 * @param object $course
 */
function pcast_display_episode_full($episode, $cm, $course) {
    global $CFG, $DB, $USER, $OUTPUT;

    $context = context_module::instance($cm->id);

    $strsep = get_string('labelsep', 'langconfig');
    $html = html_writer::start_tag('div', ['class' => 'pcast-episode']) . "\n";

    $table = new html_table();
    $table->cellpadding = '5';
    $table->colclasses = ['pcast-header', 'pcast-data'];
    $table->width = '100%';
    $table->align = ["RIGHT", "LEFT"];
    // Name of episode.
    $table->data[] = [get_string("name", "pcast"), $episode->name];

    // Description.
    $summarytext = file_rewrite_pluginfile_urls(
        $episode->summary,
        'pluginfile.php',
        $context->id,
        'mod_pcast',
        'summary',
        $episode->id
    );

    $summarytext = format_text($summarytext, $episode->summaryformat, ['context' => $context]);

    $table->data[] = [get_string("summary", "pcast"), $summarytext];

    // Category -Display only if enabled.
    if ((isset($episode->userscancategorize)) && ($episode->userscancategorize != '0')) {
        if ((isset($episode->topcategory)) && ($episode->topcategory != '0')) {
            $episode->category = $episode->topcategory;

            if ((isset($episode->nestedcategory)) && ($episode->nestedcategory != '0')) {
                $episode->category .= $strsep . ' ' . $episode->nestedcategory;
            }
        }
        if (isset($episode->category)) {
            $table->data[] = [get_string("category", "pcast"), $episode->category];
        }
    }

    // Attachment.
    $table->data[] = [get_string("pcastmediafile", "pcast"), pcast_display_mediafile_link($episode, $cm)];

    // Duration.
    $length = [];
    $length2 = new stdClass();
    if (!empty($episode->duration)) {
        // Split up duration for printing.
        $length = explode(":", $episode->duration);
        if (count($length) == 2) {
            $length2->hour = 0;
            $length2->min = $length[0];
            $length2->sec = $length[1];
        } else {
            $length2->hour = $length[0];
            $length2->min = $length[1];
            $length2->sec = $length[2];
        }
    } else {
        $length2->hour = 0;
        $length2->min = 0;
        $length2->sec = 0;
    }
    if ($length2->hour == 0) {
        $table->data[] = [get_string("duration", "pcast"), get_string("durationlength2", "pcast", $length2)];
    } else {
        $table->data[] = [get_string("duration", "pcast"), get_string("durationlength", "pcast", $length2)];
    }

    // Author.
    // Only print author if allowed or has manage rights.
    if (((isset($episode->displayauthor)) && ($episode->displayauthor != '0')) || (has_capability('mod/pcast:manage', $context))) {
        $user = $DB->get_record("user", ["id" => $episode->userid]);
        $table->data[] = [get_string("author", "pcast"), fullname($user)];
    }

    // Created.
    $table->data[] = [get_string("created", "pcast"), userdate($episode->timecreated)];

    // Updated.
    $table->data[] = [get_string("updated", "pcast"), userdate($episode->timemodified)];

    // Total views.
    $table->data[] = [get_string("totalviews", "pcast"), pcast_get_episode_view_count($episode)];

    // Total comments.
    if (($CFG->usecomments) && ($episode->userscancomment) && (has_capability('moodle/comment:view', $context))) {
        $table->data[] = [get_string("totalcomments", "pcast"), pcast_get_episode_comment_count($episode, $cm)];
    }

    // Total Ratings.
    if (
        ($episode->assessed) &&
        ((has_capability('moodle/rating:view', $context)) && ($episode->userid == $USER->id)) ||
        (has_capability('moodle/rating:viewany', $context))
    ) {
        $table->data[] = [get_string("totalratings", "pcast"), pcast_get_episode_rating_count($episode, $cm)];
    }

    // Tags.
    if (core_tag_tag::is_enabled('mod_pcast', 'pcast_episodes')) {
        $tags = $OUTPUT->tag_list(core_tag_tag::get_item_tags('mod_pcast', 'pcast_episodes', $episode->id), null, 'pcast-tags');
        $table->data[] = [get_string("tags", "tag"), $tags];
    }

    // Calculate editing period.
    $ineditingperiod = ((time() - $episode->timecreated < $CFG->maxeditingtime));
    $manage = '';
    $approve = '';

        // Management Links.
    if ((has_capability('mod/pcast:manage', $context)) || ($ineditingperiod)) {
        // Edit Link.
        $url = new moodle_url('/mod/pcast/edit.php', ['cmid' => $cm->id, 'id' => $episode->id]);
        $manage .= html_writer::tag(
            'a',
            get_string('edit'),
            ['href' => $url, 'title' => get_string('editthisepisode', 'pcast')]
        );
        $manage .= ' | ' . "\n";

        // Delete link.
        $url = new moodle_url('/mod/pcast/deleteepisode.php', ['id' => $cm->id, 'episode' => $episode->id, 'prevmode' => 0]);
        $manage .= html_writer::tag(
            'a',
            get_string('delete'),
            ['href' => $url, 'title' => get_string('deletethisepisode', 'pcast')]
        );
    }

    // Approve / Disapprove Link.
    if ((has_capability('mod/pcast:approve', $context)) && ($episode->requireapproval)) {
        if (!$episode->approved) {
            // Approve link.
            $url = new moodle_url(
                '/mod/pcast/approveepisode.php',
                ['eid' => $episode->id, 'mode' => PCAST_APPROVAL_VIEW, 'sesskey' => sesskey()]
            );

            $approve .= html_writer::tag(
                'a',
                get_string('approve'),
                ['href' => $url, 'title' => get_string('approvethisepisode', 'pcast')]
            );
        } else {
            // Disapprove link.
            $url = new moodle_url(
                '/mod/pcast/approveepisode.php',
                ['eid' => $episode->id, 'newstate' => PCAST_EPISODE_DISAPPROVE, 'sesskey' => sesskey()]
            );

            $approve .= html_writer::tag(
                'a',
                get_string('disapprove', 'pcast'),
                ['href' => $url, 'title' => get_string('disapprovethisepisode', 'pcast')]
            );
        }
    }

    // Construct links.
    if ((!empty($manage)) && (!empty($approve))) {
        $link = $manage . ' | ' . "\n" . $approve;
    } else {
        $link = $manage . $approve;
    }
    $table->data[] = ['', $link];

    echo $html;
    echo html_writer::table($table);
    echo html_writer::end_tag('div') . "\n";
}

/**
 * Displays all views for a single episode
 * @param object $episode
 * @param object $cm
 */
function pcast_display_episode_views($episode, $cm) {

    global $DB;

    if (!$views = $DB->get_records("pcast_views", ["episodeid" => $episode->id])) {
        echo get_string('noviews', 'pcast', get_string('modulename', 'pcast'));
    } else {
        $strviews  = get_string("views", "pcast");
        $struser = get_string("user", "pcast");
        $strdate = get_string("date");

        $table = new html_table();
        $table->attributes['class'] = 'views';
        $table->head  = [$struser, $strdate, $strviews];
        $table->align = ["CENTER", "LEFT", "CENTER"];
        $table->width = '100%';

        foreach ($views as $view) {
            $user = $DB->get_record("user", ["id" => $view->userid]);
            $linedata = [fullname($user), userdate($view->lastview), $view->views];
            $table->data[] = $linedata;
        }
        echo html_writer::empty_tag('br');
        echo html_writer::table($table);
    }

    // Trigger view list has been viewed event.
    $params = ['context' => context_module::instance($cm->id), 'objectid' => $episode->id];
    $event = \mod_pcast\event\episode_views_viewed::create($params);
    $event->add_record_snapshot('pcast_episodes', $episode);
    $event->trigger();
}

/**
 * Displays all comments for a single episode
 * @param object $episode
 * @param object $cm
 * @param object $course
 */
function pcast_display_episode_comments($episode, $cm, $course) {

    global $CFG;
    $html = '';

    if ($episode->approved) {
        if ($episode->userscancomment) {
            // Get episode comments and display the comment box.
            $context = context_module::instance($cm->id);

            // Generate comment box using API.
            if (!empty($CFG->usecomments)) {
                require_once($CFG->dirroot . '/comment/lib.php');
                $cmt = new stdClass();
                $cmt->component = 'pcast';
                $cmt->context  = $context;
                $cmt->course   = $course;
                $cmt->cm       = $cm;
                $cmt->area     = 'pcast_episode';
                $cmt->itemid   = $episode->id;
                $cmt->showcount = true;
                $comment = new comment($cmt);
                $html = html_writer::tag('div', $comment->output(true), ['class' => 'pcast-comments']);
            }
            // Trigger comment viewed event.
            $params = ['context' => $context, 'objectid' => $episode->id];
            $event = \mod_pcast\event\comments_viewed::create($params);
            $event->add_record_snapshot('pcast_episodes', $episode);
            $event->trigger();
        }
    } else {
        $html = html_writer::tag('div', get_string("nocommentuntilapproved", "pcast"), ['class' => 'pcast-episode-notice']);
    }

    echo $html;
}

/**
 * Displays the ratings for a specific episode
 * @param object $episode
 * @param object $cm
 * @param object $course
 */
function pcast_display_episode_ratings($episode, $cm, $course) {

    global $CFG, $USER, $DB, $OUTPUT;

    if ($episode->approved) {
        $sql = pcast_get_episode_sql();
        $sql .= " WHERE p.id = ?";
        $episodes = $DB->get_records_sql($sql, ['id' => $episode->id]);
        $context = context_module::instance($cm->id);

        // Load ratings.
        require_once($CFG->dirroot . '/rating/lib.php');
        if ($episode->assessed != RATING_AGGREGATE_NONE) {
            $ratingoptions = new stdClass();
            $ratingoptions->component = 'mod_pcast';
            $ratingoptions->context = $context;
            $ratingoptions->items = $episodes;
            $ratingoptions->aggregate = $episode->assessed; // The aggregation method.
            $ratingoptions->scaleid = $episode->scale;
            $ratingoptions->userid = $USER->id;
            $ratingoptions->assesstimestart = $episode->assesstimestart;
            $ratingoptions->assesstimefinish = $episode->assesstimefinish;
            $ratingoptions->ratingarea = 'episode';
            $ratingoptions->returnurl = new moodle_url(
                '/mod/pcast/showepisode.php',
                ['eid' => $episode->id, 'mode' => PCAST_EPISODE_COMMENT_AND_RATE]
            );

            $rm = new rating_manager();
            $allepisodes = $rm->get_ratings($ratingoptions);
        }
        foreach ($allepisodes as $thisepisode) {
            if (!empty($thisepisode->rating)) {
                echo html_writer::tag('div', $OUTPUT->render($thisepisode->rating), ['class' => 'pcast-episode-rating']);
            }
        }

        // Trigger ratings viewed event.
        $params = ['context' => $context, 'objectid' => $episode->id];
        $event = \mod_pcast\event\ratings_viewed::create($params);
        $event->add_record_snapshot('pcast_episodes', $episode);
        $event->trigger();
    } else {
        echo html_writer::tag('div', get_string("noratinguntilapproved", "pcast"), ['class' => 'pcast-episode-notice']);
    }
}

/**
 * Get the total number of views for a specific episode
 * @param object $episode
 * @return string
 */
function pcast_get_episode_view_count($episode) {
    global $DB;
    $count = 0;
    // Get all views.
    if (!$views = $DB->get_records("pcast_views", ["episodeid" => $episode->id])) {
        // No views.
        return $count;
    } else {
        foreach ($views as $view) {
            // Total up the views.
            $count += (int)$view->views;
        }
    }
    return $count;
}

/**
 * Get the total number of comments for a specific episode
 * @param object $episode
 * @param object $cm
 * @return string
 */
function pcast_get_episode_comment_count($episode, $cm) {
    global $DB;
    $context = context_module::instance($cm->id);
    if (
        $count = $DB->count_records(
            'comments',
            [
                'itemid' => $episode->id,
                'commentarea' => 'pcast_episode',
                'contextid' => $context->id,
            ],
        )
    ) {
        return $count;
    } else {
        return 0;
    }
}

/**
 * Get the total number of ratings for a specific episode
 * @param object $episode
 * @param object $cm
 * @return string
 */
function pcast_get_episode_rating_count($episode, $cm) {

    global $DB;
    $context = context_module::instance($cm->id);
    if (
        $count = $DB->count_records(
            'rating',
            [
                'itemid' => $episode->id,
                'scaleid' => $episode->scale,
                'contextid' => $context->id,
            ],
        )
    ) {
        return $count;
    } else {
        return 0;
    }
}

/**
 * Print the podcast attachment and the media player if appropriate
 *
 * @param object $episode
 * @param object $cm
 * @return string image string or nothing depending on $type param
 */
function pcast_display_mediafile_link($episode, $cm) {

    global $CFG, $OUTPUT;

    if (!$context = context_module::instance($cm->id)) {
        return '';
    }

    $fs = get_file_storage();

    if ($files = $fs->get_area_files($context->id, 'mod_pcast', 'episode', $episode->id, "timemodified", false)) {
        foreach ($files as $file) {
            $filename = $file->get_filename();
            $mimetype = $file->get_mimetype();
            $iconimage = $OUTPUT->image_icon(file_mimetype_icon($mimetype), $mimetype);
            $path = url::make_pluginfile_url(
                contextid: $context->id,
                component: 'mod_pcast',
                area: 'episode',
                itemid: $episode->id,
                pathname: '/',
                filename: $filename
            )->out();
        }
    }

    $templink = get_string('nopcastmediafile', 'pcast');
    // Make sure there is actually an attachment before trying to render the file link and player.
    if (!empty($filename)) {
        $out = html_writer::start_tag('div', ['class' => 'pcast-media']);
        $out .= html_writer::tag('a', $iconimage, ['href' => $path]); // Icon.
        $out .= html_writer::tag('a', s($filename), ['href' => $path]); // File.
        $out .= html_writer::end_tag('div');

        $templink = format_text($out, FORMAT_HTML, ['context' => $context]);
    }
    return $templink;
}

/**
 * Helper function used to generate allowed file types.
 * @param object $pcast
 * @return array
 */
function pcast_get_supported_file_types($pcast) {
    $defaultallowed = ['html_audio', 'web_audio', 'html_video', 'web_video'];
    $filetypesutil = new \core_form\filetypes_util();

    if (empty($pcast->allowedfiletypes)) {
            // If not configured, limit to audio / video only.
        return $filetypesutil->normalize_file_types($defaultallowed);
    }
    // Use the teachers list.
    return $filetypesutil->normalize_file_types($pcast->allowedfiletypes);
}

/**
 * Helper function, builds an array of categories.
 * @todo cache results using MUC.
 * @return array
 */
function pcast_get_categories() {
    global $DB;
    $selectelements = [];
    $topcat = [];
    // Get categories.
    if ($topcategories = $DB->get_records("pcast_itunes_categories")) {
        // Construct an array of top categories.
        foreach ($topcategories as $topcategory) {
            $value = (int)$topcategory->id * 1000;
            $topcat[(int)$value] = $topcategory->name;
            $selectelements[$topcategory->name] = [];
        }
    }
    if ($nestedcategories = $DB->get_records("pcast_itunes_nested_cat")) {
        foreach ($nestedcategories as $nestedcategory) {
            $value = (int)$nestedcategory->topcategoryid * 1000;
            $topcatname = $topcat[$value];
            $value = $value + (int)$nestedcategory->id;

            $selectelements[$topcatname][$value] = $nestedcategory->name;
        }
    }
    return $selectelements;
}

/**
 * Helper function used to create paging bars.
 * @param object $pcast
 * @param object $cm
 * @param int $count
 * @param int $page
 * @param int $mode
 * @param string $hook
 * @param string $sortkey
 * @param string $sortorder
 */
function pcast_display_paging_bar($pcast, $cm, $count, $page, $mode, $hook, $sortkey = '', $sortorder = '') {
    global $OUTPUT;
    if ($count > $pcast->episodesperpage) {
        // Print a paging bar here.
        $url = new moodle_url(
            '/mod/pcast/view.php',
            [
                'id' => $cm->id,
                'mode' => $mode,
                'hook' => $hook,
                'sortkey' => $sortkey,
                'sortorder' => $sortorder,
            ]
        );

        echo html_writer::start_tag('div', ['class' => 'pcast-paging']);
        echo $OUTPUT->paging_bar($count, $page, $pcast->episodesperpage, $url);
        echo html_writer::end_tag('div');
    }
}


/**
 * Returns pcast episodes tagged with a specified tag.
 *
 * This is a callback used by the tag area mod_pcast/pcast_episodes to search for pcast episodes
 * tagged with a specific tag.
 *
 * @param core_tag_tag $tag
 * @param bool $exclusivemode if set to true it means that no other entities tagged with this tag
 *             are displayed on the page and the per-page limit may be bigger
 * @param int $fromctx context id where the link was displayed, may be used by callbacks
 *            to display items in the same context first
 * @param int $ctx context id where to search for records
 * @param bool $rec search in subcontexts as well
 * @param int $page 0-based number of page being displayed
 * @return \core_tag\output\tagindex
 */
function mod_pcast_get_tagged_episodes($tag, $exclusivemode = false, $fromctx = 0, $ctx = 0, $rec = 1, $page = 0) {
    global $OUTPUT;
    $perpage = $exclusivemode ? 20 : 5;

    // Build the SQL query.
    $ctxselect = context_helper::get_preload_record_columns_sql('ctx');
    $query = "SELECT pe.id, pe.name, pe.pcastid, pe.approved,
                    cm.id AS cmid, c.id AS courseid, c.shortname, c.fullname, $ctxselect
                FROM {pcast_episodes} pe
                JOIN {pcast} p ON p.id = pe.pcastid
                JOIN {modules} m ON m.name='pcast'
                JOIN {course_modules} cm ON cm.module = m.id AND cm.instance = p.id
                JOIN {tag_instance} tt ON pe.id = tt.itemid
                JOIN {course} c ON cm.course = c.id
                JOIN {context} ctx ON ctx.instanceid = cm.id AND ctx.contextlevel = :coursemodulecontextlevel
               WHERE tt.itemtype = :itemtype AND tt.tagid = :tagid AND tt.component = :component
                 AND cm.deletioninprogress = 0
                 AND pe.id %ITEMFILTER% AND c.id %COURSEFILTER%";

    $params = ['itemtype' => 'pcast_episodes', 'tagid' => $tag->id, 'component' => 'mod_pcast',
                    'coursemodulecontextlevel' => CONTEXT_MODULE, ];

    if ($ctx) {
        $context = $ctx ? context::instance_by_id($ctx) : context_system::instance();
        $query .= $rec ? ' AND (ctx.id = :contextid OR ctx.path LIKE :path)' : ' AND ctx.id = :contextid';
        $params['contextid'] = $context->id;
        $params['path'] = $context->path . '/%';
    }

    $query .= " ORDER BY ";
    if ($fromctx) {
        // In order-clause specify that modules from inside "fromctx" context should be returned first.
        $fromcontext = context::instance_by_id($fromctx);
        $query .= ' (CASE WHEN ctx.id = :fromcontextid OR ctx.path LIKE :frompath THEN 0 ELSE 1 END),';
        $params['fromcontextid'] = $fromcontext->id;
        $params['frompath'] = $fromcontext->path . '/%';
    }
    $query .= ' c.sortorder, cm.id, pe.id';

    $totalpages = $page + 1;

    // Use core_tag_index_builder to build and filter the list of items.
    $builder = new core_tag_index_builder('mod_pcast', 'pcast_episodes', $query, $params, $page * $perpage, $perpage + 1);
    while ($item = $builder->has_item_that_needs_access_check()) {
        context_helper::preload_from_record($item);
        $courseid = $item->courseid;
        if (!$builder->can_access_course($courseid)) {
            $builder->set_accessible($item, false);
            continue;
        }
        $modinfo = get_fast_modinfo($builder->get_course($courseid));
        // Set accessibility of this item and all other items in the same course.
        $builder->walk(function ($taggeditem) use ($courseid, $modinfo, $builder) {
            if ($taggeditem->courseid == $courseid) {
                $accessible = false;
                if (($cm = $modinfo->get_cm($taggeditem->cmid)) && $cm->uservisible) {
                    if ($taggeditem->approved) {
                        $accessible = true;
                    } else {
                        $accessible = has_capability('mod/pcast:approve', context_module::instance($cm->id));
                    }
                }
                $builder->set_accessible($taggeditem, $accessible);
            }
        });
    }

    $items = $builder->get_items();
    if (count($items) > $perpage) {
        $totalpages = $page + 2; // We don't need exact page count, just indicate that the next page exists.
        array_pop($items);
    }

    // Build the display contents.
    if ($items) {
        $tagfeed = new core_tag\output\tagfeed();
        foreach ($items as $item) {
            context_helper::preload_from_record($item);
            $modinfo = get_fast_modinfo($item->courseid);
            $cm = $modinfo->get_cm($item->cmid);
            $pageurl = new moodle_url('/mod/pcast/showepisode.php', ['eid' => $item->id]);
            $pagename = format_string($item->name, true, ['context' => context_module::instance($item->cmid)]);
            $pagename = html_writer::link($pageurl, $pagename);
            $courseurl = course_get_url($item->courseid, $cm->sectionnum);
            $cmname = html_writer::link($cm->url, $cm->get_formatted_name());
            $coursename = format_string($item->fullname, true, ['context' => context_course::instance($item->courseid)]);
            $coursename = html_writer::link($courseurl, $coursename);
            $icon = html_writer::link($pageurl, html_writer::empty_tag('img', ['src' => $cm->get_icon_url()]));
            $tagfeed->add($icon, $pagename, $cmname . '<br>' . $coursename);
        }

        $content = $OUTPUT->render_from_template('core_tag/tagfeed', $tagfeed->export_for_template($OUTPUT));

        return new core_tag\output\tagindex(
            $tag,
            'mod_pcast',
            'pcast_episodes',
            $content,
            $exclusivemode,
            $fromctx,
            $ctx,
            $rec,
            $page,
            $totalpages
        );
    }
}

/**
 * Class representing the virtual node with all itemids in the file browser
 *
 * @category  files
 * @copyright 2012 Stephen Bourget
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
class pcast_file_info_container extends file_info {
    /** @var file_browser */
    protected $browser;
    /** @var stdClass */
    protected $course;
    /** @var stdClass */
    protected $cm;
    /** @var string */
    protected $component;
    /** @var stdClass */
    protected $context;
    /** @var array */
    protected $areas;
    /** @var string */
    protected $filearea;

    /**
     * Constructor (in case you did not realize it ;-)
     *
     * @param file_browser $browser
     * @param stdClass $course
     * @param stdClass $cm
     * @param stdClass $context
     * @param array $areas
     * @param string $filearea
     */
    public function __construct($browser, $course, $cm, $context, $areas, $filearea) {
        parent::__construct($browser, $context);
        $this->browser = $browser;
        $this->course = $course;
        $this->cm = $cm;
        $this->component = 'mod_pcast';
        $this->context = $context;
        $this->areas = $areas;
        $this->filearea = $filearea;
    }

    /**
     * Helper function that loads parameters.
     * @return array with keys contextid, filearea, itemid, filepath and filename
     */
    public function get_params() {
        return [
            'contextid' => $this->context->id,
            'component' => $this->component,
            'filearea' => $this->filearea,
            'itemid' => null,
            'filepath' => null,
            'filename' => null,
        ];
    }

    /**
     * Can new files or directories be added via the file browser
     *
     * @return bool
     */
    public function is_writable() {
        return false;
    }

    /**
     * Should this node be considered as a folder in the file browser
     *
     * @return bool
     */
    public function is_directory() {
        return true;
    }

    /**
     * Returns localised visible name of this node
     *
     * @return string
     */
    public function get_visible_name() {
        return $this->areas[$this->filearea];
    }

    /**
     * Returns list of children nodes
     *
     * @return array of file_info instances
     */
    public function get_children() {
        global $DB;

        $sql = "SELECT DISTINCT f.itemid, pe.name
                  FROM {files} f
                  JOIN {modules} m ON (m.name = 'pcast' AND m.visible = 1)
                  JOIN {course_modules} cm ON (cm.module = m.id AND cm.id = ?)
                  JOIN {pcast} p ON p.id = cm.instance
                  JOIN {pcast_episodes} pe ON (pe.pcastid = p.id AND pe.id = f.itemid)
                 WHERE f.contextid = ? AND f.component = ? AND f.filearea = ?
              ORDER BY pe.name, f.itemid";
        $params = [$this->context->instanceid, $this->context->id, $this->component, $this->filearea];

        $rs = $DB->get_recordset_sql($sql, $params);
        $children = [];
        foreach ($rs as $file) {
            if ($child = $this->browser->get_file_info($this->context, 'mod_pcast', $this->filearea, $file->itemid)) {
                $children[] = $child;
            }
        }
        $rs->close();

        return $children;
    }

    /**
     * Returns parent file_info instance
     *
     * @return file_info or null for root
     */
    public function get_parent() {
        return $this->browser->get_file_info($this->context);
    }
}
