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

/**
 * @package    block_ned_teacher_tools
 * @copyright  Michael Gardener <mgardener@cissq.com>
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

use block_ned_teacher_tools as NED;
use block_ned_teacher_tools\shared_lib as SH;
use block_ned_teacher_tools\deadline_manager as DM;
use block_ned_teacher_tools\output\menu_bar as MB;
use local_kica as KICA;
use local_ned_controller\marking_manager\marking_manager as MM;
use local_ned_controller\mod_assign\assign as ned_assign;
use local_ned_controller\tt_config_manager as CM;

require_once(__DIR__ . '/common_lib.php');
require_once(__DIR__ . '/local_lib.php');
require_once(__DIR__ . '/common_data_utils.php');
require_once($CFG->dirroot . '/grade/querylib.php');
require_once($CFG->dirroot . '/lib/gradelib.php');
require_once($CFG->dirroot . '/lib/grade/constants.php');
require_once($CFG->dirroot . '/lib/grade/grade_grade.php');
require_once($CFG->dirroot . '/lib/grade/grade_item.php');
require_once($CFG->dirroot . '/mod/assignment/lib.php');
require_once($CFG->dirroot . '/grade/grading/lib.php');
require_once($CFG->dirroot . '/group/lib.php');
require_once(NED\DIRROOT . '/lib/grouplib.php');
require_once(NED\DIRROOT . '/lib/enrollib.php');

/**
 * @param        $assignment
 * @param        $graded
 * @param        $students
 * @param string $show
 * @param bool   $extra
 * @param \stdClass $instance
 *
 * @return int
 */
function block_ned_teacher_tools_assignment_count_ungraded($assignment, $graded, $students, $show='unmarked', $extra=false, $instance=null) {
    global $DB;

    $studentlist = implode(',', array_keys($students));
    if (empty($studentlist)) {
        return 0;
    }
    $subtable = 'assignment_submissions';
    if (($show == 'unmarked') || ($show == 'all')) {
        if ($instance->var4 == 1) {
            $select = '(assignment = ' . $assignment . ') AND (userid in (' . $studentlist . ')) AND ' .
                '(timemarked < timemodified) AND (timemodified > 0) AND data2="submitted"';
        } else {
            $select = '(assignment = ' . $assignment . ') AND (userid in (' . $studentlist . ')) AND ' .
                '(timemarked < timemodified) AND (timemodified > 0)';
        }
        return $DB->count_records_select($subtable, $select, array(), 'COUNT(DISTINCT userid)');
    } else if ($show == 'marked') {
        if ($instance->var4 == 1) {
            $select = '(assignment = ' . $assignment . ') AND (userid in (' . $studentlist . ')) AND ' .
                '(timemarked >= timemodified) AND (timemodified > 0) AND data2="submitted"';
        } else {
            $select = '(assignment = ' . $assignment . ') AND (userid in (' . $studentlist . ')) AND ' .
                '(timemarked >= timemodified) AND (timemodified > 0)';
        }
        $marked = $DB->count_records_select($subtable, $select, array(), 'COUNT(DISTINCT userid)');
        return $marked;
    } else if ($show == 'unsubmitted') {
        if ($instance->var4) {
            $select = '(assignment = ' . $assignment . ') AND (userid in (' . $studentlist . ')) AND ' .
                '(timemodified > 0) AND data2="submitted"';
        } else {
            $select = '(assignment = ' . $assignment . ') AND (userid in (' . $studentlist . ')) AND ' .
                '(timemodified > 0)';
        }

        $subbed = $DB->count_records_select($subtable, $select, array(), 'COUNT(DISTINCT userid)');
        $unsubbed = abs(count($students) - $subbed);
        return ($unsubbed);
    } elseif ($show == 'saved' || $show == 'draft') {
        if ($instance->var4) {
            $select = '(assignment = ' . $assignment . ') AND (userid in (' . $studentlist . ')) AND ' .
                '(timemodified > 0) AND data2="" ';
            $saved = $DB->count_records_select($subtable, $select, array(), 'COUNT(DISTINCT userid)');
            return $saved;
        } else {
            return 0;
        }
    } else {
        return 0;
    }
}

function block_ned_teacher_tools_assign_count_ungraded($assign, $graded, $students, $show='unmarked', $extra=false, $instance=null, $keepseparate=1) {
    global $DB;

    $var = array(
        'unmarked' => 0,
        'marked' => 0,
        'unsubmitted' => 0,
        'saved' => 0,
    );

    $studentlist = implode(',', array_keys($students));

    $instance = $DB->get_record('assign', array('id' => $assign));

    if (empty($studentlist)) {
        return $var;
    }

    if (!$keepseparate) {
        $showdraft = false;
    } else if ($instance->submissiondrafts) {
        $showdraft = true;
    } else {
        $showdraft = false;
    }
    $comentjoin = '';
    $comentwhereunmarked = '';
    $comentwhernmarked = '';

    if ($instance->grade == 0) {
        $comentjoin = "LEFT JOIN {assignfeedback_comments} f ON g.assignment = f.assignment AND g.id = f.grade";
        $comentwhereunmarked = " AND (f.commenttext = '' OR f.commenttext IS NULL)";
        $comentwhernmarked = " AND f.commenttext <> ''";
    }

    $sql = "SELECT COUNT(DISTINCT s.id)
                  FROM {assign_submission} s
             LEFT JOIN {assign_grades} g
                    ON (s.assignment=g.assignment 
                   AND s.userid=g.userid 
                   AND s.attemptnumber = g.attemptnumber)
                       $comentjoin
                 WHERE s.assignment=$assign
                   AND (s.userid IN ($studentlist))
                   AND s.status IN ('submitted', 'draft')
                   AND ((g.grade is null OR g.grade = -1) OR g.timemodified < s.timemodified)
                   AND s.latest = 1
                       $comentwhereunmarked";

    $var['unmarked'] = $DB->count_records_sql($sql);

    // Marked.
    if ($instance->grade != 0) {
        $sqlunmarked = "SELECT DISTINCT s.userid
                          FROM {assign_submission} s
                     LEFT JOIN {assign_grades} g 
                            ON (s.assignment=g.assignment
                           AND s.userid=g.userid 
                           AND s.attemptnumber = g.attemptnumber)
                         WHERE s.assignment=$assign
                           AND (s.userid IN ($studentlist))
                           AND s.status='submitted'
                           AND g.grade IS NULL";

        $students = explode(',', $studentlist);
        if ($unmarkedstus = $DB->get_records_sql($sqlunmarked)) {
            foreach ($unmarkedstus as $unmarkedstu) {
                $students = array_diff($students, array($unmarkedstu->userid));
            }
        }
        $studentlistmarked = implode(',', $students);
    }

    if (!empty($studentlistmarked)) {
        if ($instance->grade == 0) {
            $sql = "SELECT COUNT(DISTINCT s.userid)
              FROM {assign_submission} s
         LEFT JOIN {assign_grades} g 
                ON (s.assignment=g.assignment and s.userid=g.userid
               AND s.attemptnumber = g.attemptnumber)
                   $comentjoin
             WHERE s.assignment=$assign
               AND s.userid in ($studentlistmarked)
               AND s.status IN ('submitted', 'resub', 'new')                    
                   $comentwhernmarked";
        } else {
            $sql = "SELECT COUNT(DISTINCT s.userid)
              FROM {assign_submission} s
         LEFT JOIN {assign_grades} g 
                ON (s.assignment=g.assignment and s.userid=g.userid
               AND s.attemptnumber = g.attemptnumber)
             WHERE ((s.assignment=$assign
               AND (s.userid in ($studentlistmarked))
               AND s.status IN ('submitted', 'resub')
               AND g.grade is not null  AND g.grade <> -1)
                OR (s.assignment=$assign
               AND (s.userid in ($studentlistmarked))
               AND s.status='draft'
               AND g.grade is not null
               AND g.grade <> -1
               AND g.timemodified > s.timemodified))";
        }
    }

    $var['marked'] = $DB->count_records_sql($sql);


    // Unsubmitted.
    $sql = "SELECT COUNT(DISTINCT userid)
              FROM {assign_submission}
             WHERE assignment=$assign AND (userid in ($studentlist)) AND status='submitted'";
    $subbed = $DB->count_records_sql($sql);
    $unsubbed = abs(count($students) - $subbed);
    $var['unsubmitted'] = $unsubbed;

    // Saved.
    if ($showdraft) {
        $sql = "SELECT COUNT(DISTINCT s.id)
              FROM {assign_submission} s
         LEFT JOIN {assign_grades} g
                ON s.assignment = g.assignment
               AND s.userid = g.userid
               AND s.attemptnumber = g.attemptnumber
             WHERE s.assignment = $assign
               AND s.userid IN ($studentlist)
               AND s.status = 'draft'
               AND (s.timemodified >= g.timemodified OR g.grade IS NULL)";

        $var['saved'] = $DB->count_records_sql($sql);
    }

    return $var;
}

function block_ned_teacher_tools_quiz_count_ungraded($quizid, $graded, $students, $show='unmarked', $extra=false, $instance=null, $keepseparate=1) {
    global $DB;

    $var = array(
        'unmarked' => 0,
        'marked' => 0,
        'unsubmitted' => 0,
        'saved' => 0,
    );

    $studentlist = implode(',', array_keys($students));

    if (empty($studentlist)) {
        return $var;
    }

    // Unmarked.
    $sqlgradablequiz = "SELECT qs.id,
                               q.qtype
                            FROM {quiz_slots} qs
                            JOIN {question_references} AS qre 
                                ON qre.itemid = qs.id
                            JOIN {question_bank_entries} AS qbe 
                                ON qbe.id = qre.questionbankentryid
                            JOIN {question_versions} AS qve 
                                ON qve.questionbankentryid = qbe.id
                            JOIN {question} AS q 
                                ON q.id = qve.questionid    
                          
                         WHERE qs.quizid = ?
                           AND q.qtype IN ('essay', 'shortanswer')
                         UNION 
                        SELECT q.id,
                               q.qtype 
                          FROM {question} q
                         WHERE q.category IN (SELECT DISTINCT q2.category 
                                                FROM {quiz_slots} qs2
                                                JOIN {question_references} AS qre2 
                                                    ON qre2.itemid = qs2.id
                                                JOIN {question_bank_entries} AS qbe2 
                                                    ON qbe2.id = qre2.questionbankentryid
                                                JOIN {question_versions} AS qve2 
                                                    ON qve2.questionbankentryid = qbe2.id
                                                JOIN {question} AS q2 
                                                    ON q2.id = qve2.questionid    
                                               WHERE qs2.quizid = ?
                                                 AND q2.qtype = 'random') 
                           AND q.qtype IN ('essay', 'shortanswer')";



    if ($DB->record_exists_sql($sqlgradablequiz, array($instance->id, $instance->id))) {
        $sql = "SELECT COUNT(DISTINCT qa.userid)
                  FROM {quiz_attempts} qa
                 WHERE qa.quiz = ?
                   AND qa.state = 'finished'
                   AND qa.userid IN ($studentlist)
                   AND qa.sumgrades IS NULL";

        $var['unmarked'] = $DB->count_records_sql($sql, array($quizid));
    }

    // Marked.
    $sql = "SELECT COUNT(DISTINCT qa.userid)
              FROM {quiz_attempts} qa
             WHERE qa.quiz = ?
               AND qa.state = 'finished'
               AND qa.userid IN ($studentlist)
               AND qa.sumgrades >= 0";

    $var['marked'] = $DB->count_records_sql($sql, array($quizid));

    // Unsubmitted.
    $sql = "SELECT DISTINCT qa.userid
              FROM {quiz_attempts} qa
             WHERE qa.quiz = ?
               AND qa.state = 'finished'
               AND qa.userid IN ($studentlist)
               AND qa.sumgrades >= 0";

    if ($attempts = $DB->get_records_sql($sql, array($quizid))) {
        $unsubmitted = array_diff(array_keys($students), array_keys($attempts));
        $var['unsubmitted'] = count($unsubmitted);
    } else {
        $var['unsubmitted'] = count($students);
    }
    return $var;
}

function block_ned_teacher_tools_journal_count_ungraded($journalid, $graded, $students, $show='unmarked',
                                               $extra=false, $instance=null, $keepseparate=1) {
    global $DB;

    $var = array(
        'unmarked' => 0,
        'marked' => 0,
        'unsubmitted' => 0,
        'saved' => 0,
    );

    $studentlist = implode(',', array_keys($students));

    if (empty($studentlist)) {
        return $var;
    }

    // Unmarked.
    if ($instance->grade == 0) {
        $sql = "SELECT COUNT(1) 
                  FROM {journal_entries} j 
                 WHERE j.journal = ? 
                   AND j.entrycomment IS NULL 
                   AND j.userid IN ($studentlist)";
    } else {
        $sql = "SELECT COUNT(1) 
                  FROM {journal_entries} j 
                 WHERE j.journal = ? 
                   AND j.rating IS NULL 
                   AND j.userid IN ($studentlist)";
    }
    $var['unmarked'] = $DB->count_records_sql($sql, array($journalid));

    // Marked.
    if ($instance->grade == 0) {
        $sql = "SELECT COUNT(1) 
                  FROM {journal_entries} j 
                 WHERE j.journal = ? 
                   AND j.entrycomment IS NOT NULL 
                   AND j.userid IN ($studentlist)";
    } else {
        $sql = "SELECT COUNT(1) 
                  FROM {journal_entries} j 
                 WHERE j.journal = ? 
                   AND j.rating IS NOT NULL
                   AND j.userid IN ($studentlist)";
    }
    $var['marked'] = $DB->count_records_sql($sql, array($journalid));

    // Unsubmitted.
    if ($instance->grade == 0) {
        $sql = "SELECT j.userid 
                  FROM {journal_entries} j 
                 WHERE j.journal = ? 
                   AND j.entrycomment IS NOT NULL 
                   AND j.userid IN ($studentlist)";
    } else {
        $sql = "SELECT j.userid
                  FROM {journal_entries} j 
                 WHERE j.journal = ? 
                   AND j.rating IS NOT NULL
                   AND j.userid IN ($studentlist)";
    }

    if ($attempts = $DB->get_records_sql($sql, array($journalid))) {
        $unsubmitted = array_diff(array_keys($students), array_keys($attempts));
        $var['unsubmitted'] = count($unsubmitted);
    } else {
        $var['unsubmitted'] = count($students);
    }

    return $var;
}

function block_ned_teacher_tools_assign_students_ungraded($assign, $graded, $students, $show='unmarked',
                                                    $extra=false, $instance=null, $sort=false, $keepseparate=1) {
    global $DB, $CFG;

    $studentlist = implode(',', array_keys($students));

    $instance = $DB->get_record('assign', array('id' => $assign));

    if (empty($studentlist)) {
        return 0;
    }

    if (!$keepseparate) {
        $showdraft = false;
    } else if ($instance->submissiondrafts) {
        $showdraft = true;
    } else {
        $showdraft = false;
    }

    $comentjoin = '';
    $comentwhereunmarked = '';

    if ($instance->grade == 0) {
        $comentjoin = "LEFT JOIN {assignfeedback_comments} f ON g.assignment = f.assignment AND g.id = f.grade";
        $comentwhereunmarked = " AND (f.commenttext = '' OR f.commenttext IS NULL)";
    }

    if (($show == 'unmarked') || ($show == 'all')) {
        if (!$showdraft) {
            $sql = "SELECT DISTINCT s.userid
                      FROM {assign_submission} s
                 LEFT JOIN {assign_grades} g 
                        ON (s.assignment=g.assignment
                       AND s.userid=g.userid 
                       AND s.attemptnumber = g.attemptnumber)
                           $comentjoin
                     WHERE s.assignment=$assign
                       AND (s.userid in ($studentlist))
                       AND s.status='submitted'
                       AND ((g.grade is null OR g.grade = -1) OR g.timemodified < s.timemodified)
                       AND s.latest = 1
                       $comentwhereunmarked";
        } else {
            $sql = "SELECT DISTINCT s.userid
                      FROM {assign_submission} s
                 LEFT JOIN {assign_grades} g 
                        ON (s.assignment=g.assignment
                       AND s.userid=g.userid 
                       AND s.attemptnumber = g.attemptnumber)
                           $comentjoin
                     WHERE s.assignment=$assign
                       AND (s.userid IN ($studentlist))
                       AND s.status  IN ('submitted', 'draft')
                       AND ((g.grade is null OR g.grade = -1) OR g.timemodified < s.timemodified)
                       AND s.latest = 1
                           $comentwhereunmarked";
        }

        if ($data = $DB->get_records_sql($sql)) {
            $arr = array();
            foreach ($data as $value) {
                $arr[] = $value->userid;
            }
            return $arr;
        } else {
            return false;
        }

    } else if ($show == 'marked') {

        $students = explode(',', $studentlist);
        if ($instance->grade != 0) {
            $sql = "SELECT s.userid
                     FROM {assign_submission} s
                LEFT JOIN {assign_grades} g 
                       ON (s.assignment=g.assignment
                      AND s.userid=g.userid 
                      AND  s.attemptnumber = g.attemptnumber)
                    WHERE s.assignment=$assign
                      AND (s.userid in ($studentlist))
                      AND s.status='submitted'
                      AND s.latest = 1
                      AND (g.grade is null  OR g.grade = -1)";

            if ($unmarkedstus = $DB->get_records_sql($sql)) {

                foreach ($unmarkedstus as $unmarkedstu) {
                    $students = array_diff($students, array($unmarkedstu->userid));
                }
            }

            $studentlist = implode(',', $students);
        }

        if ($instance->grade == 0) {
            $sql = "SELECT MAX(s.id) id,
                           s.userid
                      FROM {assign_submission} s
                 LEFT JOIN {assign_grades} g
                        ON (s.assignment=g.assignment AND s.userid=g.userid AND s.attemptnumber = g.attemptnumber)
                     WHERE s.assignment=$assign
                       AND s.userid IN ($studentlist)
                       AND g.grade = -1
                       AND s.latest = 1
                  GROUP BY s.userid";
        } else {
            $sql = "SELECT MAX(s.id) id,
                           s.userid
                      FROM {assign_submission} s
                 LEFT JOIN {assign_grades} g
                        ON (s.assignment=g.assignment AND s.userid=g.userid AND s.attemptnumber = g.attemptnumber)
                     WHERE s.assignment=$assign
                       AND (s.userid in ($studentlist))
                       AND g.grade is not null
                       AND g.grade <> -1
                       AND s.latest = 1
                  GROUP BY s.userid";
        }
        if ($data = $DB->get_records_sql($sql)) {
            if ($sort) {
                $arrids = array();
                $drafted = array();

                foreach ($data as $value) {
                    $arrids[] = $value->id;
                }

                // CHECK DRAFT is_Graded.
                $sqldraft = "SELECT s.id,
                                    s.timemodified  submissiontime,
                                    g.timemodified  gradetime
                               FROM {assign_submission} s
                          LEFT JOIN {assign_grades} g
                                 ON (s.assignment=g.assignment and s.userid=g.userid and s.attemptnumber = g.attemptnumber)
                              WHERE s.assignment = $assign
                                AND s.userid IN ($studentlist)
                                AND s.latest = 1
                                AND s.status = 'draft'";

                if ($draftgrades = $DB->get_records_sql($sqldraft)) {
                    foreach ($draftgrades as $draftgrade) {
                        if (($draftgrade == null) || ($draftgrade->submissiontime >= $draftgrade->gradetime)) {
                            $drafted[] = $draftgrade->id;
                        }
                    }
                    $arrids = array_diff($arrids, $drafted);
                }

                switch ($sort) {
                    case 'lowest':
                        $sqls = "SELECT s.userid
                                   FROM {assign_submission} s
                              LEFT JOIN {assign_grades} g
                                     ON (s.assignment = g.assignment AND s.userid = g.userid AND s.attemptnumber = g.attemptnumber)
                                  WHERE s.id IN (" . implode(',', $arrids) . ") AND s.latest = 1
                               ORDER BY g.grade ASC";
                        break;

                    case 'highest':
                        $sqls = "SELECT s.userid
                                   FROM {assign_submission} s
                              LEFT JOIN {assign_grades} g
                                     ON (s.assignment = g.assignment AND s.userid = g.userid AND s.attemptnumber = g.attemptnumber)
                                  WHERE s.id IN (" . implode(',', $arrids) . ") AND s.latest = 1
                               ORDER BY g.grade DESC";
                        break;

                    case 'date':
                        $sqls = "SELECT s.userid
                                   FROM {assign_submission} s
                                  WHERE s.id IN (" . implode(',', $arrids) . ") AND s.latest = 1
                               ORDER BY s.timemodified DESC";
                        break;

                    case 'alpha':
                        $sqls = "SELECT s.userid
                                   FROM {assign_submission} s
                             INNER JOIN {user AS u
                                     ON s.userid = u.id
                                  WHERE s.id IN (" . implode(',', $arrids) . ") AND s.latest = 1
                               ORDER BY u.lastname ASC";
                        break;
                }

                if (!empty($sqls) && $datas = $DB->get_records_sql($sqls)) {
                    $arr = array();
                    foreach ($datas as $value) {
                        $arr[] = $value->userid;
                    }

                    return $arr;
                } else {
                    return false;
                }
            }

            $arr = array();
            foreach ($data as $value) {
                $arr[] = $value->userid;
            }

            return $arr;
        } else {
            return false;
        }

    } else if ($show == 'unsubmitted') {
        $sql = "SELECT DISTINCT s.userid
                  FROM {assign_submission} s
                 WHERE assignment=$assign AND (userid in ($studentlist)) AND status='submitted' AND s.latest = 1";
        $subbed = $DB->get_records_sql($sql);

        $unsubmitted = array_diff(array_keys($students), array_keys($subbed));
        return $unsubmitted = array_values($unsubmitted);

    } else if ($show == 'saved' || $show == 'draft') {

        $sql = "SELECT DISTINCT s.userid
                  FROM {assign_submission} s
                 WHERE assignment=$assign AND (userid in ($studentlist)) AND status='draft' AND s.latest = 1";

        if ($data = $DB->get_records_sql($sql)) {
            $arr = array();
            foreach ($data as $value) {
                $arr[] = $value->userid;
            }
            return $arr;
        } else {
            return false;
        }
    } else {
        return 0;
    }
}

function block_ned_teacher_tools_assignment_oldest_ungraded($assignment) {
    global $CFG, $DB;

    $sql = 'SELECT MIN(timemodified) FROM ' . $CFG->prefix . 'assignment_submissions ' .
        'WHERE (assignment = ' . $assignment . ') AND (timemarked < timemodified) AND (timemodified > 0)';
    return $DB->get_field_sql($sql);
}

function block_ned_teacher_tools_assign_oldest_ungraded($assign) {
    global $CFG, $DB;

    $sql = "SELECT MIN(s.timemodified)
              FROM {assign_submission} s
              LEFT JOIN {assign_grades} g ON (s.assignment=g.assignment and s.userid=g.userid)
             WHERE s.assignment=$assign AND s.status='submitted' AND g.grade is null";
    return $DB->get_field_sql($sql);
}

function block_ned_teacher_tools_forum_count_ungraded($forumid, $graded, $students, $show='unmarked') {
    global $DB;

    $var = [
//        'unmarked' => 0,
//        'marked' => 0,
//        'unsubmitted' => 0,
        'saved' => 0,
    ];

    // Get students from forum_posts.
    $sql = "SELECT DISTINCT u.id
              FROM {forum_discussions} d
        INNER JOIN {forum_posts} p
                ON p.discussion = d.id
        INNER JOIN {user} u
                ON u.id = p.userid
             WHERE d.forum = ?";

    if ($fusers = $DB->get_records_sql($sql, array($forumid))) {
        foreach ($fusers as $key => $user) {
            if (!array_key_exists($key, $students)) {
                unset($fusers[$key]);
            }
        }
    }

    // Unmarked.
    if (empty($graded) && !empty($fusers)) {
        $var['unmarked'] = count($fusers);
    } else if (empty($fusers)) {
        $var['unmarked'] = 0;
    } else {
        $var['unmarked'] = (count($fusers) - count($graded));
    }


    // Marked.
    $var['marked'] = count($graded);

    // Unsubmitted
    $numuns = count($students) - count($fusers);
    $var['unsubmitted'] = max(0, $numuns);

    return $var;
}

/**
 * @param        $course
 * @param        $mod
 * @param string $info
 * @param bool   $sort
 *
 * @return int|array|null|\mixed
 */
function block_ned_teacher_tools_count_unmarked_students(&$course, $mod, $info='unmarked', $sort=false) {

    global $CFG, $DB, $MB;

    $keepseparate = get_config('block_ned_teacher_tools', 'keepseparate');

    if (isset($MB) && $MB->courseid == $course->id){
        $students = $MB->students;
    } else {
        $show_inactive = NED\check_show_inactive_with_cache(null, $course->id);
        [$allstudents, $groups] = block_ned_teacher_tools_get_users_and_groups($course, false, !$show_inactive);
        $groupid = NED\check_group_with_cache(null, $course->id, $groups);
        $students = NED\choose_students_by_group($allstudents, $groups, $groupid);
    }
    return $students;
}

function block_ned_teacher_tools_count_unmarked_activities(&$course, $info='unmarked', $module='', $userid=0, $hypercron_mode=false, $groupid=0) {

    global $DB;

    $var = array(
        'unmarked' => 0,
        'marked' => 0,
        'unsubmitted' => 0,
        'saved' => 0,
    );

    $context = context_course::instance($course->id);
    $includeorphaned = get_config('block_ned_teacher_tools', 'include_orphaned');
    if ($blockconfig = block_ned_teacher_tools_get_block_config ($course->id)) {
        if (isset($blockconfig->include_orphaned)) {
            $includeorphaned = $blockconfig->include_orphaned;
        }
    }

    // FIND CURRENT WEEK.
    $courseformatoptions = course_get_format($course)->get_format_options();
    $courseformat = course_get_format($course)->get_format();
    if (isset($courseformatoptions['numsections'])) {
        $coursenumsections = $courseformatoptions['numsections'];
    } else {
        if (!$coursenumsections = $DB->count_records('course_sections', array('course' => $course->id))) {
            $coursenumsections = 10; // Default section number.
        }
    }

    if ($courseformat == 'weeks') {
        $timenow = time();
        $weekofseconds = 604800;

        // Calculate the current week based on today's date and the starting date of the course.
        $currentweek = ($timenow > $course->startdate) ? (int)((($timenow - $course->startdate) / $weekofseconds) + 1) : 0;
        $currentweek = min($currentweek, $coursenumsections);
        $upto = min($currentweek, $coursenumsections);
    } else {
        $upto = $coursenumsections;
    }

    // Array of functions to call for grading purposes for modules.
    $modgradesarray = block_ned_teacher_tools_supported_mods();

    $sections = $DB->get_records('course_sections', array('course' => $course->id), 'section ASC', 'section, sequence');

    $selectedsection = array();
    for ($i = 0; $i <= $upto; $i++) {
        $selectedsection[] = $i;
    }
    $selectedsection = range(0, $upto);
    if ($includeorphaned && (count($sections) > ($coursenumsections + 1))) {
        $selectedsection = array_merge($selectedsection, range($coursenumsections + 1, count($sections)-1));
    }

    $students = NED\get_students($course, $userid, $groupid);
    if (empty($students)) {
        return $var;
    }

    foreach ($selectedsection as $sectionnum) {
        $i = $sectionnum;
        if (!isset($sections[$i]) || !$sections[$i]->sequence){
            continue;
        }

        $section = $sections[$i];
        $sectionmods = explode(",", $section->sequence);
        foreach ($sectionmods as $sectionmod) {
            $mod = get_coursemodule_from_id('', $sectionmod, $course->id);
            if ( empty($mod) || !isset($modgradesarray[$mod->modname]) || ($module && ($module != $mod->modname)) ) {
                continue;
            }

            $mcontext = context_module::instance($mod->id);
            $instance = $DB->get_record("$mod->modname", array("id" => $mod->instance));
            if (($mod->modname == 'forum') && (($instance->assessed <= 0) || !has_capability('mod/forum:rate', $mcontext))){
                continue;
            }

            $gradefunction = NED\get_grade_function($mod);
            if (!$gradefunction){
                continue;
            }

            // Use the object function for fnassignments.

            $modgrades = new stdClass();
            if (!$modgrades->grades = $gradefunction($instance)) {
                $modgrades->grades = array();
            }
            if ($mod->modname == 'assign') {
                $sql = "SELECT asub.id,
                               asub.userid,
                               ag.grade
                          FROM {assign_submission} asub
                     LEFT JOIN {assign_grades} ag
                            ON asub.userid = ag.userid
                           AND asub.assignment = ag.assignment
                           AND asub.attemptnumber = ag.attemptnumber
                         WHERE asub.assignment = ?
                           AND asub.status = 'submitted'";

                if ($gradedsubmissions = $DB->get_records_sql($sql, array($instance->id))) {
                    foreach ($gradedsubmissions as $gradedsubmission) {
                        if (!$gradedsubmission->grade) {
                            if (isset($modgrades->grades[$gradedsubmission->userid])) {
                                unset($modgrades->grades[$gradedsubmission->userid]);
                            }
                        }
                    }
                }

            }

            // Store the number of ungraded entries for this group.
            if (is_array($modgrades->grades) && is_array($students)) {
                $gradedarray = array_intersect(array_keys($students), array_keys($modgrades->grades));
                $ungradedfunction = 'block_ned_teacher_tools_' . $mod->modname . '_count_ungraded';

                if (function_exists($ungradedfunction)) {
                    $extra = false;
                    $ung = $ungradedfunction($instance->id, $gradedarray, $students, $info, $extra, $instance, null);
                    $var['unmarked'] += $ung['unmarked'];
                    $var['marked'] += $ung['marked'];
                    $var['unsubmitted'] += $ung['unsubmitted'];
                    $var['saved'] += $ung['saved'];
                }
            }
        }
    }

    return $var;
}

function block_ned_teacher_tools_get_failing($course, $percent, $groupid=0) {
    global $DB;
    $blockconfig = block_ned_teacher_tools_get_block_config($course->id);

    $studentids = array();
    $students = NED\get_students($course, 0, $groupid);

    if (!empty($blockconfig->days)) {
        $lastweek = time() - (60 * 60 * 24 * $blockconfig->days);

        $sql = "SELECT ue.id, 
                       ue.userid 
                  FROM {user_enrolments} ue 
                  JOIN {enrol} e 
                    ON ue.enrolid = e.id 
                 WHERE e.courseid = ? 
                   AND ue.status = ? 
                   AND ue.timestart > ?";
        if ($newenrollments = $DB->get_records_sql($sql, array($course->id, 0, $lastweek))) {
            foreach ($newenrollments as $newenrollment) {
                if (isset($students[$newenrollment->userid])) {
                    unset($students[$newenrollment->userid]);
                }
            }
        }
    }

    // Students array is keyed on id.
    if ($students) {
        foreach ($students as $student) {
            $studentids[] = $student->id;
        }
    }

    $allgrades = grade_get_course_grades($course->id, $studentids);
    $grades = $allgrades->grades;

    $failing = array();

    foreach ($grades as $studentid => $gradeobj) {

        // Grab grade and convert to int (NULL -> 0).
        $grade = (int) $gradeobj->grade;

        if ($grade < $percent) {
            $failing[$studentid] = $students[$studentid];
        }
    }

    return $failing;
}

function block_ned_teacher_tools_count_failing($course, $percent, $groupid=0) {
    return count(block_ned_teacher_tools_get_failing($course, $percent, $groupid));
}

function block_ned_teacher_tools_get_notsubmittedany($course, $since, $count, $sections, $students=null, $groupid=0) {
    global $DB;

    // Grab modgradesarry.
    $modgradesarray = block_ned_teacher_tools_supported_mods();

    if (is_null($students)) {
        $students = NED\get_students($course, 0, $groupid);
    }

    if ($since) {
        $sql = "SELECT ue.id,
                       ue.userid
                  FROM {user_enrolments} ue
                  JOIN {enrol} e
                    ON ue.enrolid = e.id
                 WHERE e.courseid = ?
                   AND ue.status = ?
                   AND ue.timestart > ?";
        if ($newenrollments = $DB->get_records_sql($sql, array($course->id, 0, $since))) {
            foreach ($newenrollments as $newenrollment) {
                if (isset($students[$newenrollment->userid])) {
                    unset($students[$newenrollment->userid]);
                }
            }
        }
    }

    for ($i = 0; $i < count($sections); $i++) {
        if (isset($sections[$i])) {
            $section = $sections[$i];
            if ($section->sequence) {
                $sectionmods = explode(",", $section->sequence);
                foreach ($sectionmods as $sectionmod) {
                    $mod = get_coursemodule_from_id('', $sectionmod, $course->id);
                    if ($mod && isset($modgradesarray[$mod->modname])) {
                        // Build mod method.
                        $f = 'block_ned_teacher_tools_' . $mod->modname . '_get_notsubmittedany';
                        // Make sure function exists.
                        if (!function_exists($f)) {
                            continue;
                        }

                        // Grab list of students with submissions for this activity.
                        $studentswithsubmissions = $f($course->id, $mod->instance, $students, $since);
                        if ($studentswithsubmissions) {
                            $studentids = array_keys($students);
                            $swsids = array_keys($studentswithsubmissions);
                            foreach ($swsids as $id) {
                                unset($students[$id]);
                            }
                        }

                        // If all students have a submission, return null.
                        if (empty($students)) {
                            if ($count) {
                                return 0;
                            } else {
                                return null;
                            }
                        }
                    } // Wrong activity type.
                } // Move onto next sectionmod.
            } // Section has mods in it.
        } // Should always be true?.
    } // Next section.

    if ($count) {
        return count($students);
    } else {
        return $students;
    }
}

/**
 * @param int                  $userid
 * @param ned_assign | \assign $assign
 *
 * @return bool
 */
function block_ned_teacher_tools_is_graded($userid, $assign) {
    $grade = $assign->get_user_grade($userid, false);
    if ($grade) {
        return ($grade->grade !== null && $grade->grade >= 0);
    }
    return false;
}

/**
 * @param $userid
 * @param $grade
 * @param $gradingdisabled
 * @param ned_assign | \assign $assign
 *
 * @return \gradingform_instance|mixed|null
 */
function block_ned_teacher_tools_get_grading_instance($userid, $grade, $gradingdisabled, $assign) {
    global $CFG, $USER;

    $grademenu = make_grades_menu($assign->get_instance()->grade);

    $advancedgradingwarning = false;
    $gradingmanager = get_grading_manager($assign->get_context(), 'mod_assign', 'submissions');
    $gradinginstance = null;
    do {
        $gradingmethod = $gradingmanager->get_active_method();
        if (!$gradingmethod) break;

        try {
            $controller = $gradingmanager->get_controller($gradingmethod);
        } catch (\Exception){
            break;
        }

        if ($controller->is_form_available()) {
            $itemid = null;
            if ($grade) {
                $itemid = $grade->id;
            }
            if ($gradingdisabled && $itemid) {
                $gradinginstance = $controller->get_current_instance($USER->id, $itemid);
            } else if (!$gradingdisabled) {
                $instanceid = optional_param('advancedgradinginstanceid', 0, PARAM_INT);
                $gradinginstance = $controller->get_or_create_instance($instanceid,
                    $USER->id,
                    $itemid);
            }
        } else {
            $advancedgradingwarning = $controller->form_unavailable_notification();
        }
    } while (false);

    if ($gradinginstance) {
        $gradinginstance->get_controller()->set_grade_range($grademenu);
    }
    return $gradinginstance;
}


function block_ned_teacher_tools_load_plugins($subtype, $assign, $by_name=false, $check_visible=false) {
    global $CFG;
    $result = array();

    $names = core_component::get_plugin_list($subtype);

    foreach ($names as $name => $path) {
        if (file_exists($path . '/locallib.php')) {
            require_once($path . '/locallib.php');

            $shortsubtype = substr($subtype, strlen('assign'));
            $pluginclass = 'assign_' . $shortsubtype . '_' . $name;

            /** @var assign_plugin $plugin */
            $plugin = new $pluginclass($assign, $name);

            if ($check_visible){
                try {
                    if (!$plugin->is_enabled() || !$plugin->is_visible()){
                        continue;
                    }
                } catch (\Exception){
                    continue;
                }
            }

            if ($plugin instanceof assign_plugin) {
                if ($by_name){
                    $result[$pluginclass] = $plugin;
                } else {
                    $idx = $plugin->get_sort_order();
                    while (array_key_exists($idx, $result)) {
                        $idx++;
                    }
                    $result[$idx] = $plugin;
                }
            }
        }
    }

    if (!$by_name){
        ksort($result);
    }
    return $result;
}


/**
 * @param            $mform
 * @param ned_assign $assign
 * @param            $context
 * @param            $pageparams
 * @param bool       $gradingonly
 *
 * @return bool
 */
function block_ned_teacher_tools_process_save_grade(&$mform, $assign, $context, $pageparams, $gradingonly=true) {
    global $CFG;
    // Include grade form.
    require_once($CFG->dirroot . '/mod/assign/gradeform.php');

    if (!$gradingonly) {
        return true;
    }
    // Need submit permission to submit an assignment.
    require_capability('mod/assign:grade', $context);
    require_sesskey();

    $rownum = required_param('rownum', PARAM_INT);
    $useridlist = optional_param('useridlist', '', PARAM_TEXT);
    $attemptnumber = optional_param('attemptnumber', -1, PARAM_INT);
    $useridlistid = optional_param('useridlistid', time(), PARAM_INT);
    $userid = optional_param('userid', 0, PARAM_INT);
    $activitytype = optional_param('activity_type', 0, PARAM_TEXT);
//    $group = optional_param('group', 0, PARAM_INT);
    $participants = optional_param('participants', 0, PARAM_INT);

    if ($useridlist) {
        $useridlist = explode(',', $useridlist);
    } else {
        $useridlist = $assign->get_grading_userid_list2();
    }
    $last = false;
    $userid = $useridlist[$rownum] ?? $userid;
    if ($rownum == count($useridlist) - 1) {
        $last = true;
    }

    $data = new stdClass();

    $pageparams['rownum']     = $rownum;
    $pageparams['useridlist'] = $useridlist;
    $pageparams['useridlistid'] = $useridlistid;
    $pageparams['last']       = $last;
    $pageparams['savegrade']  = true;
    $pageparams['attemptnumber']  = $attemptnumber;
    $pageparams['activity_type']  = $activitytype;
    $pageparams['group']  = optional_param('group', NED\GROUP_ALL, PARAM_INT);;
    $pageparams['participants']  = $participants;

    $formparams = array($assign, $data, $pageparams);

    $mform = new mod_assign_grading_form_fn(null, $formparams, 'post', '', array('class' => 'gradeform unresponsive'));

    if ($formdata = $mform->get_data()) {
        $formdata->attemptnumber = $attemptnumber;
        return $assign->save_grade($userid, $formdata);
    }

    return false;
}

/**
 * @param      $mform
 * @param      $offset
 * @param ned_assign | \assign $assign
 * @param      $context
 * @param      $cm
 * @param      $course
 * @param      $pageparams
 * @param null $showattemptnumber
 *
 * @return string
 */
function block_ned_teacher_tools_view_single_grade_page($mform, $offset, $assign, $context,
                                                  $cm, $course, $pageparams, $showattemptnumber=null) {
    global $DB, $CFG, $MB, $MM;

    $o = '';
    if (!isset($MB)){
        $MB = new MB('', [], $course->id);
    }
    if (!isset($MM)){
        $MM = MM::get_MM_by_params($MB->parameters_for_MM(
            ['type' => $MB->get_pageparam('activity_type'), 'studuserid' => $MB->get_pageparam(MM::P_PARTICIPANTS)]
        ));
    }

    // Include grade form.
    require_once($CFG->dirroot . '/mod/assign/gradeform.php');

    // Need submit permission to submit an assignment.
    $readonly = false;
    if (! has_capability('mod/assign:grade', $context)) {
        if (has_capability('block/ned_teacher_tools:viewreadonly', $context)) {
            $readonly = true;
        } else {
            require_capability('mod/assign:grade', $context);
        }
    }

    $rownum = $pageparams['rownum'] + $offset;
    $userid = $MB->userid;
    $attemptnumber = optional_param('attemptnumber', -1, PARAM_INT);
    $activitytype = $pageparams['activity_type'];
    $participants = optional_param('participants', 0, PARAM_INT);

    if ($participants) {
        $userid = $participants;

        $arruser = block_ned_teacher_tools_count_unmarked_students($course, $cm, $pageparams['show']);
        $useridlist = array_keys($arruser);
        $last = false;

        $rownum = array_search($userid, $useridlist);
        if ($rownum == count($useridlist) - 1) {
            $last = true;
        }

    } else if ($userid) {
        $arruser = block_ned_teacher_tools_count_unmarked_students($course, $cm, $pageparams['show']);
        $useridlist = array_keys($arruser);
        $last = false;
        $rownum = 0;
        if ($useridlist){
            $rownum = array_search($userid, $useridlist);
            if ($rownum == count($useridlist) - 1) {
                $last = true;
            }
        }

    } else {
        $filter = [
            MM::BY_USER,
            MM::NOT_ZERO => true,
            MM::ONLY_KICA => $MB->get_pageparam(MM::P_KICA_ACTIVITIES),
            MM::CHECK_TAG => $MB->tag_id,
            $MB->get_pageparam(MM::P_STATUS),
            MM::COURSEMODULE_IDS => $cm->id,
        ];

        $mm_data = $MM->get_data_all($filter);
        $useridlist = array_keys($mm_data);
        $last = false;

        if (!$numofuser = count($useridlist)){
            $o = "There is no record";
            return $o;
        }

        $rownum = min($rownum, $numofuser - 1);
        $userid = $useridlist[$rownum];

        if ($rownum == $numofuser - 1) {
            $last = true;
        }
    }

    $user = $DB->get_record('user', array('id' => $userid));
    $grade = $assign->get_user_grade($userid, false, $showattemptnumber);

    if ($grade) {
        $data = new stdClass();
        if ($grade->grade !== null && $grade->grade >= 0) {
            $data->grade = format_float($grade->grade, 2);
        }
    } else {
        $data = new stdClass();
    }

    block_ned_teacher_tools_get_all_submissions_fix($userid, $assign);
    $allsubmissions = $assign->get_all_submissions($userid);

    if ($attemptnumber != -1) {
        $params = array('attemptnumber' => $attemptnumber + 1,
            'totalattempts' => count($allsubmissions));
        $message = get_string('editingpreviousfeedbackwarning', 'assign', $params);
        $o .= $assign->get_renderer()->notification($message);
    }
    $maxattemptnumber = $assign->get_instance()->maxattempts;
    // Now show the grading form.
    if (!$mform) {
        $pageparams['rownum']     = $rownum;
        $pageparams['useridlist'] = $useridlist;
        $pageparams['last']       = $last;
        $pageparams['userid']     = $MB->userid;
        $pageparams['group']      = $MB->groupid;
        $pageparams['readonly']   = $readonly;
        $pageparams['attemptnumber'] = $attemptnumber;
        $pageparams['maxattemptnumber'] = $maxattemptnumber;
        $pageparams['activity_type'] = $activitytype;
        $pageparams['participants'] = $participants;

        $formparams = array($assign, $data, $pageparams);

        $mform = new mod_assign_grading_form_fn(null,
            $formparams,
            'post',
            '',
            array('class' => 'gradeform unresponsive'));
    }
    $o .= $assign->get_renderer()->render(new assign_form('gradingform', $mform));

    if (count($allsubmissions) > 1 && $attemptnumber == -1) {
        $allgrades = block_ned_teacher_tools_get_all_grades($userid, $assign);

        $history = new assign_attempt_history($allsubmissions,
            $allgrades,
            $assign->get_submission_plugins(),
            $assign->get_feedback_plugins(),
            $assign->get_course_module()->id,
            $assign->get_return_action(),
            $assign->get_return_params(),
            true, null, null);
        $o .= $assign->get_renderer()->render($history);
    }

    \mod_assign\event\grading_form_viewed::create_from_user($assign, $user)->trigger();

    return $o;
}

function block_ned_teacher_tools_view_submissions(\assign $assign, $cm, $course, $pageparams, $students) {
    global $DB, $CFG;

    $o = '';

    if ($pageparams['show'] == 'unsubmitted') {
        $instance = $assign->get_instance();
        $params = ['assignment' => $instance->id, 'status' => ASSIGN_SUBMISSION_STATUS_SUBMITTED, 'latest' => 1];
        $user_submitted = $DB->get_records('assign_submission', $params, '','userid');
        $o .= NED\render_marking_manager_not_submitted($students, $user_submitted, $cm, $instance);
    } else {
        require_once($CFG->dirroot . '/mod/assign/gradeform.php');

        $participants = optional_param('participants', '0', PARAM_INT);
        $useridlist = block_ned_teacher_tools_count_unmarked_students($course, $cm, $pageparams['show'], $pageparams['sort']) ?: [];

        if (empty($useridlist)) {
            return NED\str('nostudents');
        }
        foreach ($useridlist as $key => $user_or_id) {
            $userid = $user_or_id->id ?? $user_or_id;
            $user = $DB->get_record('user', array('id' => $userid));
            $submission = $assign->get_user_submission($userid, false);
            $submissiongroup = null;

            // Get the current grade.
            $grade = $assign->get_user_grade($userid, false, null);

            // Get all the submissions (for the history view).
            [$allsubmissions, $allgrades, $attemptnumber, $maxattemptnumber]
                = block_ned_teacher_tools_get_submission_history_view($submission, $grade, $user, null, $assign);


            $o .= block_ned_teacher_tools_render_assign_submission_history_summary(
                new \block_ned_teacher_tools\assign_submission_history($allsubmissions, $allgrades, $attemptnumber,
                $maxattemptnumber, $assign->get_submission_plugins(),
                $assign->get_feedback_plugins(),
                $assign->get_course_module()->id,
                $assign->get_return_action(),
                $assign->get_return_params()
                ),
                $assign->get_renderer(),
                $user,
                $assign
            );

            $msg = get_string('viewgradingformforstudent',
                'assign',
                array('id' => $user->id, 'fullname' => fullname($user)));
            block_ned_teacher_tools_add_to_log_legacy($assign, 'view grading form', $msg);

        }
    }
    return $o;
}

/**
 * @param $submission
 * @param $grade
 * @param $user
 * @param $showattemptnumber
 * @param ned_assign | \assign $assign
 *
 * @return array
 */
function block_ned_teacher_tools_get_submission_history($submission, $grade, $user, $showattemptnumber, $assign) {
    global $DB;

    $attemptnumber = ($submission) ? $submission->attemptnumber : 1;
    $allsubmissions = array($attemptnumber => $submission);
    $allgrades = array($attemptnumber => $grade);
    $graders = array();
    if (is_null($showattemptnumber)) {
        // If attemptnumber was not set, then we already have the most recent submission.
        $maxattemptnumber = $attemptnumber;
    } else {
        // Get the most recent submission.
        if ($maxsub = $assign->get_user_submission($user->id, false)) {
            $maxattemptnumber = $maxsub->attemptnumber;
            $allsubmissions[$maxsub->attemptnumber] = $maxsub;
        } else {
            $maxattemptnumber = 0;
        }
    }
    for ($i = 1; $i <= $maxattemptnumber; $i++) {
        // Retrieve any submissions / grades we haven't already retrieved.
        if (!array_key_exists($i, $allsubmissions)) {
            $allsubmissions[$i] = $assign->get_user_submission($user->id, false, $i);
        }
        if (!array_key_exists($i, $allgrades)) {
            $allgrades[$i] = $assign->get_user_grade($user->id, false, $i);
            if ($allgrades[$i]) {
                $allgrades[$i]->gradefordisplay = $assign->display_grade($allgrades[$i]->grade, false);
                if (!array_key_exists($allgrades[$i]->grader, $graders)) {
                    $graders[$allgrades[$i]->grader] = $DB->get_record('user', array('id' => $allgrades[$i]->grader));
                }
                $allgrades[$i]->grader = $graders[$allgrades[$i]->grader];
            }
        }
    }

    return array($allsubmissions, $allgrades, $attemptnumber, $maxattemptnumber);
}

/**
 * @param $submission
 * @param $grade
 * @param $user
 * @param $showattemptnumber
 * @param ned_assign | \assign $assign
 *
 * @return array
 */
function block_ned_teacher_tools_get_submission_history_view($submission, $grade, $user, $showattemptnumber, $assign) {
    global $DB;

    $attemptnumber = ($submission) ? $submission->attemptnumber : 1;
    $allsubmissions = array();
    $allgrades = array();
    $graders = array();
    if (is_null($showattemptnumber)) {
        // If attemptnumber was not set, then we already have the most recent submission.
        $maxattemptnumber = $attemptnumber;
    } else {
        // Get the most recent submission.
        if ($maxsub = $assign->get_user_submission($user->id, false)) {
            $maxattemptnumber = $maxsub->attemptnumber;
            $allsubmissions[$maxsub->attemptnumber] = $maxsub;
        } else {
            $maxattemptnumber = 0;
        }
    }
    for ($i = 0; $i <= $maxattemptnumber; $i++) {
        // Retrieve any submissions / grades we haven't already retrieved.
        if (!array_key_exists($i, $allsubmissions)) {
            $allsubmissions[$i] = $assign->get_user_submission($user->id, false, $i);
        }
        if (!array_key_exists($i, $allgrades)) {
            $allgrades[$i] = $assign->get_user_grade($user->id, false, $i);
            if ($allgrades[$i]) {
                $allgrades[$i]->gradefordisplay = $assign->display_grade($allgrades[$i]->grade, false);
                if (!array_key_exists($allgrades[$i]->grader, $graders)) {
                    $graders[$allgrades[$i]->grader] = $DB->get_record('user', array('id' => $allgrades[$i]->grader));
                }
                $allgrades[$i]->grader = $graders[$allgrades[$i]->grader];
            }
        }
    }

    return array($allsubmissions, $allgrades, $attemptnumber, $maxattemptnumber);
}

/**
 * @param int                  $userid
 * @param ned_assign | \assign $assign
 *
 */
function block_ned_teacher_tools_add_resubmission($userid, $assign) {
    global $DB;

    $user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);

    $currentgrade = $assign->get_user_grade($userid, false);
    if (!$currentgrade) {
        return; // If the most recent submission is not graded, then resubmissions are not allowed.
    }
    if (block_ned_teacher_tools_reached_resubmission_limit($currentgrade->attemptnumber, $assign)) {
        return; // Already reached the resubmission limit.
    }
    if ($assign->get_instance()->teamsubmission) {
        $submission = $assign->get_group_submission($userid, 0, true); // Create the submission, if it doesn't already exist.
    } else {
        $submission = $assign->get_user_submission($userid, true); // Create the submissoin, if it doesn't already exist.
    }

    // Set the submission's status to resubmission.
    $DB->set_field('assign_submission', 'status', ASSIGN_SUBMISSION_STATUS_REOPENED, array('id' => $submission->id));

    block_ned_teacher_tools_add_to_log_legacy($assign, 'add resubmission', get_string('addresubmissionforstudent', 'assign',
        array('id' => $user->id, 'fullname' => fullname($user))));
}

/**
 * @param int                  $userid
 * @param ned_assign | \assign $assign
 *
 */
function block_ned_teacher_tools_remove_resubmission($userid, $assign) {
    global $DB;

    $user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);

    if ($assign->get_instance()->teamsubmission) {
        $submission = $assign->get_group_submission($userid, 0, false);
    } else {
        $submission = $assign->get_user_submission($userid, false);
    }

    if (!$submission || $submission->status != ASSIGN_SUBMISSION_STATUS_REOPENED) {
        return; // No resubmission currently open.
    }

    $DB->set_field('assign_submission', 'status', ASSIGN_SUBMISSION_STATUS_SUBMITTED, array('id' => $submission->id));

    // Set the submission's status to resubmission.
    block_ned_teacher_tools_add_to_log_legacy($assign, 'remove resubmission', get_string('removeresubmissionforstudent', 'assign',
        array('id' => $user->id, 'fullname' => fullname($user))));
}

function block_ned_teacher_tools_get_grading_userid_list($assign) {
    global $CFG;

    require_once($CFG->dirroot.'/mod/assign/gradingtable.php');

    $filter = get_user_preferences('assign_filter', '');
    $table = new assign_grading_table($assign, 0, $filter, 0, false);

    $useridlist = $table->get_column_data('userid');

    return $useridlist;
}

/**
 * @param ned_assign | \assign $assign
 * @param int                  $userid
 * @param bool                 $create
 * @param int                  $attemptnumber
 *
 * @return array|bool|mixed|\stdClass
 */
function block_ned_teacher_tools_get_user_submission($assign, $userid, $create, $attemptnumber=null) {
    global $DB, $USER;

    if (!$userid) {
        $userid = $USER->id;
    }

    // If the userid is not null then use userid.
    $params = array('assignment' => $assign->get_instance()->id, 'userid' => $userid, 'groupid' => 0);
    if (!is_null($attemptnumber)) {
        $params['attemptnumber'] = $attemptnumber;
    }
    $submission = $DB->get_records('assign_submission', $params, 'attemptnumber DESC', '*', 0, 1);

    if ($submission) {
        return reset($submission);
    }
    if ($create) {
        $submission = new stdClass();
        $submission->assignment   = $assign->get_instance()->id;
        $submission->userid       = $userid;
        $submission->timecreated = time();
        $submission->timemodified = $submission->timecreated;

        if ($assign->get_instance()->submissiondrafts) {
            $submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT;
        } else {
            $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
        }

        $submission->attemptnumber = is_null($attemptnumber) ? 1 : $attemptnumber;

        $sid = $DB->insert_record('assign_submission', $submission);
        $submission->id = $sid;
        return $submission;
    }
    return false;
}

/**
 * @param ned_assign | \assign $assign
 * @param int                  $userid
 *
 * @return bool|mixed
 */
function block_ned_teacher_tools_get_user_grade($assign, $userid) {
    global $DB, $USER;

    if (!$userid) {
        $userid = $USER->id;
    }

    $grade = $DB->get_record('assign_grades', array('assignment' => $assign->get_instance()->id, 'userid' => $userid));

    if ($grade) {
        return $grade;
    }
    return false;
}

function block_ned_teacher_tools_is_graded_($assign, $userid) {
    $grade = block_ned_teacher_tools_get_user_grade($assign, $userid);
    if ($grade) {
        return ($grade->grade !== null && $grade->grade >= 0);
    }
    return false;
}

/**
 * @param \block_ned_teacher_tools\assign_submission_history $history
 * @param \core_renderer                                     $assignrenderer
 * @param \stdClass                                          $user
 * @param ned_assign | \assign                               $assign
 *
 * @return string
 */
function block_ned_teacher_tools_render_assign_submission_history_summary(\block_ned_teacher_tools\assign_submission_history $history,
                                                                    $assignrenderer, $user, $assign) {
    global $OUTPUT, $DB, $CFG, $pageparams, $base_url;
    if (!isset($base_url)){
        $base_url = new moodle_url(NED\PLUGIN_URL . 'fn_gradebook.php', $pageparams);
    }

    if (!$base_url->param('mid')){
        $base_url->param('mid', $assign->get_course_module()->id);
    }
    $historyout = '';

    if ($user) {
        $viewfullnames = has_capability('moodle/site:viewfullnames', $assign->get_course_context());

        $already = [];
        $fields = \core_user\fields::for_identity($assign->get_context(), false)->excluding(...$already);
        $requiredfields = $fields->get_required_fields();

        $summary = new assign_user_summary($user,
            $assign->get_course()->id,
            $viewfullnames,
            $assign->is_blind_marking(),
            $assign->get_uniqueid_for_user($user->id),
            $requiredfields
        );

        $gradeitem = $DB->get_record('grade_items', array('itemtype' => 'mod', 'itemmodule' => 'assign',
            'iteminstance' => $assign->get_instance()->id));

        $maxattemptnumber = isset(
            $pageparams['maxattemptnumber']) ? $pageparams['maxattemptnumber'] : count($history->allsubmissions);

        $resubstatus = '';
        $resubtype = $assign->get_instance()->attemptreopenmethod;
        if (!SH::is_resubmission_enabled($assign->get_course()->id) && $resubtype != ASSIGN_ATTEMPT_REOPEN_METHOD_NONE) {
            if (block_ned_teacher_tools_reached_resubmission_limit($maxattemptnumber, $assign)) {
                $resubstatus = get_string('atmaxresubmission', 'block_ned_teacher_tools');
            } else if ($resubtype == ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL) {

                if ($history->allsubmissions[(count($history->allsubmissions) - 1)]->status == 'reopened') {
                    $resubstatus = 'Allow resubmit: <input name="checkbox" type="checkbox" id="checkbox" value="1"
                        checked="checked" disabled="disabled" />';
                } else {
                    $resubstatus = 'Allow resubmit: <input name="checkbox" type="checkbox" id="checkbox" value="1"
                        disabled="disabled" />';
                }

            } else if ($resubtype == ASSIGN_ATTEMPT_REOPEN_METHOD_UNTILPASS) {
                $gradepass = $gradeitem->gradepass;
                if ($gradeitem->gradepass > 0) {
                    $resubstatus = get_string('attemptreopenmethod_untilpass', 'assign');
                }
            }
        }

        if ($assign->get_instance()->teamsubmission) {

            $submissiongroup = $assign->get_submission_group($user->id);
            if (isset($submissiongroup->name)) {
                $groupname = ' ('.$submissiongroup->name.')';
            } else {
                $groupname = ' (Default group)';
            }
        } else {
            $groupname = '';
        }

        $header = '<table class="headertable"><tr>';

        if ($summary->blindmarking) {
            $header .= '<td>'.get_string('hiddenuser', 'assign') . $summary->uniqueidforuser;
            $header .= '<br />Assignment ' .$assign->get_instance()->name.'</td>';
        } else {
            $header .= '<td width="35px">'.$OUTPUT->user_picture($summary->user).'</td>';
            $urlparams = array('id' => $summary->user->id, 'course' => $summary->courseid);
            $url = new moodle_url('/user/view.php', $urlparams);

            $header .= '<td><div style="color:white;">'.$OUTPUT->action_link($url, fullname($summary->user,
                    $summary->viewfullnames),
                    null, array('target' => '_blank', 'class' => 'userlink')). $groupname. '</div>';
            $header .= '<div style="margin-top:5px; color:white;">Assignment: <a target="_blank" class="marking_header_link"
                title="Assignment" href="'.
                $CFG->wwwroot.'/mod/assign/view.php?id='.$assign->get_course_module()->id.'">' .
                $assign->get_instance()->name.'</a></div></td>';
            $header .= '<td align="right" style="color:white;">'.$resubstatus.'</td>';
        }
        $header .= '</tr></table>';

    }

    $t = new html_table();
    $t->attributes['class'] = 'generaltable historytable';
    $cell = new html_table_cell($header ?? '');
    $cell->attributes['class'] = 'historyheader';
    $cell->colspan = 3;
    $t->data[] = new html_table_row(array($cell));

    $submittedicon = '<img width="16" height="16" border="0" alt="Assignment" src="'.
        $CFG->wwwroot.'/blocks/ned_teacher_tools/pix/text.gif" valign="absmiddle"> ';
    $markedicon = '<img width="16" height="16" border="0" alt="Assignment" src="'.
        $CFG->wwwroot.'/blocks/ned_teacher_tools/pix/completed.gif" valign="absmiddle"> ';
    $savedicon = '<img width="16" height="16" border="0" alt="Assignment" src="'.
        $CFG->wwwroot.'/blocks/ned_teacher_tools/pix/saved.gif" valign="absmiddle"> ';
    if (!empty($gradeitem) && $gradeitem->gradepass > 0) {
        $markediconincomplete = '<img width="16" height="16" border="0" alt="Assignment" src="'.
            $CFG->wwwroot.'/blocks/ned_teacher_tools/pix/incomplete.gif" valign="absmiddle"> ';
    } else {
        $markediconincomplete = '<img width="16" height="16" border="0" alt="Assignment" src="'.
            $CFG->wwwroot.'/blocks/ned_teacher_tools/pix/graded.gif" valign="absmiddle"> ';
    }

    for ($i = $history->maxsubmissionnum; $i >= 0; $i--) {

        $submission = $history->allsubmissions[$i];
        $grade = $history->allgrades[$i];

        if (($i == $history->maxsubmissionnum) && (isset($grade->grade))) {
            if (!empty($gradeitem) && $gradeitem->gradepass > 0) {
                $lastsubmissionclass = ($grade->grade >= $gradeitem->gradepass) ? 'bg_green' : 'bg_orange';
            } else {
                $lastsubmissionclass = 'bg_white';
            }
        } else {
            $lastsubmissionclass = '';
        }

        if ($grade) {
            if ($grade->grade == -1) {
                $cell1 = new html_table_cell(get_string('nograde', 'block_ned_teacher_tools'));
            } else {
                $cell1 = new html_table_cell($grade->gradefordisplay);
            }

            $cell1->rowspan = 2;
            if ($i == $history->maxsubmissionnum) {
                $cell1->attributes['class'] = $lastsubmissionclass;
            }

            $use_cell3 = true;
            switch ($submission->status){
                default:
                case ASSIGN_SUBMISSION_STATUS_NEW:
                    $cell2 = new html_table_cell(get_string('submissionstatus_new', 'assign'));
                    $use_cell3 = false;
                    break;
                case ASSIGN_SUBMISSION_STATUS_DRAFT:
                    $cell2 = new html_table_cell($savedicon . get_string('submissionstatus_draft', 'assign'));
                    break;
                case ASSIGN_SUBMISSION_STATUS_REOPENED:
                    $cell2 = new html_table_cell(get_string('submissionstatus_reopened', 'assign'));
                    break;
                case ASSIGN_SUBMISSION_STATUS_SUBMITTED:
                    $cell2 = new html_table_cell($submittedicon . get_string('submitted', 'assign'));
                    break;
            }

            if ($use_cell3){
                $cell3 = new html_table_cell(userdate($submission->timemodified));
                if ($i == $history->maxsubmissionnum) {
                    $url = new moodle_url($base_url);
                    $url->param('expand', 1);
                    $url->param(MM::P_ASSIGN_GRADER, 1);
                    $url->param(MB::PAR_SETUSER, $user->id);
                    $cell3->text = html_writer::div($cell3->text, 'mm-submission-date') .
                        html_writer::div(
                            NED\img('fullscreen_maximize.gif'), 'mm-expand',
                            ['data-url' => $url->out(false)]
                        );

                    $cell2->attributes['class'] = $lastsubmissionclass;
                    $cell3->attributes['class'] = $lastsubmissionclass;
                }
            } else {
                $cell3 = new html_table_cell('-');
            }

            if ($grade->grade == -1) {
                $cell1->attributes['class'] = 'bg_grey';
                $cell2->attributes['class'] = 'bg_grey';
                $cell3->attributes['class'] = 'bg_grey';
            }
            $t->data[] = new html_table_row(array($cell1, $cell2, $cell3));

            if ($grade->grade == -1) {
                $cell1 = new html_table_cell('<img width="16" height="16" border="0" alt="Assignment" src="'.
                    $CFG->wwwroot.'/blocks/ned_teacher_tools/pix/graded.gif" valign="absmiddle"> Marked');
            } else {
                $cell1 = new html_table_cell(((!empty($gradeitem) && ($gradeitem->gradepass > 0)
                        && ($grade->grade >= $gradeitem->gradepass)) ? $markedicon : $markediconincomplete) . 'Marked');
            }
            if (isset($grade->grader)) {
                $grader = ' ('.fullname($grade->grader).')';
            } else {
                $grader = '';
            }
            $cell2 = new html_table_cell(userdate($grade->timemodified).$grader);
            if ($grade->grade == -1) {
                $cell1->attributes['class'] = 'bg_grey';
                $cell2->attributes['class'] = 'bg_grey';
            } elseif ($i == $history->maxsubmissionnum) {
                $cell1->attributes['class'] = $lastsubmissionclass;
                $cell2->attributes['class'] = $lastsubmissionclass;
            }
            $t->data[] = new html_table_row(array($cell1, $cell2));

        }

    }

    $historyout .= html_writer::table($t);

    $o = '';
    if ($historyout) {
        $o .= $assignrenderer->box_start('generalbox submissionhistory_summary');
        $o .= $historyout;
        $o .= $assignrenderer->box_end();
    }

    return $o;
}

/**
 * @param \mod_assign\output\assign_submission_status                         $status
 * @param \local_ned_controller\mod_assign\assign | \assign $assign
 * @param \stdClass                                         $user
 * @param \stdClass                                         $grade
 * @param  \core_renderer                                   $assignrenderer
 *
 * @return string
 */
function block_ned_teacher_tools_render_assign_submission_status(mod_assign\output\assign_submission_status $status, $assign, $user,
                                                           $grade, $assignrenderer) {
    global $OUTPUT, $DB, $CFG, $pageparams, $MB;
    $o = '';

    $base_url = isset($MB) ? $MB->get_url() : new moodle_url('/blocks/ned_teacher_tools/fn_gradebook.php', $pageparams);
    $header = '';
    if ($user) {
        $viewfullnames = has_capability('moodle/site:viewfullnames', $assign->get_course_context());
        $summary = new assign_user_summary($user,
            $assign->get_course()->id,
            $viewfullnames,
            $assign->is_blind_marking(),
            $assign->get_uniqueid_for_user($user->id),
            null);

        $gradeitem = $DB->get_record('grade_items', array('itemtype' => 'mod', 'itemmodule' => 'assign',
            'iteminstance' => $assign->get_instance()->id));

        if ($assign->get_instance()->teamsubmission) {

            $submissiongroup = $assign->get_submission_group($user->id);
            if (isset($submissiongroup->name)) {
                $groupname = ' ('.$submissiongroup->name.')';
            } else {
                $groupname = ' (Default group)';
            }
        } else {
            $groupname = '';
        }

        $header = '<table class="headertable"><tr>';

        if ($summary->blindmarking) {
            $header .= '<td>'.get_string('hiddenuser', 'assign') . $summary->uniqueidforuser;
            $header .= '<br />Assignment ' .$assign->get_instance()->name.'</td>';
        } else {
            $header .= '<td width="35px">'.$OUTPUT->user_picture($summary->user).'</td>';
            $urlparams = array('id' => $summary->user->id, 'course' => $summary->courseid);
            $url = new moodle_url('/user/view.php', $urlparams);

            $header .= '<td><div style="color:white;">'.
                $OUTPUT->action_link($url, fullname($summary->user, $summary->viewfullnames),
                    null, array('target' => '_blank', 'class' => 'userlink')). $groupname. '</div>';
            $header .= '<div style="margin-top:5px; color:white;">Assignment: <a target="_blank" class="marking_header_link"
                title="Assignment" href="'.$CFG->wwwroot.'/mod/assign/view.php?id='.$assign->get_course_module()->id.'">' .
                $assign->get_instance()->name.'</a></div></td>';
        }
        $header .= '</tr></table>';
    }

    $time = time();

    $t = new html_table();
    $t->attributes['class'] = 'generaltable historytable';
    $cell = new html_table_cell($header);
    $cell->attributes['class'] = 'historyheader';
    $cell->colspan = 3;
    $t->data[] = new html_table_row(array($cell));

    $submittedicon = '<img width="16" height="16" border="0" alt="Assignment" src="'.
        $CFG->wwwroot.'/blocks/ned_teacher_tools/pix/text.gif" valign="absmiddle"> ';
    $markedicon = '<img width="16" height="16" border="0" alt="Assignment" src="'.
        $CFG->wwwroot.'/blocks/ned_teacher_tools/pix/completed.gif" valign="absmiddle"> ';
    $savedicon = '<img width="16" height="16" border="0" alt="Assignment" src="'.
        $CFG->wwwroot.'/blocks/ned_teacher_tools/pix/saved.gif" valign="absmiddle"> ';
    if (!empty($gradeitem) && $gradeitem->gradepass > 0) {
        $markediconincomplete = '<img width="16" height="16" border="0" alt="Assignment" src="'.
            $CFG->wwwroot.'/blocks/ned_teacher_tools/pix/incomplete.gif" valign="absmiddle"> ';
    } else {
        $markediconincomplete = '<img width="16" height="16" border="0" alt="Assignment" src="'.
            $CFG->wwwroot.'/blocks/ned_teacher_tools/pix/graded.gif" valign="absmiddle"> ';
    }

    $grade->gradefordisplay = $assign->display_grade($grade->grade, false);

    $submission = $assign->get_user_submission($user->id, false);

    if ($grade) {

        $cell1 = new html_table_cell($grade->gradefordisplay);
        $cell1->rowspan = 2;

        if ($submission->status == 'draft') {
            $cell2 = new html_table_cell($savedicon . 'Draft');
        } else {
            $cell2 = new html_table_cell($submittedicon . get_string('submitted', 'assign'));
        }

        $cell3 = new html_table_cell(userdate($submission->timemodified));
        $lastsubmissionclass = '';
        if (true) {
            $url = new moodle_url($base_url);
            $url->param('user', $user->id);
            $url->param('expand', true);
            $cell3->text = '<div style="float:left;">'.$cell3->text.'
                                </div>
                                <div style="float:right;">
                                <a href="'.$url->out(false).'">
                                <img width="16" height="16" border="0" alt="Assignment" src="'.
                $CFG->wwwroot.'/blocks/ned_teacher_tools/pix/fullscreen_maximize.gif" valign="absmiddle">
                                </a>
                                </div>';
            $cell2->attributes['class'] = $lastsubmissionclass;
            $cell3->attributes['class'] = $lastsubmissionclass;
        }

        $t->data[] = new html_table_row(array($cell1, $cell2, $cell3));

        $cell1 = new html_table_cell(((!empty($gradeitem) && ($gradeitem->gradepass > 0)
                && ($grade->grade >= $gradeitem->gradepass)) ? $markedicon : $markediconincomplete) . 'Marked');
        $cell2 = new html_table_cell(userdate($grade->timemodified));
        if (true) {
            $cell1->attributes['class'] = $lastsubmissionclass;
            $cell2->attributes['class'] = $lastsubmissionclass;
        }
        $t->data[] = new html_table_row(array($cell1, $cell2));

    }

    $historyout = html_writer::table($t);

    $o = '';
    if ($historyout) {
        $o .= $assignrenderer->box_start('generalbox submissionhistory_summary');
        $o .= $historyout;
        $o .= $assignrenderer->box_end();
    }

    return $o;
}

/**
 * @param $userid
 * @param ned_assign | \assign $assign
 *
 * @return bool
 */
function block_ned_teacher_tools_get_all_submissions_fix($userid, $assign) {
    global $DB, $USER;

    // If the userid is not null then use userid.
    if (!$userid) {
        $userid = $USER->id;
    }

    $params = array('assignment' => $assign->get_instance()->id, 'userid' => $userid, 'status' => 'reopened');

    if ($submissions = $DB->get_records('assign_submission', $params, 'attemptnumber DESC')) {
        array_shift($submissions);
        if ($submissions) {
            foreach ($submissions as $submission) {
                if ($submission->status == 'reopened') {
                    $rec = new stdClass();
                    $rec->id = $submission->id;
                    $rec->status = 'submitted';
                    $DB->update_record('assign_submission', $rec);
                }
            }
        }
    }
    return true;
}

/**
 * @param int                  $userid
 * @param ned_assign | \assign $assign
 *
 * @return array
 */
function block_ned_teacher_tools_get_all_grades($userid, $assign) {
    global $DB, $USER, $PAGE;

    // If the userid is not null then use userid.
    if (!$userid) {
        $userid = $USER->id;
    }

    $params = array('assignment' => $assign->get_instance()->id, 'userid' => $userid);

    $grades = $DB->get_records('assign_grades', $params, 'attemptnumber ASC');

    $gradercache = array();
    $cangrade = has_capability('mod/assign:grade', $assign->get_context());

    // Need gradingitem and gradingmanager.
    $gradingmanager = get_grading_manager($assign->get_context(), 'mod_assign', 'submissions');
    $controller = $gradingmanager->get_active_controller();

    $gradinginfo = grade_get_grades($assign->get_course()->id,
        'mod',
        'assign',
        $assign->get_instance()->id,
        $userid);

    $gradingitem = null;
    if (isset($gradinginfo->items[0])) {
        $gradingitem = $gradinginfo->items[0];
    }

    foreach ($grades as $grade) {
        // First lookup the grader info.
        if (isset($gradercache[$grade->grader])) {
            $grade->grader = $gradercache[$grade->grader];
        } else {
            // Not in cache - need to load the grader record.
            $grade->grader = $DB->get_record('user', array('id' => $grade->grader));
            if (isset($grade->grader) && is_object($grade->grader) && property_exists($grade->grader, 'id')){
                $gradercache[$grade->grader->id] = $grade->grader;
            }
        }

        // Now get the gradefordisplay.
        if ($controller) {
            $controller->set_grade_range(make_grades_menu($assign->get_instance()->grade));
            $grade->gradefordisplay = $controller->render_grade($PAGE,
                $grade->id,
                $gradingitem,
                $grade->grade,
                $cangrade);
        } else {
            $grade->gradefordisplay = $assign->display_grade($grade->grade, false);
        }

    }

    return $grades;
}

/**
 * @param int                  $submissionnum
 * @param ned_assign | \assign $assign
 *
 * @return bool
 */
function block_ned_teacher_tools_reached_resubmission_limit($submissionnum, $assign) {
    $maxresub = $assign->get_instance()->maxattempts;
    if ($maxresub == ASSIGN_UNLIMITED_ATTEMPTS) {
        return false;
    }
    return ($submissionnum >= $maxresub);
}

function block_ned_teacher_tools_add_to_log_legacy_ ($courseid, $module, $action, $url='', $info='', $cm=0, $user=0) {
    $manager = get_log_manager();
    if (method_exists($manager, 'legacy_add_to_log')) {
        $manager->legacy_add_to_log($courseid, $module, $action, $url, $info, $cm, $user);
    }
}

/**
 * @param ned_assign | \assign $assign
 * @param string               $action
 * @param string               $info
 * @param string               $url
 * @param bool                 $return
 *
 * @return array|\mixed
 */
function block_ned_teacher_tools_add_to_log_legacy($assign, $action = '', $info = '', $url='', $return = false) {
    global $USER;

    $fullurl = 'view.php?id=' . $assign->get_course_module()->id;
    if ($url != '') {
        $fullurl .= '&' . $url;
    }

    $args = array(
        $assign->get_course()->id,
        'assign',
        $action,
        $fullurl,
        $info,
        $assign->get_course_module()->id
    );

    if ($return) {
        return $args;
    }
    return call_user_func_array('block_ned_teacher_tools_add_to_log_legacy_', $args);
}

/**
 * @param null $pluginconfig
 *
 * @return string
 */
function block_ned_teacher_tools_footer($pluginconfig=null) {
    if (is_null($pluginconfig)){
        $pluginconfig = get_config(NED\PLUGIN_NAME);
    }

    $text = sprintf('%s [%s %s]', NED\str('teachertools'), NED\str('version'), $pluginconfig->version);
    $output = \html_writer::div(
        NED\link(['/local/ned_controller'], $text),
        'tt-footer'
    );

    return $output;
}

function block_ned_teacher_tools_mygroup_members($courseid, $userid, $only_active=false) {
    $members = array();
    $context = context_course::instance($courseid);

    if (has_capability('block/ned_teacher_tools:view_all_groups', $context, $userid)) {
        $groups = groups_get_all_groups($courseid);
        $groups = array_keys($groups);
    } else {
        $groups = groups_get_user_groups($courseid, $userid);
        $groups = $groups[0];
    }

    if (empty($groups)) {
        return false;
    }
    foreach ($groups as $groupid){
        $members += get_enrolled_users($context, 'mod/assign:submit', $groupid,
            'u.*', 'u.suspended ASC,u.firstname ASC', 0, 0, $only_active);
    }

    return $members;
}

function block_ned_teacher_tools_get_users_and_groups($course, $for_deadline_manager=false, $show_only_active=null, $load_cm_students=null) {
    return NED\get_users_and_groups($course, $for_deadline_manager, $show_only_active, $load_cm_students);
}

function block_ned_teacher_tools_groups_print_course_menu($course, moodle_url $urlroot, $return=false, $activegroup=false, $groups=false) {
    global $USER, $OUTPUT;

    if (!$groupmode = $course->groupmode) {
        return '';
    }

    $urlroot = new moodle_url($urlroot);
    $urlroot->remove_params('group');

    $context = context_course::instance($course->id);
    $aag = has_capability('moodle/site:accessallgroups', $context);
    $usergroups = array();
    $groupsmenu = array();

    if ($groups === false){

        if ($groupmode == VISIBLEGROUPS or $aag) {
            $allowedgroups = groups_get_all_groups($course->id, 0, $course->defaultgroupingid);
            // Get user's own groups and put to the top.
            $usergroups = groups_get_all_groups($course->id, $USER->id, $course->defaultgroupingid);
        } else {
            $allowedgroups = groups_get_all_groups($course->id, $USER->id, $course->defaultgroupingid);
        }
    } else {
        $allowedgroups = $groups;
        if ($groupmode == VISIBLEGROUPS or $aag) {
            $usergroups = groups_get_all_groups($course->id, $USER->id, $course->defaultgroupingid);
        }
    }

    foreach ($allowedgroups as $key => $group){
        $course_users = NED\get_enrolled_users($course->id, $context, 'mod/assign:submit', $group->id);
        if (empty($course_users)){
            unset($allowedgroups[$key]);
        }
    }

    if ($activegroup === false) {
        $activegroup = groups_get_course_group($course, true, $allowedgroups);
    }

    $groupsmenuoptions = groups_sort_menu_options($allowedgroups, $usergroups);

    if (!empty($groupsmenuoptions)){
        $select_label = NED\img('i/users', '');
        if (count($groupsmenuoptions) > 1){
            if (!$allowedgroups or $groupmode == VISIBLEGROUPS or $groupmode == SEPARATEGROUPS or $aag) {
                $groupsmenu[NED\GROUP_NONE] = get_string('none');
                $groupsmenu[NED\GROUP_ALL] = get_string('allgroups', 'block_ned_teacher_tools');
            }

            $groupsmenu += groups_sort_menu_options($allowedgroups, $usergroups);

            $select = new single_select(new moodle_url($urlroot), 'group', $groupsmenu, $activegroup, null, 'selectgroup');
            $select->label = $select_label;
            $output = $OUTPUT->render($select);
        } else {
            $output = $select_label . html_writer::span(reset($groupsmenuoptions), 'singleselect');
        }

    } else {
        $output = '';
    }

    $output = html_writer::div($output, 'groupselector group-selector');

    if (!$return) {
        echo $output;
    }
    return $output;
}

/**
 * @param moodle_url $url
 * @param string     $name
 * @param array      $options
 * @param            $selected
 * @param string     $formid
 * @param mixed      $nothing
 * @param string     $label
 * @param bool       $print
 *
 * @return string
 * @throws \moodle_exception
 */
function block_ned_teacher_tools_groups_menu($url, $name, $options, $selected='', $formid=null, $nothing='', $label=null, $print=false){
    global $OUTPUT;
    if (is_null($label)){
        $label = NED\img('i/user', '');
    }
    if (count($options) == 2 && isset($options[0])){
        unset($options[0]);
    }
    if (count($options) > 1){
        $url = new moodle_url($url);
        $studentselect = new single_select($url, $name, $options, $selected, $nothing, $formid);
        $studentselect->label = $label;
        $studentselect_r = $OUTPUT->render($studentselect);
    } else {
        $studentselect_r = $label . html_writer::span(reset($options), 'singleselect');
    }

    $output = html_writer::div($studentselect_r, 'groupselector student-selector');

    if ($print){
        echo $output;
    }
    return $output;
}

function block_ned_teacher_tools_check_activity_tag($activity_id, $tag, &$tags=[], &$activity_tag_list=[]){
    $activity_tags = core_tag_tag::get_item_tags('core', 'course_modules', $activity_id);
    $selected_tag = false;
    foreach ($activity_tags as $a_tag){
        $activity_tag_list[] = $a_tag->rawname;
        $tags[$a_tag->id] = $a_tag->rawname;
        $selected_tag = $selected_tag || $tag == $a_tag->id;
    }
    if ($tag == 0 || ($tag == -1 && count($activity_tags) == 0)){
        return true;
    } else {
        return $selected_tag;
    }
}

function block_ned_teacher_tools_tags_print_course_menu($url, $tags, $tag){
    global $OUTPUT;

    $def_tags = ['0' => get_string('alltags', 'block_ned_teacher_tools'),
        '-1' => get_string('nonetags', 'block_ned_teacher_tools')];
    ksort($tags);
    $select = new single_select($url, 'tag', $def_tags + $tags, $tag, null, 'selecttag');
    $select->label = html_writer::tag('i','',
        ['class' => 'fa fa-tags', 'title' => get_string('choosetag', 'block_ned_teacher_tools')]);
    $output = html_writer::div($OUTPUT->render($select), 'groupselector tag-selector');

    return $output;
}

function block_ned_teacher_tools_supported_mods() {
    return array(
        'assign' => 'assign.submissions.fn.php',
        'quiz' => 'quiz.submissions.fn.php',
        'forum' => 'forum.submissions.fn.php',
        'journal' => 'journal.submissions.fn.php'
    );
}

function block_ned_teacher_tools_view_journal_submissions($journal, $students, $cm, $course, $pageparams, $show = 'marked') {
    global $DB, $CFG, $OUTPUT, $MB;

    $base_url = isset($MB) ? $MB->get_url() : new moodle_url('/blocks/ned_teacher_tools/fn_gradebook.php', $pageparams);
    $context = context_module::instance($cm->id);
    require_capability('mod/journal:manageentries', $context);

    $o = '';

    $studentlist = implode(',', array_keys($students));

    if ($journal->grade == 0) {
        $sql = "SELECT j.userid 
                      FROM {journal_entries} j 
                     WHERE j.journal = ? 
                       AND j.entrycomment IS NOT NULL 
                       AND j.userid IN ($studentlist)";
    } else {
        $sql = "SELECT j.userid 
                      FROM {journal_entries} j 
                     WHERE j.journal = ? 
                       AND j.rating IS NOT NULL 
                       AND j.userid IN ($studentlist)";
    }

    $attempts = $DB->get_records_sql($sql, array($journal->id));

    if ($show == 'unsubmitted') {
        $o .= NED\render_marking_manager_not_submitted($students, $attempts, $cm, $journal);
    } else {
        // make some easy ways to access the entries.
        $attempts = array_keys($attempts);
        if ($eee = $DB->get_records("journal_entries", array("journal" => $journal->id))) {
            foreach ($eee as $ee) {
                $entrybyuser[$ee->userid] = $ee;
            }
        } else {
            $entrybyuser = array();
        }

        foreach ($attempts as $studentid) {
            if (empty($entrybyuser[$studentid])) continue;

            $historyout = '';

            $item = $entrybyuser[$studentid];
            $student = $DB->get_record('user', array('id' => $item->userid));

            $groupname = '';

            if (!$gradeitem = $DB->get_record('grade_items', array('itemtype' => 'mod', 'itemmodule' => 'journal',
                'iteminstance' => $journal->id))) {
                $gradeitem = new stdClass();
                $gradeitem->gradepass = 0;
            }

            $header = '<table class="headertable"><tr>';

            $header .= '<td width="35px">'.$OUTPUT->user_picture($student).'</td>';
            $urlparams = array('id' => $item->userid, 'course' => $cm->course);
            $url = new moodle_url('/user/view.php', $urlparams);

            $header .= '<td><div style="color:white;">'.$OUTPUT->action_link($url, fullname($student),
                    null, array('target' => '_blank', 'class' => 'userlink')). $groupname. '</div>';
            $header .= '<div style="margin-top:5px; color:white;">Journal: <a target="_blank" class="marking_header_link"
            title="Journal" href="'.
                $CFG->wwwroot.'/mod/journal/view.php?id='.$journal->id.'">' .
                $journal->name.'</a></div></td>';
            $header .= '<td align="right" style="color:white;"></td>';

            $header .= '</tr></table>';


            $t = new html_table();
            $t->attributes['class'] = 'generaltable historytable';
            $cell = new html_table_cell($header);
            $cell->attributes['class'] = 'historyheader';
            $cell->colspan = 3;
            $t->data[] = new html_table_row(array($cell));

            $submittedicon = '<img width="16" height="16" border="0" alt="Journal" src="'.
                $OUTPUT->image_url('text', 'block_ned_teacher_tools').'" valign="absmiddle"> ';
            $markedicon = '<img width="16" height="16" border="0" alt="Journal" src="'.
                $OUTPUT->image_url('completed', 'block_ned_teacher_tools').'" valign="absmiddle"> ';
            $savedicon = '<img width="16" height="16" border="0" alt="Journal" src="'.
                $OUTPUT->image_url('saved', 'block_ned_teacher_tools').'" valign="absmiddle"> ';
            if ($gradeitem->gradepass > 0) {
                $markediconincomplete = '<img width="16" height="16" border="0" alt="Journal" src="'.
                    $OUTPUT->image_url('incomplete', 'block_ned_teacher_tools').'" valign="absmiddle"> ';
            } else {
                $markediconincomplete = '<img width="16" height="16" border="0" alt="Journal" src="'.
                    $OUTPUT->image_url('graded', 'block_ned_teacher_tools').'" valign="absmiddle"> ';
            }

            $lastsubmissionclass = '';

            $background = '';
            if ($journal->grade == 0) {
                $cell1 = new html_table_cell(get_string('nograde', 'block_ned_teacher_tools'));
            } else {
                if ($gradeitem->gradepass > 0) {
                    $background = ($item->rating  >= $gradeitem->gradepass) ? 'bg_green' : 'bg_orange';
                } else {
                    $background = 'bg_white';
                }
                $cell1 = new html_table_cell($item->rating . '/' . $journal->grade);
            }

            $cell1->rowspan = 2;
            $cell2 = new html_table_cell($submittedicon . get_string('submitted', 'assign'));

            $cell3 = new html_table_cell(userdate($item->modified));
            $url = new moodle_url($base_url);
            $url->param('user', $studentid);
            $url->param('expand', true);

            $cell3->text = '<div style="float:left;">'.$cell3->text.'</div>
                                <div style="float:right;">
                                <a href="'.$url->out(false).'">
                                <img width="16" height="16" border="0" alt="Assignment" src="'.
                $CFG->wwwroot.'/blocks/ned_teacher_tools/pix/fullscreen_maximize.gif" valign="absmiddle">
                                </a>
                                </div>';

            if ($journal->grade == 0) {
                $cell1->attributes['class'] = 'bg_grey';
                $cell2->attributes['class'] = 'bg_grey';
                $cell3->attributes['class'] = 'bg_grey';
            } else {
                $cell1->attributes['class'] = $background;
                $cell2->attributes['class'] = $background;
                $cell3->attributes['class'] = $background;
            }
            $t->data[] = new html_table_row(array($cell1, $cell2, $cell3));

            if ($journal->grade == 0) {
                $cell1 = new html_table_cell('<img width="16" height="16" border="0" alt="Journal" src="'.
                    $OUTPUT->image_url('graded', 'block_ned_teacher_tools').'" valign="absmiddle"> Marked');
            } else {
                $cell1 = new html_table_cell(((($gradeitem->gradepass > 0)
                        && ($item->rating >= $gradeitem->gradepass)) ? $markedicon : $markediconincomplete) . 'Marked');
            }


            $cell2 = new html_table_cell(userdate($item->timemarked));
            if ($journal->grade == 0) {
                $cell1->attributes['class'] = 'bg_grey';
                $cell2->attributes['class'] = 'bg_grey';
            } else {
                $cell1->attributes['class'] = $background;
                $cell2->attributes['class'] = $background;
            }
            $t->data[] = new html_table_row(array($cell1, $cell2));


            $historyout .= html_writer::table($t);

            if ($historyout) {
                $o .= $OUTPUT->box_start('generalbox submissionhistory_summary');
                $o .= $historyout;
                $o .= $OUTPUT->box_end();
            }
        }
    }

    return $o;
}

function block_ned_teacher_tools_assignment_get_notsubmittedany($courseid, $id = "0", $users = null, $timestart=0) {
    global $DB;
    // Split out users array.
    if ($users) {
        $userids = array_keys($users);
        $userselect = ' AND u.id IN (' . implode(',', $userids) . ')';
        $studentswithsubmissions = $DB->get_records_sql("SELECT DISTINCT u.id as userid,
                                                                  u.firstname,
                                                                  u.lastname,
                                                                  u.email,
                                                                  u.picture,
                                                                  u.imagealt
                                                             FROM {assignment_submissions} asb
                                                             JOIN {assignment} a
                                                               ON a.id = asb.assignment
                                                            JOIN {user} u ON u.id = asb.userid
                                          WHERE asb.timemodified > $timestart AND a.id = $id
                                                $userselect");
        return $studentswithsubmissions;
    }
    return [];
}

function block_ned_teacher_tools_assign_get_notsubmittedany($courseid, $instanceid, $users, $timestart) {
    global $DB;

    if (empty($users)) {
        return array();
    }
    [$insql, $params] = $DB->get_in_or_equal(array_keys($users));
    $params[] = $instanceid;
    $params[] = $timestart;

    $sql = "SELECT DISTINCT asub.userid
              FROM {assign_submission} asub
             WHERE asub.userid {$insql}
               AND asub.assignment = ?
               AND asub.timemodified > ?";
    return $DB->get_records_sql($sql, $params);
}

function block_ned_teacher_tools_forum_get_notsubmittedany($courseid, $instanceid, $users, $timestart) {
    global $DB;

    if (empty($users)) {
        return array();
    }
    [$insql, $params] = $DB->get_in_or_equal(array_keys($users));
    $params[] = $timestart;
    $params[] = $instanceid;

    $sql = "SELECT DISTINCT u.id userid,
                   u.firstname,
                   u.lastname,
                   u.email,
                   u.picture,
                   u.imagealt
              FROM {forum_posts} p
              JOIN {forum_discussions} d
                ON d.id = p.discussion
              JOIN {forum} f
                ON f.id = d.forum
              JOIN {user} u
                ON u.id = p.userid
             WHERE u.id {$insql}
               AND p.created > ?
               AND f.id = ?";
    return $DB->get_records_sql($sql, $params);
}

function block_ned_teacher_tools_quiz_get_notsubmittedany($courseid, $instanceid, $users, $timestart) {
    global $DB;

    if (empty($users)) {
        return array();
    }
    [$insql, $params] = $DB->get_in_or_equal(array_keys($users));
    $params[] = $instanceid;
    $params[] = $timestart;

    $sql = "SELECT DISTINCT qa.userid 
              FROM {quiz_attempts} qa
             WHERE qa.userid {$insql}
               AND qa.quiz = ?
               AND qa.timemodified > ?";
    return $DB->get_records_sql($sql, $params);
}

/**
 * @param $itemtype
 * @param $itemid
 * @param $newvalue
 * @return null|\core\output\inplace_editable
 * @throws coding_exception
 * @throws dml_exception
 * @throws invalid_parameter_exception
 * @throws required_capability_exception
 * @throws restricted_context_exception
 */
function block_ned_teacher_tools_inplace_editable($itemtype, $itemid, $newvalue) {
    global $DB;

    $itemtypes = ['schedule', 'startdate', 'enddate', 'userstartdate', 'userenddate'];

    if (in_array($itemtype, $itemtypes)) {
        if ($itemtype === 'schedule') {
            $group = $DB->get_record('groups', array('id' => $itemid), '*', MUST_EXIST);
            $coursecontext = context_course::instance($group->courseid);
            \external_api::validate_context($coursecontext);
            require_capability('block/ned_teacher_tools:manage_group_requirements', $coursecontext);

            $scheduleoptions = DM::get_schedule_options();

            $newvalue = clean_param($newvalue, PARAM_INT);
            $DB->update_record('groups', (object)['id' => $group->id, $itemtype => $newvalue]);

            cache_helper::invalidate_by_definition('core', 'groupdata', array(), array($group->courseid));

            $group->schedule = $newvalue;

            return new \core\output\inplace_editable('block_ned_teacher_tools', 'schedule', $group->id, true,
                $scheduleoptions[$group->schedule], $group->schedule);
        } else if ($itemtype === 'startdate' || $itemtype === 'enddate') {
            $group = $DB->get_record('groups', array('id' => $itemid), '*', MUST_EXIST);
            $coursecontext = context_course::instance($group->courseid);
            \external_api::validate_context($coursecontext);
            require_capability('block/ned_teacher_tools:manage_group_requirements', $coursecontext);

            $newvalue = clean_param($newvalue, PARAM_TEXT);

            if (empty($newvalue)) {
                $newvalue = 0;
            } else {
                $newvalue = strtotime($newvalue);
            }

            $valid = true;
            if ($newvalue !== false && $newvalue != $group->{$itemtype}) {
                if ($itemtype == 'startdate' && !empty($group->enddate)) {
                    if ($newvalue >= $group->enddate) {
                        $valid = false;
                    }
                }
                if ($itemtype == 'enddate' && !empty($group->startdate)) {
                    if ($newvalue <= $group->startdate) {
                        $valid = false;
                    }
                }
                if ($valid) {
                    $DB->update_record('groups', (object)['id' => $group->id, $itemtype => $newvalue]);
                    cache_helper::invalidate_by_definition('core', 'groupdata', array(), array($group->courseid));
                    $group->{$itemtype} = $newvalue;

                    if ($itemtype == 'enddate' && $group->schedule == DM::SCHEDULE_FULL) {
                        if ($syncdmenddateswithenrolment = get_config('block_ned_teacher_tools', 'syncdmenddateswithenrolment')) {
                            $blockcfg = block_ned_teacher_tools_get_block_config($group->courseid);
                            DM::sync_group_enrollments($group, $blockcfg->enrolmentenddaterole, $blockcfg->setenrolmentenddayduration);
                            $cache = cache::make('report_ghs', 'group');
                            $cache->set($group->id, $group);
                        }
                    }
                }
            }

            if ($valid) {
                $date = ($group->{$itemtype}) ? date('j M Y', $group->{$itemtype}) : '';
            } else {
                $date = ($group->{$itemtype}) ? date('j M Y', $group->{$itemtype}) : '';
            }

            return new \core\output\inplace_editable('block_ned_teacher_tools', $itemtype, $group->id, true,
                $date, $date);
        } else if ($itemtype === 'userstartdate' || $itemtype === 'userenddate') {
            $arr = explode('_', $itemid);
            $uitemid = $arr[0];
            $userid = $arr[1];
            $courseid = $arr[2];

            $coursecontext = context_course::instance($courseid);
            \external_api::validate_context($coursecontext);

            if ($itemtype === 'userstartdate') {
                $itemtype = 'startdate';
            }

            if ($itemtype === 'userenddate') {
                $itemtype = 'enddate';
            }

            if (!$userdates = $DB->get_record('block_ned_teacher_tools_usrd', ['id' => $uitemid])) {
                $userdates = $DB->get_record('block_ned_teacher_tools_usrd', ['userid' => $userid, 'courseid' => $courseid]);
            }

            $newvalue = clean_param($newvalue, PARAM_TEXT);

            if (empty($newvalue)) {
                $newvalue = 0;
            } else {
                $newvalue = strtotime($newvalue);
            }

            if ($newvalue !== false) {
                if (empty($userdates)) {
                    $rec = new stdClass();
                    $rec->userid = $userid;
                    $rec->courseid = $courseid;
                    $rec->{$itemtype} = $newvalue;
                    $rec->timecreated = time();
                    $DB->insert_record('block_ned_teacher_tools_usrd', $rec);
                    $valid = true;
                } else {
                    if ($newvalue != $userdates->{$itemtype}) {
                        $valid = true;
                        if ($itemtype == 'startdate' && !empty($userdates->enddate)) {
                            if ($newvalue >= $userdates->enddate) {
                                $valid = false;
                            }
                        }
                        if ($itemtype == 'enddate' && !empty($userdates->startdate)) {
                            if ($newvalue <= $userdates->startdate) {
                                $valid = false;
                            }
                        }
                        if ($valid) {
                            $userdates->{$itemtype} = $newvalue;
                            $userdates->timemodified = time();
                            $DB->update_record('block_ned_teacher_tools_usrd', $userdates);
                        }
                    }
                }
            }

            if (!empty($valid)){
                $date = ($newvalue) ? date('j M Y', $newvalue) : '';
            } else {
                $date = ($userdates->{$itemtype}) ? date('j M Y', $userdates->{$itemtype}) : '';
            }

            return new \core\output\inplace_editable('block_ned_teacher_tools', 'user'.$itemtype, $itemid, true,
                $date, $date);
        }
    }
    return null;
}

/**
 * Return config for block on course page by course id
 * Don't use it in a course loop!
 *
 * @param        $course_id
 *
 * @param string $blockname
 *
 * @return bool|\stdClass
 */
function block_ned_teacher_tools_get_block_config($course_id, $blockname=SH::TT_NAME){
    if ($blockname == SH::TT_NAME){
        return SH::get_tt_block_config($course_id);
    } else {
        return SH::get_block_config($course_id, $blockname);
    }
}

/**
 * Save config for block, return true if success
 *
 * @param        $course_id
 * @param        $config
 * @param string $blockname
 *
 * @return bool
 */
function block_ned_teacher_tools_set_block_config($course_id, $config, $blockname=SH::TT){
    return SH::save_block_config($course_id, $blockname, $config);
}

/**
 * Get block_ned_teacher_tools config for course, based on site block_ned_teacher_tools config and course block settings
 * @param $course_id
 *
 * @return object
 */
function block_ned_teacher_tools_get_config($course_id){
    $config = NED\get_site_and_course_block_config($course_id, SH::TT);
    $config->enableresubmissions = SH::is_resubmission_enabled($course_id);
    $config->resubmission_assignments = $config->enableresubmissions ?
        \local_ned_controller\tt_config_manager::get_enabled_resubmission_activities($course_id) : [];

    return $config;
}

/**
 * Serve the new group form as a fragment.
 *
 * @param array $args List of named arguments for the fragment loader.
 * @return string
 */
function block_ned_teacher_tools_output_fragment_user_extension_form($args) {
    $args = (object) $args;
    $context = $args->context;
    $o = '';

    $formdata = [];
    if (!empty($args->jsonformdata) && $args->jsonformdata != '{}') {
        $serialiseddata = json_decode($args->jsonformdata);
        parse_str($serialiseddata, $formdata);
    }

    $mform = new \block_ned_teacher_tools\form\user_extension(null,
        ['userid' => $args->userid, 'cmid' => $args->cmid, 'add_new' => $args->add_new, 'context' => $context],
        'post', '', null, true, $formdata);

    if (!empty($args->jsonformdata)) {
        // If we were passed non-empty form data we want the mform to call validation functions and show errors.
        $mform->is_validated();
    }
    $o .= $mform->draw();

    return $o;
}

/**
 * Serve the new group form as a fragment.
 *
 * @param array $args List of named arguments for the fragment loader.
 *
 * @return string
 */
function block_ned_teacher_tools_output_fragment_class_extension_form($args){
    $args = (object) $args;
    $o = '';

    $formdata = [];
    if (!empty($args->jsonformdata) && $args->jsonformdata != '{}') {
        $serialiseddata = json_decode($args->jsonformdata);
        parse_str($serialiseddata, $formdata);
    }

    $extendclassstudentenddate = DM::can_extend_group_user_enddate($args->context);
    if ($extendclassstudentenddate) {
        $mform = new \block_ned_teacher_tools\form\class_extension(null, ['groupid' => $args->groupid], 'post', '', null, true, $formdata);

        if (!empty($args->jsonformdata)) {
            // If we were passed non-empty form data we want the mform to call validation functions and show errors.
            $mform->is_validated();
        }

        ob_start();
        $mform->display();
        $o .= ob_get_contents();
        ob_end_clean();
    }

    return $o;
}

/**
 * Serve the new group form as a fragment.
 *
 * @param array $args List of named arguments for the fragment loader.
 *
 * @return string
 */
function block_ned_teacher_tools_output_fragment_student_extension_form($args){
    $args = (object) $args;
    $o = '';

    $formdata = [];
    if (!empty($args->jsonformdata) && $args->jsonformdata != '{}') {
        $serialiseddata = json_decode($args->jsonformdata);
        parse_str($serialiseddata, $formdata);
    }

    if (!DM::can_extend_group_user_enddate($args->context)) return $o;

    $courseid = $args->context->instanceid;
    $mform = new \block_ned_teacher_tools\form\student_extension(null,
        ['studentid' => $args->studentid, 'groupid' => $args->groupid, 'courseid' => $courseid],
        'post', '', null, true, $formdata);

    if (!empty($formdata)) {
        // If we were passed non-empty form data we want the mform to call validation functions and show errors.
        $mform->is_validated();
    }

    ob_start();
    $mform->display();
    $o .= ob_get_contents();
    ob_end_clean();

    return $o;
}

/**
 * @param \stdClass $course
 * @param int       $groupid
 *
 * @return array $kica_stats ['all', 'incomplete', 'a_mismatch', 'a_flagged', 'a_ungraded', 'u_mismatch', 'u_flagged', 'u_ungraded']
 *               prefix 'a_' - count of activities, 'u_' - count of users in all this activities
 */
function block_ned_teacher_tools_get_kica_activity_stats(stdClass $course, $groupid=0){
    $kica_stats = [
        'all' => 0,
        'incomplete' => 0,
        'a_mismatch' => 0,
        'a_flagged' => 0,
        'a_ungraded' => 0,
        'u_mismatch' => 0,
        'u_flagged' => 0,
        'u_ungraded' => 0,
    ];
    $courseid = $course->id;
    $kica = SH::get_kica($courseid);
    if (!$kica){
        return $kica_stats;
    }

    $pullfromgradebook = $kica->pullfromgradebook ?? null;
    $users =  NED\get_students($course, 0, $groupid);
    $kicaitems = SH::ki_get_all_by_course($courseid);
    if (empty($kicaitems)){
        return $kica_stats;
    }

    $kica_stats['all'] = count($kicaitems);
    foreach ($kicaitems as $kicaitem) {
        $controller = $kicaitem->get_grading_controller();
        if ($kicaitem->incomplete) {
            $kica_stats['incomplete']++;
        }

        $l_stats = [
            'a_mismatch' => false,
            'a_flagged' => false,
            'a_ungraded' => false,
        ];

        foreach ($users as $user) {
            $kicagrade = SH::kg_get_by_userid_itemid($user, $kicaitem);
            if (empty($kicagrade->id)) continue;

            if ($pullfromgradebook){
                if ($kicagrade->grade_mismatch() ||
                    KICA\helper::is_gradingform_needupdate($kicaitem, $user->id, $controller)) {
                    $l_stats['a_mismatch'] = true;
                    $kica_stats['u_mismatch']++;
                }
            }

            if ($kicagrade->flag){
                $l_stats['a_flagged'] = true;
                $kica_stats['u_flagged']++;
            }

            if ($kicagrade->is_gradable() && !($kicagrade->is_graded() || $kicagrade->grade_mismatch() || $kicagrade->flag)) {
                $l_stats['a_ungraded'] = true;
                $kica_stats['u_ungraded']++;
            }
        }

        foreach ($l_stats as $key => $stat){
            $kica_stats[$key] += (int)$stat;
        }

    }

    return $kica_stats;
}

/**
 * Check whether a user has a particular capability for deadline manager
 * @param context_course $coursecontext
 *
 * @return bool
 * @throws \coding_exception
 */
function block_ned_teacher_tools_has_deadline_capability($coursecontext){
    $viewallgroups = has_capability('block/ned_teacher_tools:view_all_groups', $coursecontext);
    $viewowngroups = has_capability('block/ned_teacher_tools:view_own_groups', $coursecontext);
    $deadlineviewonly = has_capability('block/ned_teacher_tools:deadlineviewonly', $coursecontext);

    return $viewallgroups || $viewowngroups || $deadlineviewonly;
}

/**
 * Add group selector for TT block and return current group
 * @param \stdClass $course
 * @param           $block_content
 * @param           $url
 *
 * @return false|int|mixed
 */
function block_ned_teacher_tools_group_for_block(stdClass $course, &$block_content, $url){
    global $_GET, $OUTPUT;
    $gr = NED\GROUP_NONE;
    $add_item = false;
    do{
        $context = \context_course::instance($course->id, MUST_EXIST);
        $isteacher = has_capability('moodle/grade:viewall', $context);
        if (!$isteacher){
            break;
        }

        [$enrolledusers, $allowedgroups] = block_ned_teacher_tools_get_users_and_groups($course, false);
        if (empty($allowedgroups)){
            break;
        }

        if (count($allowedgroups) == 1){
            $group = reset($allowedgroups);
            $add_item = $group->name;
            $gr = $group->id;
            NED\set_cache_group($course->id, $gr);
            break;
        }

        $par_name = 'block_ned_teacher_tools_group';
        $options = [NED\GROUP_NONE => NED\str('selectgroup'), NED\GROUP_ALL => get_string('all')];
        foreach ($allowedgroups as $group){
            $options[$group->id] = $group->name;
        }

        if (isset($_GET[$par_name]) && isset($options[$_GET[$par_name]])){
            $gr = $_GET[$par_name];
            NED\set_cache_group($course->id, $gr);
        } else {
            $gr = NED\get_cache_group($course->id);
            if ($gr === false){
                $gr = NED\GROUP_NONE;
                NED\set_cache_group($course->id, $gr);
            }
        }
        $add_item = $OUTPUT->render(new single_select($url, $par_name, $options, $gr, null, $par_name)) .
            $OUTPUT->help_icon('selectgroup', NED\PLUGIN_NAME);

    } while(false);

    if ($add_item){
        $block_content->items[] = html_writer::div($add_item, 'block-group');
        $block_content->icons[] = html_writer::div(NED\img('i/users'), 'block-group');
    }
    return $gr;
}

/**
 * Return single_checkbox for "kica_activities"
 * @param             $value
 * @param \moodle_url $url
 * @param string      $name
 *
 * @return string
 */
function block_ned_teacher_tools_render_kica_activities_checkbox($value, \moodle_url $url, $name='kica_activities'){
    return SH::single_checkbox($url, $value, $name, NED\str('showkicaactivities'), 'kica_activities-checkbox');
}

/**
 * @param $courseid
 * @return array
 * @throws moodle_exception
 */
function block_ned_teacher_tools_get_course_metadata($courseid) {
    $handler = \core_customfield\handler::get_handler('core_course', 'course');
    $datas = $handler->get_instance_data($courseid, true);
    $metadata = [];
    foreach ($datas as $data) {
        if (empty($data->get_value())) {
            continue;
        }
        $metadata[$data->get_field()->get('shortname')] = [
            'export_value' => $data->export_value(),
            'value' => $data->get_value()
        ];
    }
    return $metadata;
}

function block_ned_teacher_tools_remove_inactive_deadlines($courseid){
    global $DB;
    $cmids = CM::get_enabled_dm_activities($courseid);
    $where = ['cm.course = :course'];
    $params = ['course' => $courseid];
    if (!empty($cmids)){
        [$sql, $params_cmids] = $DB->get_in_or_equal($cmids, SQL_PARAMS_NAMED, 'param', false);
        $where[] = "cm.id " . $sql;
        $params = array_merge($params, $params_cmids);
    }
    $sql = "DELETE qo, ao
        FROM {course_modules} cm
        JOIN {modules} m
            ON m.id = cm.module
        LEFT JOIN {assign_overrides} AS ao
            ON ao.assignid = cm.instance
            AND m.name = 'assign'
        LEFT JOIN {quiz_overrides} AS qo
            ON qo.quiz = cm.instance
            AND m.name = 'quiz'
        ";
    $where = !empty($where) ? ("\nWHERE (" . join(') AND (', $where) . ')') : '';
    $DB->execute($sql.$where, $params);
}

/**
 * Get the current user preferences that are available
 *
 * @return Array preferences configuration
 */
function block_ned_teacher_tools_user_preferences() {
    global $CFG;

    $choices = core_date::get_list_of_timezones($CFG->timezone, true);
    return [
        'block_ned_teacher_tools_dm_timezone' => [
            'choices' => array_keys($choices),
            'type' => PARAM_TEXT,
            'null' => NULL_NOT_ALLOWED,
            'default' => SH::NED_TIMEZONE,
        ],
    ];
}

/**
 * This hook is called from lib/externallib.php and webservice/lib.php
 *
 * @param $externalfunctioninfo
 * @param $params
 *
 * @return bool|mixed
 */
function block_ned_teacher_tools_override_webservice_execution($externalfunctioninfo, $params){
    $classname = $externalfunctioninfo->classname ?? '';
    $methodname = $externalfunctioninfo->methodname ?? '';
    $result = false;

    if ($classname == 'mod_assign_external' && $methodname == 'submit_grading_form'){
        // rewrite mod_assign_external call for grading_tracker
        $classname = "\\block_ned_teacher_tools\\$classname";
        $callable = [$classname, $methodname];
        $result = call_user_func_array($callable, $params);
        if ($result === false){
            $result = 0;
        }
    }

    return $result;
}

/**
 * Return result for the modal_form
 * See AMD module block_ned_teacher_tools\modal_form for the details
 *
 * @param array $args List of named arguments for the fragment loader.
 * @return string
 */
function block_ned_teacher_tools_output_fragment_modal_form($args) {
    $args = (object) $args;
    $classname = $args->classname ?? '';
    $methodname = $args->methodname ?? '';

    $result = null;
    if (class_exists($classname) && method_exists($classname, $methodname) && is_callable([$classname, $methodname])){
        $callable = [$classname, $methodname];
        $formdata = [];
        $data = [];
        if (!empty($args->jsonformdata) && $args->jsonformdata != '{}') {
            $serialiseddata = json_decode($args->jsonformdata);
            parse_str($serialiseddata, $formdata);
            $formdata = (array)$formdata;
        }

        if (!empty($args->data) && $args->data != '{}') {
            $data = json_decode($args->data);
            $data = (array)$data;
        }
        $params = ['context' => $args->context, 'formdata' => $formdata, 'data' => $data, 'confirm' => ($args->confirm ?? false),];
        $result = call_user_func_array($callable, $params);
    }

    if (is_array($result)){
        if (!isset($result['body']) || $result['body'] === true){
            $result['body'] = 'OK';
        }
        $result = json_encode($result);
    } elseif ($result === true){
        return "OK";
    }

    return $result;
}

/**
 * Update course_modules in the course
 *
 * @param \stdClass $course
 *
 */
function block_ned_teacher_tools_update_all_cm_by_course($course){
    $mi = get_fast_modinfo($course->id);
    $ci = new completion_info($course);
    $activities = $mi->get_cms();

    $progressbar = new \progress_bar('update_cm_'.$course->id, 500, true);
    $a = array(
        'count' => count($activities),
        'done'  => 0,
    );

    foreach ($activities as $cm) {
        if (!$cm->visible) {
            $a['count']--;
            continue;
        }
        $a['done']++;
        $a['name'] = $cm->name;
        $progressbar->update($a['done'], $a['count'], NED\str('recalculationactivity', $a));
        $ci->reset_all_state($cm);
    }

    $progressbar->update($a['done'], $a['count'], NED\str('recalculatedsuccessfully', $a));
}

/**
 * Called from @see \course_delete_module()
 * For after-deleting activity event @see \block_ned_teacher_tools_observer::core_course_module_deleted()
 *
 * @param object $cm
 *
 * @noinspection PhpUnused
 */
function block_ned_teacher_tools_pre_course_module_delete($cm){
    DM::course_module_pre_deleted_event($cm);
    CM::delete_config_by_cmids([$cm->id]);
}

/**
 * Set up delete extensions by course
 *
 * @param stdClass $course
 *
 * @noinspection PhpUnused
 */
function block_ned_teacher_tools_pre_course_delete($course){
    $data = [SH::PAR_COURSE => $course->id];
    DM::create_delete_extension_task($data, true);
    CM::delete_config_by_course($course->id);
}

/**
 * Set up delete extensions by course
 *
 * @param stdClass $user
 *
 * @noinspection PhpUnused
 */
function block_ned_teacher_tools_pre_user_delete($user){
    $data = [SH::PAR_USER => $user->id];
    DM::create_delete_extension_task($data, true);
}

/**
 * Class block_ned_teacher_tools_block_content
 * only used by IDE
 */
class block_ned_teacher_tools_block_content{
    public $items = [];
    public $icons = [];
    public $text = '';
    public $footer = '';
}
