<?PHP

// $Id: lib.php,v 1.1 2012/07/10 05:37:08 sunshine Exp $
/// Library of functions and constants for module programming
/// (replace programming with the name of your module and delete this line)
require_once($CFG->dirroot . '/lib/datalib.php');

define('PROGRAMMING_STATUS_NEW', 0);
define('PROGRAMMING_STATUS_COMPILING', 1);
define('PROGRAMMING_STATUS_COMPILEOK', 2);
define('PROGRAMMING_STATUS_RUNNING', 3);
define('PROGRAMMING_STATUS_WAITING', 4);
define('PROGRAMMING_STATUS_FINISH', 10);
define('PROGRAMMING_STATUS_COMPILEFAIL', 11);
define('PROGRAMMING_MAX_ATTEMPTS', 6);

define('PROGRAMMING_SHOWMODE_NORMAL', 1);
define('PROGRAMMING_SHOWMODE_CONTEST', 2);

define('PROGRAMMING_RESEMBLE_DELETED', -1);
define('PROGRAMMING_RESEMBLE_NEW', 0);
define('PROGRAMMING_RESEMBLE_WARNED', 1);
define('PROGRAMMING_RESEMBLE_CONFIRMED', 2);
define('PROGRAMMING_RESEMBLE_FLAG1', 11);
define('PROGRAMMING_RESEMBLE_FLAG2', 12);
define('PROGRAMMING_RESEMBLE_FLAG3', 13);

define('PROGRAMMING_TEST_SHOWAFTERDISCOUNT', -2);
define('PROGRAMMING_TEST_HIDDEN', -1);
define('PROGRAMMING_TEST_SHOWINRESULT', 0);
define('PROGRAMMING_TEST_SHOW', 1);

define('PROGRAMMING_PRESET_BEGIN', "/* PRESET CODE BEGIN - NEVER TOUCH CODE BELOW */");
define('PROGRAMMING_PRESET_END', "/* PRESET CODE END - NEVER TOUCH CODE ABOVE */");

define('PROGRAMMING_RANGE_ALL', 0);
define('PROGRAMMING_RANGE_LATEST', 1);

function programming_add_instance($programming) {
/// Given an object containing all the necessary data, 
/// (defined by the form in mod.html) this function 
/// will create a new instance and return the id number 
/// of the new instance.
    global $DB;

    $programming->timemodified = time();

    if ($programming->inputs == 0)
        $programming->inputfile = '';
    if ($programming->outputs == 0)
        $programming->outputfile = '';
    $id = $DB->insert_record('programming', $programming);

    if (isset($programming->langlimit) && is_array($programming->langlimit)) {
        foreach ($programming->langlimit as $lang) {
            $pl = new stdClass();
            $pl->programmingid = $id;
            $pl->languageid = $lang;
            $DB->insert_record('programming_langlimit', $pl);
        }
    }
    if (isset($programming->category) && is_array($programming->category)) {
        foreach ($programming->category as $cat) {
            $pc = new stdClass();
            $pc->pid = $id;
            $pc->catid = $cat;
            $DB->insert_record('programming_catproblemmap', $pc);
        }
    }
    global $CFG;
    if (!function_exists('grade_update')) { //workaround for buggy PHP versions
        require_once($CFG->libdir . '/gradelib.php');
    }
    $params = array('itemname' => $programming->name,
        'grademax' => $programming->grade,
        'grademin' => 0,
        'gradetype' => GRADE_TYPE_VALUE);
    grade_update('mod/programming', $programming->course, 'mod', 'programming', $id, 0, NULL, $params);
    return $id;
}

function programming_update_instance($programming) {
/// Given an object containing all the necessary data, 
/// (defined by the form in mod.html) this function 
/// will update an existing instance with new data.
    global $DB;

    $programming->timemodified = time();
    $programming->id = $programming->instance;

    if ($programming->inputs == 0)
        $programming->inputfile = '';
    if ($programming->outputs == 0)
        $programming->outputfile = '';

    if (isset($programming->keeplatestonly) and $programming->keeplatestonly) {
        programming_delete_old_submits($programming->id);
    }

    // Update the langlimit table
    $changed = false;
    if (isset($programming->langlimit) && is_array($programming->langlimit)) {
        $newlangs = $programming->langlimit;
    } else {
        $newlangs = array();
    }

    $langs = array();
    $rows = $DB->get_records('programming_langlimit', array('programmingid' => $programming->id));
    if (is_array($rows)) {
        foreach ($rows as $row) {
            $langs[] = $row->languageid;
        }
    }

    foreach (array_diff($langs, $newlangs) as $lang) {
        $DB->delete_records('programming_langlimit', array('programmingid' => $programming->id, 'languageid' => $lang));
        $changed = true;
    }

    foreach (array_diff($newlangs, $langs) as $lang) {
        $pl = new stdClass();
        $pl->programmingid = $programming->id;
        $pl->languageid = $lang;
        $DB->insert_record('programming_langlimit', $pl);
        $changed = true;
    }

    if (isset($programming->category) && is_array($programming->category)) {
        $newcats = $programming->category;
    } else {
        $newcats = array();
    }

    $cats = array();
    $rows1 = $DB->get_records('programming_catproblemmap', array('pid' => $programming->id));
    if (is_array($rows1)) {
        foreach ($rows1 as $row) {
            $cats[] = $row->catid;
        }
    }

    foreach (array_diff($cats, $newcats) as $cat) {
        $DB->delete_records('programming_catproblemmap', array('pid' => $programming->id, 'catid' => $cat));
        $changed = true;
    }

    foreach (array_diff($newcats, $cats) as $cat) {
        $pc = new stdClass();
        $pc->pid = $programming->id;
        $pc->catid = $cat;
        $DB->insert_record('programming_catproblemmap', $pc);
        $changed = true;
    }
    global $CFG;
    if (!function_exists('grade_update')) { //workaround for buggy PHP versions
        require_once($CFG->libdir . '/gradelib.php');
    }
    $params = array('itemname' => $programming->name,
        'grademax' => $programming->grade,
        'grademin' => 0,
        'gradetype' => GRADE_TYPE_VALUE);
    grade_update('mod/programming', $programming->course, 'mod', 'programming', $programming->id, 0, NULL, $params);
    return $DB->update_record('programming', $programming) or $changed;
}

function programming_delete_instance($id) {
/// Given an ID of an instance of this module, 
/// this function will permanently delete the instance 
/// and any data that depends on it.  
    global $DB;

    if (!$programming = $DB->get_record('programming', array('id' => $id))) {
        return false;
    }

    $result = true;

    //判断是否有引用该实例 如果没有则删除
    $sql = "SELECT 
                * FROM {modules} m,{course_modules} cm 
            WHERE m.id=cm.module AND m.name='programming' AND cm.instance=?";
    $params = array($programming->id);
    $ps = $DB->get_records_sql($sql, $params);
    if (count($ps) <= 1) {

        # Delete any dependent records here #
        if (!$DB->delete_records('programming_langlimit', array('programmingid' => $programming->id))) {
            $result = false;
        }

        if (!$DB->delete_records('programming', array('id' => $programming->id))) {
            $result = false;
        }
    }
    return $result;
}

function programming_user_outline($course, $user, $mod, $programming) {
/// Return a small object with summary information about what a 
/// user has done with a given particular instance of this module
/// Used for user activity reports.
/// $return->time = the time they did it
/// $return->info = a short text description

    global $DB;

    $return = new stdClass();
    $params = array($programming->id, $user->id);
    $sql = "SELECT ps.*
          FROM {programming_submits} AS ps,
               {programming_result} AS pr
         WHERE pr.programmingid = ?
           AND pr.userid = ?
           AND pr.latestsubmitid = ps.id";
    $ps = $DB->get_records_sql($sql, $params);

    if (!empty($ps)) {
        $ps = array_shift($ps);
        if ($programming->showmode == PROGRAMMING_SHOWMODE_NORMAL) {
            if ($ps->status == PROGRAMMING_STATUS_FINISH) {
                $return->info = programming_get_test_results_short($ps);
            } else {
                $return->info = programming_get_submit_status_short($ps);
            }
        } else {
            if ($ps->status == PROGRAMMING_STATUS_FINISH) {
                $results = $DB->get_records('programming_test_results', array('submitid' => $ps->id));
                $return->info = programming_contest_get_judgeresult($results);
            } else if ($ps->status == PROGRAMMING_STATUS_COMPILEFAIL) {
                $return->info = get_string('CE', 'programming');
            }
        }
        $return->time = $ps->timemodified;
    }

    return $return;
}

function programming_user_complete($course, $user, $mod, $programming) {
/// Print a detailed representation of what a  user has done with 
/// a given particular instance of this module, for user activity reports.

    return true;
}

function programming_print_recent_activity($course, $isteacher, $timestart) {
/// Given a course and a time, this module should find recent activity 
/// that has occurred in programming activities and print it out. 
/// Return true if there was output, or false is there was none.

    global $CFG;

    return false;  //  True if anything was printed, otherwise false 
}

function programming_cron() {
/// Function to be run periodically according to the moodle cron
/// This function searches for things that need to be done, such 
/// as sending out mail, toggling flags etc ... 

    global $CFG;

    return true;
}

function programming_grades($programmingid) {
/// Must return an array of grades for a given instance of this module, 
/// indexed by user.  It also returns a maximum allowed grade.
///
///    $return->grades = array of grades;
///    $return->maxgrade = maximum allowed grade;
///
///    return $return;

    global $CFG, $DB;

    $return = new stdClass;
    $programming = $DB->get_record('programming', array('id' => $programmingid));
    $return->maxgrade = $programming->grade; // find the maximum allowed grade;
    $return->grades = array();

    // get the summary of weight of all the test
    $select = 'programmingid = :pid';
    $params = array('pid' => $programmingid);
    $total_weight = $DB->get_field_select('block_rss_client', 'SUM(weight)', $select, $params);
    $total_weight = $total_weight ? 1.0 / $total_weight : 0;

    // get weight summary of each submit
    $params = array($programming->grade, $total_weight, $programming->discount,
        $programmingid, $programmingid, $programmingid, $programming->timediscount,
        $programmingid, $programmingid);

    $query = "
        SELECT w.userid AS userid,
               round(? * w.weight * ? * (1 - d.discount * (1 - ? / 10.0)), 2) AS grade
          FROM (
            SELECT s.userid AS userid,
                   SUM(tr.passed * t.weight) AS weight
              FROM {programming_result} AS r,
                   {programming_submits} AS s,
                   {programming_test_results} AS tr,
                   {programming_tests} AS t
             WHERE r.programmingid = ?
               AND s.programmingid = ?
               AND t.programmingid = ?
               AND r.latestsubmitid=s.id
               AND s.id=tr.submitid
               AND tr.testid=t.id
          GROUP BY s.userid) AS w, (
            SELECT s.userid AS userid,
                   s.timemodified > ? AS discount
              FROM {programming_result} AS r,
                   {programming_submits} AS s
             WHERE r.programmingid = ?
               AND s.programmingid = ?
               AND r.latestsubmitid=s.id
          GROUP BY s.userid) AS d
         WHERE d.userid = w.userid
      ";
    //print $query;
    $grades = $DB->get_records_sql($query, $params);

    if (is_array($grades)) {
        foreach ($grades as $grade) {
            $return->grades[$grade->userid] = $grade->grade;
        }
    }

    return $return;
}

/**
 * Update the grade of a submission.
 *
 * @param $submitid ID of submission.
 */
function programming_update_grade($submitid) {
    global $CFG, $DB;

    $submit = $DB->get_record('programming_submits', array('id' => $submitid));
    if (empty($submit))
        return;

    # do not update grade if this is not the latest submission
    $rst = $DB->get_record('programming_result', array('programmingid' => $submit->programmingid, 'userid' => $submit->userid));
    if ($rst->latestsubmitid != $submitid)
        return;

    $p = $DB->get_record('programming', array('id' => $submit->programmingid));
    if (empty($p))
        return;

    $grade = 0;
    if ($submit->judgeresult != 'CE') {
        // get the summary of weight of all the test
        $total_weight = $DB->get_field_select('programming_tests', 'SUM(weight)', 'programmingid=?', array($submit->programmingid));
        if ($total_weight == 0)
            $total_weight = 1;

        $sql = "SELECT SUM(pt.weight) AS weight
                      FROM {programming_test_results} AS ptr,
                           {programming_tests} AS pt
                     WHERE ptr.submitid = ?
                       AND ptr.passed = 1
                       AND pt.programmingid = ?
                       AND ptr.testid = pt.id";
        $weight = $DB->get_field_sql($sql, array($submit->id, $submit->programmingid));
        $grade = $p->grade * $weight / $total_weight;
        if ($submit->timemodified > $p->timediscount) {
            $grade = $grade * $p->discount / 10.0;
        }
    }
    programming_grade_item_update($p, $submit, $grade);
}

function programming_grade_item_update($programming, $submit, $grade) {
    global $CFG;

    if (!function_exists('grade_update')) { //workaround for buggy PHP versions
        require_once($CFG->libdir . '/gradelib.php');
    }

    $cm = get_coursemodule_from_instance('programming', $programming->id, $programming->course);

    $params = array('itemname' => $programming->name,
        'idnumber' => $cm->id,
        'grademax' => $programming->grade,
        'grademin' => 0,
        'gradetype' => GRADE_TYPE_VALUE);
    $grades = new stdClass();
    $grades->itemid = $cm->id;
    $grades->userid = $submit->userid;
    $grades->rawgrade = $grade;

    return grade_update('mod/programming', $programming->course, 'mod', 'programming', $programming->id, 0, $grades, $params);
}

function programming_get_participants($programmingid) {
//Must return an array of user records (all data) who are participants
//for a given instance of programming. Must include every user involved
//in the instance, independient of his role (student, teacher, admin...)
//See other modules as example.

    global $CFG, $DB;
    $params = array($programmingid);
    $sql = "SELECT DISTINCT u.*
              FROM {programming_submits} AS ps,
                   {user} AS u,
              WHERE ps.programmingid = ?
               AND ps.userid = u.id";
    $users = $DB->get_records_sql($sql, $params);

    return $users;
}

function programming_scale_used($programmingid, $scaleid) {
//This function returns if a scale is being used by one programming
//it it has support for grading and scales. Commented code should be
//modified if necessary. See forum, glossary or journal modules
//as reference.

    $return = false;

    //$rec = get_record("programming","id","$programmingid","scale","-$scaleid");
    //
    //if (!empty($rec)  && !empty($scaleid)) {
    //    $return = true;
    //}

    return $return;
}

//////////////////////////////////////////////////////////////////////////////////////
/// Any other programming functions go here.  Each of them must have a name that 
/// starts with programming_

function programming_get_showmode_options() {
    $options = array();
    $options['1'] = get_string('normalmode', 'programming');
    $options['2'] = get_string('contestmode', 'programming');
    return $options;
}

function programming_get_yesno_options() {
    $yesnooptions = array();
    $yesnooptions[0] = get_string('no');
    $yesnooptions[1] = get_string('yes');
    return $yesnooptions;
}

function programming_get_timelimit_options($default = 0) {
    $timelimitoptions = array();
    $timelimitoptions[0] = get_string('timelimitunlimited', 'programming');
    for ($i = 1; $i <= 30; $i += 1) {
        $timelimitoptions[$i] = get_string('nseconds', 'programming', $i);
    }
    return $timelimitoptions;
}

function programming_get_difficulty_options($default = 0) {
    $difficultyoptions = array();
    for ($i = 0; $i <= 10; $i += 1) {
        $difficultyoptions[$i] = $i;
    }
    return $difficultyoptions;
}

function programming_testcase_pub_options() {
    $options = array();
    $options[PROGRAMMING_TEST_SHOWAFTERDISCOUNT] = get_string('afterdiscount', 'programming');
    $options[PROGRAMMING_TEST_HIDDEN] = get_string('never', 'programming');
    $options[PROGRAMMING_TEST_SHOWINRESULT] = get_string('inresult', 'programming');
    $options[PROGRAMMING_TEST_SHOW] = get_string('always', 'programming');
    return $options;
}

function programming_testcase_pub_getstring($pub) {
    $options = programming_testcase_pub_options();
    return $options[$pub];
}

function programming_testcase_require_view_capability($context, $case, $inresult = false, $afterdiscount = false) {
    if ($case->pub == PROGRAMMING_TEST_HIDDEN ||
            $case->pub == PROGRAMMING_TEST_SHOWINRESULT && $inresult ||
            $case->pub == PROGRAMMING_TEST_SHOWAFTERDISCOUNT && $afterdiscount) {
        require_capability('mod/programming:viewhiddentestcase', $context);
    } else {
        require_capability('mod/programming:viewpubtestcase', $context);
    }
}

function programming_testcase_visible($tests, $result, $inresult = false, $afterdiscount = false) {
    if (!is_object($result)) {
        return false;
    }

    $pub = $tests[$result->testid]->pub;
    return $pub == PROGRAMMING_TEST_SHOW ||
            $pub == PROGRAMMING_TEST_SHOWINRESULT && $inresult ||
            $pub == PROGRAMMING_TEST_SHOWAFTERDISCOUNT && $afterdiscount;
}

function programming_get_weight_options() {
    $weightoptions = array();
    //$weightoptions[0] = get_string('weightsetting', 'programming');
    for ($i = 0; $i <= 10; $i += 1) {
        $weightoptions[$i] = $i;
    }
    return $weightoptions;
}

function programming_get_memlimit_options() {
    $memlimitoptions = array();
    $memlimitoptions[0] = get_string('memlimitunlimited', 'programming');
    for ($i = 256; $i <= 512; $i += 32) {
        $memlimitoptions[$i] = $i . 'KB';
    }
    for ($i = 1; $i < 10; $i++) {
        $memlimitoptions[$i * 1024] = $i . 'MB';
    }
    for ($i = 10; $i <= 550; $i+= 10) {
        $memlimitoptions[$i * 1024] = $i . 'MB';
    }
    return $memlimitoptions;
}

function programming_get_nproc_options() {
    $options = array();
    for ($i = 0; $i <= 16; $i++) {
        $options[$i] = "$i";
    }
    return $options;
}

function programming_get_language_options($programming = False) {
    global $CFG, $DB;
    if ($programming) {
        $params = array($programming->id);
        $sql = "SELECT * FROM {$CFG->prefix}programming_languages
                 WHERE id in (
                SELECT languageid
                  FROM {programming_langlimit}
                 WHERE programmingid=?)";
        $languages = $DB->get_records_sql($sql, $params);
        if (!is_array($languages) || count($languages) == 0) {
            $languages = $DB->get_records('programming_languages');
        }
    } else {
        $languages = $DB->get_records('programming_languages');
    }
    $languageoptions = array();
    foreach ($languages as $id => $row) {
        // $languageoptions[$id] = $row->name;
        $languageoptions[$id] = $row->description;
    }
    return $languageoptions;
}

function programming_get_category_options($programming = False) {
    global $DB;
    if ($programming) {
        $params = array($programming->id);
        $sql = "SELECT * FROM {programming_category}
                 WHERE catid in (
                SELECT catid
                  FROM {programming_catproblemmap}
                 WHERE pid=?)";
        $cats = $DB->get_records_sql($sql, $params);
        if (!is_array($cats) || count($cats) == 0) {
            $cats = $DB->get_records('programming_category');
        }
    } else {
        $cats = $DB->get_records('programming_category');
    }
    $catoptions = array();
    foreach ($cats as $id => $row) {
        // $languageoptions[$id] = $row->name;
        $catoptions[$id] = $row->catname;
    }
    return $catoptions;
}

function programming_get_language_options_js($programming = False) {
    global $CFG, $DB;
    if ($programming) {
        $params = array($programming->id);
        $sql = "SELECT * FROM {$CFG->prefix}programming_languages
                 WHERE id in (
                SELECT languageid
                  FROM {programming_langlimit}
                 WHERE programmingid=?)";
        $languages = $DB->get_records_sql($sql, $params);
        if (!is_array($languages) || count($languages) == 0) {
            $languages = $DB->get_records('programming_languages');
        }
    } else {
        $languages = $DB->get_records('programming_languages');
    }
    $languageoptions = array();
    foreach ($languages as $id => $row) {
        // $languageoptions[$id] = $row->name;
        $languageoptions[$id] = $row->langmode;
    }
    return $languageoptions;
}

function programming_test_add_instance($testcase) {
    global $DB;
    if (strlen($testcase->input) > 1024) {
        $testcase->gzinput = bzcompress($testcase->input);
        $testcase->input = mb_substr($testcase->input, 0, 1024, 'UTF-8');
    }
    if (strlen($testcase->output) > 1024) {
        $testcase->gzoutput = bzcompress($testcase->output);
        $testcase->output = mb_substr($testcase->output, 0, 1024, 'UTF-8');
    }
    $testcase->timemodified = time();
    return $DB->insert_record("programming_tests", $testcase);
}

function programming_test_update_instance($testcase) {
    # If input is not provided, do not update input and gzinput
    global $DB;
    if (empty($testcase->input)) {
        unset($testcase->input);
        unset($testcase->gzinput);
    } else {
        $testcase->gzinput = bzcompress($testcase->input);
        $testcase->input = mb_substr($testcase->input, 0, 1024, 'UTF-8');
    }
    # If output is not provided, do not update output and gzoutput
    if (empty($testcase->output)) {
        unset($testcase->output);
        unset($testcase->gzoutput);
    } else {
        $testcase->gzoutput = bzcompress($testcase->output);
        $testcase->output = mb_substr($testcase->output, 0, 1024, 'UTF-8');
    }

    $testcase->timemodified = time();
    return $DB->update_record('programming_tests', $testcase);
}

function programming_submit_add_instance($programming, $submit) {
    global $CFG, $USER, $DB;

    $submit->timemodified = time();
    $submit->codelines = count(explode("\n", $submit->code));
    $submit->codesize = strlen($submit->code);
    $submit->status = 0;
    $result = $DB->insert_record('programming_submits', $submit);

    # update programming_result
    if ($result) {
        $r = $DB->get_record('programming_result', array('programmingid' => $programming->id, 'userid' => $USER->id));
        if ($r) {
            $r->submitcount++;
            $r->latestsubmitid = $result;
            $result2 = $DB->update_record('programming_result', $r);
        } else {
            $r = new stdClass;
            $r->programmingid = $programming->id;
            $r->userid = $submit->userid;
            $r->submitcount = 1;
            $r->latestsubmitid = $result;
            $result2 = $DB->insert_record('programming_result', $r);
        }
    }

    # update tester
    if ($result) {
        $DB->execute("INSERT INTO {programming_testers} (submitid, testerid) VALUES ($result, 0)");
    }

    # delete the old submits
    if ($result and $programming->keeplatestonly) {
        programming_delete_old_submits($programming->id, $submit->userid);
    }

    return $result;
}

function programming_delete_old_submits($programmingid = -1, $userid = -1) {
    global $CFG, $DB;

    if ($programmingid > 0) {
        if ($userid >= 0) {
            $params = array($programmingid, $userid);

            $sql = "SELECT id, programmingid, userid FROM
            {programming_submits} WHERE 
            programmingid = ? AND userid = ? ORDER BY timemodified DESC";
        } else {
            $params = array($programmingid);
            $sql = "SELECT id, programmingid, userid FROM 
            {programming_submits} 
            WHERE programmingid = ? ORDER BY userid, timemodified DESC";
        }
    } else {
        $params = null;
        $sql = "SELECT id, programmingid, userid FROM 
        {programming_submits} 
        ORDER BY programmingid, userid, timemodified DESC";
    }
    $submits = $DB->get_records_sql($sql, $params);
    if (is_array($submits)) {
        $ids = array();
        $pprogramming = -1;
        $puser = -1;
        foreach ($submits as $row) {
            if ($row->programmingid != $pprogramming) {
                $puser = -1;
            }
            if ($row->userid != $puser) {
                $c = 1;
            } else {
                $c++;
            }
            if ($c > PROGRAMMING_MAX_ATTEMPTS) {
                $ids[] = $row->id;
            }
            $puser = $row->userid;
            $pprogramming = $row->programmingid;
        }
        $ids = implode(',', $ids);

        if ($ids) {
            #delete from submits table
            $DB->execute("DELETE FROM {programming_submits} WHERE id IN ({$ids})");


            #delete from test results table
            $DB->execute("DELETE FROM {programming_test_results} WHERE submitid IN ({$ids})");
        }
    }

    return true;
}

function programming_get_submit_status_short($submit) {
    switch ($submit->status) {
        case PROGRAMMING_STATUS_NEW:
            return get_string('statusshortnew', 'programming');
        case PROGRAMMING_STATUS_COMPILING:
            return get_string('statusshortcompiling', 'programming');
        case PROGRAMMING_STATUS_COMPILEOK:
            return get_string('statusshortcompileok', 'programming');
        case PROGRAMMING_STATUS_RUNNING:
            return get_string('statusshortrunning', 'programming');
        case PROGRAMMING_STATUS_FINISH:
            return get_string('statusshortfinish', 'programming');
        case PROGRAMMING_STATUS_COMPILEFAIL:
            return get_string('statusshortcompilefail', 'programming');
    }
    return '';
}

function programming_get_submit_status_desc($submit) {
    switch ($submit->status) {
        case PROGRAMMING_STATUS_NEW:
            return get_string('statusnew', 'programming');
        case PROGRAMMING_STATUS_COMPILING:
            return get_string('statuscompiling', 'programming');
        case PROGRAMMING_STATUS_COMPILEOK:
            return get_string('statuscompileok', 'programming');
        case PROGRAMMING_STATUS_RUNNING:
            return get_string('statusrunning', 'programming');
        case PROGRAMMING_STATUS_FINISH:
            return get_string('statusfinish', 'programming');
        case PROGRAMMING_STATUS_COMPILEFAIL:
            return get_string('statuscompilefail', 'programming');
    }
    return '';
}

function programming_get_test_results_short($submit) {
    global $DB;

    $c = new stdClass();
    $c->total = $DB->count_records('programming_tests', array('programmingid' => $submit->programmingid));
    $c->success = $DB->count_records('programming_test_results', array('submitid' => $submit->id, 'passed' => 1));
    $c->fail = $c->total - $c->success;
    if ($c->total == $c->success) {
        return get_string('passalltests', 'programming', $c);
    } else {
        return get_string('successfailshort', 'programming', $c);
    }
}

function programming_get_test_results_desc($submit, $results) {
    $c = new stdClass();
    $c->success = 0;
    $c->fail = 0;
    if (is_array($results)) {
        foreach ($results as $result) {
            if ($result->passed == 0)
                $c->fail++;
            else
                $c->success++;
        }
    }
    $c->total = $c->success + $c->fail;
    return get_string('successfailcount', 'programming', $c);
}

function programming_format_codesize($size) {
    if ($size < 1000)
        return $size . 'B';
    else
        return round($size / 1000, 2) . 'K';
}

function check_crarr() {
    global $_SERVER;

    $uagent = $_SERVER['HTTP_USER_AGENT'];
    if (!strpos($uagent, 'MSIE')) {
        return '&crarr;';
    } else {
        return '<span style="font-family: Wingdings 3">&crarr;</span>';
    }
}

function programming_format_io($message, $autolastreturn = false) {
    $crarr = check_crarr();
    $sizelimit = 1024;

    $haslastreturn = false;
    if (substr($message, strlen($message) - 1, 1) == "\n") {
        $haslastreturn = true;
        $message = substr($message, 0, strlen($message) - 1);
    }

    $message = str_replace(
            array(' ', "\r", "\n"), array('&nbsp;', '', $crarr . '</span></li><li><span>'), htmlspecialchars(mb_substr($message, 0, $sizelimit, 'UTF-8')));

    if ($haslastreturn || $autolastreturn)
        $message .= $crarr;

    return '<div><ol><li><span>' . $message . '</span></li></ol></div>';
}

function programming_format_compile_message($message) {
    $lines = explode("\n", trim($message));
    $html = '<ol>';
    $t = '';
    foreach ($lines as $line) {
        if (strstr($line, 'warning:'))
            $t = 'warning';
        else if (strstr($line, 'error:'))
            $t = 'error';
        else
            $t = 'normal';

        $html .= "<li class='$t'>";
        $html .= str_replace(' ', '&nbsp;', htmlspecialchars($line));
        $html .= '</li>';
    }
    $html .= '</ol>';
    return $html;
}

function programming_parse_compile_message($message) {
    $r = new stdClass();
    $r->warnings = count(preg_split("/\d: warning: /", $message)) - 1;
    $r->errors = count(preg_split("/\d: error: /", $message)) - 1;
    return $r;
}

function programming_delete_submit($submit) {
    global $CFG, $DB;

    $DB->delete_records('programming_test_results', array('submitid' => $submit->id));
    $DB->delete_records('programming_submits', array('id' => $submit->id));

    # update programming_result table
    $params = array($submit->programmingid, $submit->userid);
    $submits = $DB->get_records_sql("
        SELECT * FROM {programming_submits}
         WHERE programmingid = ?
           AND userid = ?
      ORDER BY timemodified DESC", $params);
    $r = $DB->get_record('programming_result', array('programmingid' => $submit->programmingid, 'userid' => $submit->userid));
    if (is_array($submits) && count($submits)) {
        $s = array_shift($submits);
        $r->latestsubmitid = $s->id;
    } else {
        $r->latestsubmitid = 0;
    }
    $DB->update_record('programming_result', $r);
}

function programming_rejudge($programming, $submitid, $groupid, $ac) {
    global $CFG, $DB;

    if (empty($submitid)) {
        $params = array($programming->id, $programming->id);
        $sql = "SELECT latestsubmitid
                  FROM {programming_result} AS pr,
                       {programming_submits} AS ps
                 WHERE pr.latestsubmitid = ps.id
                   AND pr.programmingid = ?
                   AND ps.programmingid = ?";
        if (!$ac) {
            $sql .= " AND (ps.passed = 0 OR ps.passed IS NULL)";
        }

        $lsids = $DB->get_records_sql($sql, $params);
        foreach ($lsids as $submit) {
            $ids[] = $submit->latestsubmitid;
        }
    } else {
        $ids = $submitid;
    }
    $ids = implode(',', $ids);

    $sql = "submitid IN ($ids)";
    $DB->delete_records_select('programming_test_results', $sql);

    $sql = "UPDATE {programming_submits}
               SET judgeresult=null, timeused=null, memused=null,
                   passed=null, status=0, compilemessage=null
             WHERE id IN ($ids)";
    $DB->execute($sql);

    $sql = "INSERT INTO {programming_testers}
            SELECT id, 0, 3, 0
              FROM {programming_submits}
             WHERE id IN ($ids)
               AND id NOT IN (SELECT submitid FROM {programming_testers})";
    $DB->execute($sql);
}

function newme($params = NULL) {
    $uri = me();
    $append = '';
    $search = array();
    $replace = array();
    if (is_array($params)) {
        foreach ($params as $name => $value) {
            $s = '/(.*)&' . $name . '=[\d\w+%]*(.*)/';
            if (preg_match($s, $uri)) {
                $search[] = $s;
                $replace[] = '\1&' . $name . '=' . htmlspecialchars($value) . '\2';
            } else {
                $append .= '&' . $name . '=' . htmlspecialchars($value);
            }
        }
    }
    return preg_replace($search, $replace, $uri) . $append;
}

function programming_check_privilege($courseid, $groupid) {
    global $USER;
    if (!isteacher($courseid))
        return False;
    if (isteacheredit($courseid) || groupmode($courseid, $cm) == NOGROUPS)
        return True;
    if ($usergrps = groups_get_all_groups($courseid, $USER->id)) {
        foreach ($usergrps as $ug) {
            if ($groupid === $ug->id)
                return True;
        }
    }

    return False;
}

function programming_submit_timeused(&$results) {
    $time = 0;
    foreach ($results as $r) {
        $time = max($time, $r->timeused);
    }
    return $time;
}

function programming_submit_memused(&$results) {
    $mem = 0;
    foreach ($results as $r) {
        $mem = max($mem, $r->memused);
    }
    return $mem;
}

function programming_judgeresult_options($addempty = false) {
    $rst = array('AC', 'PE', 'WA', 'RE', 'FPE', 'KS', 'OLE', 'MLE', 'TLE', 'RFC', 'JGE', 'JSE');
    $ret = array();
    if ($addempty)
        $ret[''] = get_string('all');
    foreach ($rst as $k) {
        $ret[$k] = get_string($k, 'programming');
    }
    return $ret;
}

function programming_submit_judgeresult(&$results) {
    $err = array('JSE' => 20, 'JGE' => 19, 'RFC' => 18,
        'TLE' => 10, 'MLE' => 9, 'OLE' => 8,
        'KS' => 14, 'FPE' => 13, 'RE' => 12, 'WA' => 11,
        'PE' => 1, 'AC' => 0);

    $c = -1;
    $errstr = null;
    foreach ($results as $result) {
        if (isset($err[$result->judgeresult]) && $err[$result->judgeresult] > $c) {
            $c = $err[$result->judgeresult];
            $errstr = $result->judgeresult;
        }
    }
    return $errstr;
}

function programming_contest_get_judgeresult(&$results) {
    $errstr = programming_submit_judgeresult($results);
    return get_string($errstr, 'programming');
}

function programming_get_judgeresult($result) {
    if ($result->judgeresult) {
        return '<a title="' . get_string($result->judgeresult . ':description', 'programming') . '">' . get_string($result->judgeresult, 'programming') . '</a>';
    } else {
        return programming_get_fail_reason($result);
    }
}

function programming_get_fail_reason($result, $showmode = PROGRAMMING_SHOWMODE_NORMAL) {
    if ($result->passed)
        return get_string('n/a', 'programming');

    switch ($result->signal) {
        case 0:
            break;
        case 8:
            if ($showmode == PROGRAMMING_SHOWMODE_NORMAL)
                return get_string('failbecausefpe', 'programming');
            else
                return get_string('runtimeerror', 'programming');
        case 15:
            if ($showmode == PROGRAMMING_SHOWMODE_NORMAL)
                return get_string('failbecausetimelimit', 'programming');
            else
                return get_string('timelimitexceed', 'programming');
        case 11:
            if ($showmode == PROGRAMMING_SHOWMODE_NORMAL)
                return get_string('failbecausesegv', 'programming');
            else
                return get_string('runtimeerror', 'programming');
        case 9:
        case 24:
            if ($showmode == PROGRAMMING_SHOWMODE_NORMAL)
                return get_string('failbecausecputimelimit', 'programming');
            else
                return get_string('timelimitexceed', 'programming');
        default:
            if ($showmode == PROGRAMMING_SHOWMODE_NORMAL)
                return get_string('failbecauseunknownsig', 'programming', $result->signal);
            else
                return get_string('runtimeerror', 'programming');
    }

    if ($showmode == PROGRAMMING_SHOWMODE_NORMAL) {
        switch ($result->exitcode) {
            case 125:
                return get_string('failbecausejudgescript', 'programming');
            case 126:
                return get_string('failbecauserestrict', 'programming');
            case 127:
                return get_string('failbecausesimpleguard', 'programming');
            default:
                return get_string('failbecausewrongresult', 'programming');
        }
    } else {
        switch ($result->exitcode) {
            case 125:
            case 126:
            case 127:
                return get_string('runtimeerror', 'programming');
            default:
                return get_string('wronganswer', 'programming');
        }
    }
}

function programming_format_timelimit($timelimit) {
    if ($timelimit) {
        return get_string('nseconds', 'programming', $timelimit);
    } else {
        return get_string('timelimitunlimited', 'programming');
    }
}

function programming_format_memlimit($memlimit) {
    if ($memlimit) {
        return get_string('nkb', 'programming', $memlimit);
    } else {
        return get_string('memlimitunlimited', 'programming');
    }
}

function programming_judge_status($courseid, &$totalcount, $offset = 0, $limit = 15) {
    global $DB;

    if (!isset($courseid))
        return False;

    $crit = $courseid != 1 ? "p.course = $courseid AND" : '';
    $sql = "SELECT ps.id AS psid,
                   ps.userid AS userid,
                   p.globalid,
                   p.name AS pname,
                   ps.timemodified as timemodified,
                   p.id AS pid,
                   ps.status as status
              FROM {programming} AS p,
                   {programming_submits} AS ps
             WHERE $crit p.id = ps.programmingid
          ORDER BY ps.timemodified DESC";
    $rows = $DB->get_records_sql($sql, null, $offset, $limit);

    if ($courseid == 1) {
        $sql = "SELECT COUNT(*) AS total
                  FROM {programming_submits} AS ps";
    } else {
        $sql = "SELECT COUNT(*) AS total
                  FROM {programming} AS p,
                       {programming_submits} AS ps
                 WHERE p.course = $courseid
                   AND ps.programmingid = p.id";
    }
    $totalcount = $DB->count_records_sql($sql);

    if ($rows) {
        foreach ($rows as $row) {
            $row->user = $DB->get_record('user', array('id' => $row->userid));
            switch ($row->status) {
                case PROGRAMMING_STATUS_COMPILEFAIL:
                    $row->judgeresult = get_string('CE', 'programming');
                    break;
                case PROGRAMMING_STATUS_FINISH:
                    $tr = $DB->get_records('programming_test_results', array('submitid' => $row->psid));
                    $row->judgeresult = '';
                    if ($tr) {
                        $row->judgeresult = programming_contest_get_judgeresult($tr);
                        $row->timeused = 0;
                        foreach ($tr as $r) {
                            $row->timeused = max($row->timeused, $r->timeused);
                        }
                        $row->memused = 'Unknown';
                    }
                    break;
                default:
                    $row->judgeresult = get_string('statusshortnew', 'programming');
            }
        }
    }

    return $rows;
}

function programming_latest_ac($courseid, $roleid, &$totalcount, $offset = 0, $limit = 15) {
    global $DB;

    if (!isset($courseid))
        return False;
    $context = context_module::instance($courseid);

    if ($courseid == 1) {
        $sql = "SELECT ps.id AS psid,
                       ps.userid AS userid,
                       p.name AS pname,
                       p.globalid,
                       ps.timemodified as timemodified,
                       p.id AS pid
                  FROM {programming} AS p,
                       {programming_result} AS pr,
                       {programming_submits} AS ps
                 WHERE pr.programmingid = p.id
                   AND pr.latestsubmitid = ps.id
                   AND ps.passed = 1
              ORDER BY ps.timemodified DESC";
        $rows = $DB->get_records_sql($sql, null, $offset, $limit);

        $sql = "SELECT COUNT(*) AS total
                  FROM {programming} AS p,
                       {programming_result} AS pr,
                       {programming_submits} AS ps
                 WHERE pr.programmingid = p.id
                   AND pr.latestsubmitid = ps.id
                   AND ps.passed = 1";
        $totalcount = $DB->count_records_sql($sql);
    } else {
        $params = array($courseid, $roleid, $context->id);

        $sql = "SELECT ps.id AS psid,
                       ps.userid AS userid,
                       p.name AS pname,
                       p.globalid,
                       ps.timemodified as timemodified,
                       p.id AS pid
                  FROM {programming} AS p,
                       {programming_result} AS pr,
                       {programming_submits} AS ps,
                       {role_assignments} AS ra
                 WHERE p.course = ?
                   AND ra.roleid = ?
                   AND ra.contextid = ?
                   AND pr.programmingid = p.id
                   AND pr.latestsubmitid = ps.id
                   AND ps.passed = 1
                   AND pr.userid = ra.userid
              ORDER BY ps.timemodified DESC";
        $rows = $DB->get_records_sql($sql, $params, $offset, $limit);
        $params = array($courseid, $roleid, $context->id);

        $sql = "SELECT COUNT(*) AS total
                  FROM {programming} AS p,
                       {programming_result} AS pr,
                       {programming_submits} AS ps,
                       {role_assignments} AS ra
                 WHERE p.course = ?
                   AND ra.roleid = ?
                   AND ra.contextid = ?
                   AND pr.programmingid = p.id
                   AND pr.latestsubmitid = ps.id
                   AND ps.passed = 1
                   AND pr.userid = ra.userid";
        $totalcount = $DB->count_records_sql($sql, $params);
    }

    if ($rows) {
        foreach ($rows as $row) {
            $row->user = $DB->get_record('user', array('id' => $row->userid));
        }
    }

    return $rows;
}

/**
 * Calculate the standing of the contest.
 *
 *  1. Each run is either accepted or rejected.
 *  2. The problem is considered solved by the team, if one of the runs
 *     submitted for it is accepted.
 *  3. The time consumed for a solved problem is the time elapsed from
 *     the beginning of the contest to the submission of the first accepted
 *     run for this problem (in minutes) plus 20 minutes for every other run
 *     for this problem before the accepted one. For an unsolved problem
 *     consumed time is not computed.
 *  4. The total time is the sum of the time consumed for each problem solved.
 *  5. Teams are ranked according to the number of solved problems. Teams that
 *     solve the same number of problems are ranked by the least total time.
 *  6. While the time shown is in minutes, the actual time is measured to the
 *     precision of 1 second, and the the seconds are taken into account when
 *     ranking teams.
 *  7. Teams with equal rank according to the above rules must be sorted by
 *     increasing team number. 
 *
 * @param courseid In which course the contest is hold.
 * @param wrongsubmitminutes How many minutes for wrong submit.
 * @param roleid Which role is included
 * @return An array contains all the information of teams.
 */
function programming_calc_standing($courseid, $roleid, $wrongsubmitminutes = 20, $offset = 0, $limit = 10) {
    global $DB;

    if (!isset($courseid))
        return array();
    if ($courseid == 1) {
        $query = "SELECT pr.userid,
                         COUNT(*) AS ac
                    FROM {programming_result} AS pr,
                         {programming_submits} AS ps
                   WHERE pr.latestsubmitid = ps.id
                     AND ps.passed = 1
                GROUP BY pr.userid
                ORDER BY ac DESC";
    } else {
        $context = context_course::instance($courseid);
        $wss = $wrongsubmitminutes * 60;

        $query = "SELECT pr.userid,
                   SUM(ps.passed) AS ac,
                   SUM((ps.timemodified-p.timeopen)*ps.passed) AS timeused,
                   SUM(pr.submitcount * ps.passed) AS submitcount,
                   SUM(((ps.timemodified - p.timeopen) +
                       (pr.submitcount - ps.passed)*{$wss}) * ps.passed) AS penalty
              FROM {programming} AS p,
                   {programming_result} AS pr,
                   {programming_submits} AS ps,
                   {role_assignments} AS ra
             WHERE p.course={$courseid}
               AND ra.roleid = {$roleid}
               AND ra.contextid = {$context->id}
               AND ps.programmingid = p.id
               AND pr.programmingid = p.id
               AND pr.userid = ra.userid
               AND pr.latestsubmitid = ps.id
          GROUP BY pr.userid
            HAVING ac > 0
          ORDER BY ac DESC, penalty ASC";
    }

    //echo $query;

    $standing = $DB->get_records_sql($query, null, $offset, $limit);
    if (!is_array($standing))
        $standing = array();
    foreach ($standing as $row) {
        $row->user = $DB->get_record('user', array('id' => $row->userid));
    }

    return $standing;
}

function programming_count_standing($courseid, $roleid) {
    global $DB;

    if (!isset($courseid))
        return 0;
    if ($courseid == 1) {
        $query = "SELECT COUNT(*) AS COUNT FROM (
                  SELECT DISTINCT pr.userid
                    FROM {programming_result} AS pr,
                         {programming_submits} AS ps
                   WHERE pr.latestsubmitid = ps.id
                     AND ps.passed = 1) AS c";
    } else {
        $context = context_course::instance($courseid);

        $query = "SELECT COUNT(*) AS count FROM (
                SELECT DISTINCT pr.userid
                  FROM {programming} AS p,
                       {programming_result} AS pr,
                       {programming_submits} AS ps,
                       {role_assignments} AS ra
                 WHERE p.course={$courseid}
                   AND ra.roleid = {$roleid}
                   AND ra.contextid = {$context->id}
                   AND ps.programmingid = p.id
                   AND pr.programmingid = p.id
                   AND pr.userid = ra.userid
                   AND pr.latestsubmitid = ps.id
                   AND ps.passed = 1) AS c";
    }
    //echo $query;

    return $DB->count_records_sql($query);
}

function programming_submit_remove_preset($code) {
    $ret = array();
    $s = 0;

    $lines = explode("\n", $code);
    foreach ($lines as $line) {
        $line = trim($line, "\r");
        switch ($s) {
            case 0:
                if ($line == PROGRAMMING_PRESET_BEGIN) {
                    $s = 1;
                }
                if ($s == 0) {
                    $ret[] = $line;
                }
                break;
            case 1:
                if ($line == PROGRAMMING_PRESET_END) {
                    $s = 0;
                }
                break;
        }
    }

    return implode("\n", $ret);
}

/**
 * Put prepend and postpend preset code and user submitted code together.
 */
function programming_format_code($programming, $submit, $check = false) {
    global $DB;
    if (is_object($programming)) {
        $programming = $programming->id;
    }
    $prepend = $DB->get_record('programming_presetcode', array('programmingid' => $programming, 'languageid' => $submit->language, 'name' => '<prepend>'));
    $postpend = $DB->get_record('programming_presetcode', array('programmingid' => $programming, 'languageid' => $submit->language, 'name' => '<postpend>'));

    $ret = array();
    if (!empty($prepend)) {
        $ret[] = PROGRAMMING_PRESET_BEGIN;
        $ret[] = trim(!$check || empty($prepend->presetcodeforcheck) ? $prepend->presetcode : $prepend->presetcodeforcheck);
        $ret[] = PROGRAMMING_PRESET_END;
    }

    if (is_object($submit)) {
        $ret[] = $submit->code;
    }

    if (!empty($postpend)) {
        $ret[] = PROGRAMMING_PRESET_BEGIN;
        $ret[] = trim(!$check || empty($postpend->presetcodeforcheck) ? $postpend->presetcode : $postpend->presetcodeforcheck);
        $ret[] = PROGRAMMING_PRESET_END;
    }
    return implode("\n\n", $ret);
}

function programming_get_presetcode_name($presetcode) {
    switch ($presetcode->name) {
        case '<prepend>':
            return get_string('prependcode', 'programming');
        case '<postpend>':
            return get_string('postpendcode', 'programming');
        default:
            return $presetcode->name;
    }
}

function programming_format_presetcode($presetcode) {
    $ret = array(PROGRAMMING_PRESET_BEGIN, trim($presetcode->presetcode),
        PROGRAMMING_PRESET_END);
    return implode("\n\n", $ret);
}

function get_visible_programmings($courseid) {
    $DB;
    $sql = "SELECT p.*, cm.visible
              FROM {course_modules} cm,
                   {programming} p
             WHERE cm.course=$courseid
               AND p.course=$courseid
               AND cm.instance = p.id
          ORDER BY p.name";
    return $DB->get_records_sql($sql);
}

function programming_adjust_sequence(&$records, $moveid, $direction) {
    $seq = array();
    $i = 1;
    $idx = 0;
    foreach ($records as $rec) {
        if ($moveid == $rec->id)
            $idx = $i;
        $seq[$i++] = $rec;
    }

    if ($idx) {
        if ($direction == 1 && $idx > 1) { // move up
            $t = $seq[$idx];
            $seq[$idx] = $seq[$idx - 1];
            $seq[$idx - 1] = $t;
        } else if ($direction == 2 && $idx < count($records)) {
            $t = $seq[$idx];
            $seq[$idx] = $seq[$idx + 1];
            $seq[$idx + 1] = $t;
        }
    }
    return $seq;
}

function programming_presetcode_adjust_sequence($programmingid, $moveid = 0, $direction = 0) {
    global $DB;
    $codes = $DB->get_records('programming_presetcode', array('programmingid' => $programmingid, 'sequence' => 'id, sequence'));
    if (!is_array($codes))
        return;

    $seq = programming_adjust_sequence($codes, $moveid, $direction);

    foreach ($seq as $i => $code) {
        if ($code->sequence != $i) {
            $code->sequence = $i;
            $DB->update_record('programming_presetcode', $code);
        }
    }
}

function programming_datafile_adjust_sequence($programmingid, $moveid = 0, $direction = 0) {
    global $DB;
    $codes = $DB->get_records('programming_datafile', array('programmingid' => $programmingid, 'seq' => 'id, seq'));
    if (!is_array($codes))
        return;

    $seq = programming_adjust_sequence($codes, $moveid, $direction);

    foreach ($seq as $i => $code) {
        if ($code->seq != $i) {
            $code->seq = $i;
            $DB->update_record('programming_datafile', $code);
        }
    }
}

function programming_testcase_adjust_sequence($programmingid, $moveid = 0, $direction = 0) {
    global $DB;
    $codes = $DB->get_records('programming_tests', array('programmingid' => $programmingid, 'seq' => 'id, seq'));
    if (!is_array($codes))
        return;

    $seq = programming_adjust_sequence($codes, $moveid, $direction);

    foreach ($seq as $i => $code) {
        if ($code->seq != $i) {
            $code->seq = $i;
            $DB->update_record('programming_tests', $code);
        }
    }
}

/**
 * @param string $feature FEATURE_xx constant for requested feature
 * @return mixed True if module supports feature, null if doesn't know
 */
function programming_supports($feature) {
    switch ($feature) {
        case FEATURE_GROUPS: return true;
        case FEATURE_GROUPINGS: return true;
        case FEATURE_GROUPMEMBERSONLY: return true;
        case FEATURE_MOD_INTRO: return true;
        case FEATURE_COMPLETION_TRACKS_VIEWS: return true;
        case FEATURE_GRADE_HAS_GRADE: return true;
        case FEATURE_GRADE_OUTCOMES: return true;
        case FEATURE_BACKUP_MOODLE2: return true;
        case FEATURE_SHOW_DESCRIPTION: return true;
        case FEATURE_ADVANCED_GRADING: return false;

        default: return null;
    }
}

/**
 * This fucntion extends the global navigation for the site.
 * It is important to note that you should not rely on PAGE objects within this
 * body of code as there is no guarantee that during an AJAX request they are
 * available
 *
 * @param navigation_node $navigation The programming node within the global navigation
 * @param object $course The course object returned from the DB
 * @param object $module The module object returned from the DB
 * @param object $cm The course module instance returned from the DB
 */
function programming_extend_navigation($navigation, $course, $module, $cm) {
    global $CFG, $USER, $PAGE, $OUTPUT;

    $currentgroup = groups_get_activity_group($cm, true);

    $target = $CFG->wwwroot . '/mod/programming/';
    $param = array('id' => $cm->id);

    $navigation->add(get_string('view', 'programming'), new moodle_url($target . 'view.php', $param), navigation_node::TYPE_USER, null, null, new pix_icon('c/group', ''));
    $navigation->add(get_string('submit', 'programming'), new moodle_url($target . 'submit.php', $param), navigation_node::TYPE_USER, null, null, new pix_icon('c/group', ''));
    $navigation->add(get_string('result', 'programming'), new moodle_url($target . 'result.php', $param), navigation_node::TYPE_USER, null, null, new pix_icon('c/group', ''));
    $navigation->add(get_string('submithistory', 'programming'), new moodle_url($target . 'history.php', $param), navigation_node::TYPE_USER, null, null, new pix_icon('c/group', ''));

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

    if (has_capability('mod/programming:viewreport', $context)) {
        $reportnode = $navigation->add(get_string('reports', 'programming'), new moodle_url($target . 'reports/summary.php', $param), navigation_node::TYPE_CONTAINER, null, null, new pix_icon('c/group', ''));
        $reportnode->add(get_string('summary', 'programming'), new moodle_url($target . 'reports/summary.php', $param), navigation_node::TYPE_USER, null, null, new pix_icon('c/group', ''));
        $reportnode->add(get_string('detail', 'programming'), new moodle_url($target . 'reports/detail.php', $param), navigation_node::TYPE_USER, null, null, new pix_icon('c/group', ''));
        $reportnode->add(get_string('bestprograms', 'programming'), new moodle_url($target . 'reports/best.php', $param), navigation_node::TYPE_USER, null, null, new pix_icon('c/group', ''));
        $reportnode->add(get_string('judgeresultcountchart', 'programming'), new moodle_url($target . 'reports/judgeresultchart.php', $param), navigation_node::TYPE_USER, null, null, new pix_icon('c/group', ''));
    }

    if (has_capability('mod/programming:viewresemble', $context)) {
        $navigation->add(get_string('resemble', 'programming'), new moodle_url($target . 'resemble/view.php', $param), navigation_node::TYPE_USER, null, null, new pix_icon('c/group', ''));
    }
}

function programming_extend_settings_navigation(settings_navigation $settingsnav, navigation_node $navigation) {
    global $USER, $PAGE, $CFG, $DB, $OUTPUT;

    $target = $CFG->wwwroot . '/mod/programming/';
    $param = array('id' => $PAGE->cm->id);

    $navigation->add(get_string('view', 'programming'), new moodle_url($target . 'view.php', $param), navigation_node::TYPE_USER, null, null, new pix_icon('c/group', ''));
}

function programming_navtab($currenttab, $currenttab2, $course, $module, $cm) {
    global $CFG;
    $context = context_module::instance($cm->id);
    $tabs = array();
    $inactive = NULL;

    $row = array();

    $row[] = new tabobject('view', $CFG->wwwroot . '/mod/programming/view.php?id=' . $cm->id, get_string('view', 'programming'), '', true);

    $row[] = new tabobject('submit', $CFG->wwwroot . '/mod/programming/submit.php?id=' . $cm->id, get_string('submit', 'programming'), '', true);

    $row[] = new tabobject('result', $CFG->wwwroot . '/mod/programming/result.php?id=' . $cm->id, get_string('result', 'programming'), '', true);

    $row[] = new tabobject('history', $CFG->wwwroot . '/mod/programming/history.php?id=' . $cm->id, get_string('submithistory', 'programming'), '', true);

    if (has_capability('mod/programming:edittestcase', $context)) {
        $row[] = new tabobject('edittest', $CFG->wwwroot . '/mod/programming/testcase/list.php?id=' . $cm->id, get_string('testenv', 'programming'));
    }
    if (has_capability('mod/programming:viewreport', $context)) {
        $row[] = new tabobject('reports', $CFG->wwwroot . '/mod/programming/reports/summary.php?id=' . $cm->id, get_string('reports', 'programming'), '', true);
    }
    if (has_capability('mod/programming:viewresemble', $context)) {
        $row[] = new tabobject('resemble', $CFG->wwwroot . '/mod/programming/resemble/view.php?id=' . $cm->id, get_string('resemble', 'programming'), '', true);
    }

    $tabs[] = $row;

    if ($currenttab == 'edittest' && has_capability('mod/programming:edittestcase', $context)) {
        $row = array();
        $inactive[] = 'edittest';
        $row[] = new tabobject('testcase', $CFG->wwwroot . '/mod/programming/testcase/list.php?id=' . $cm->id, get_string('testcase', 'programming'));
        $row[] = new tabobject('datafile', $CFG->wwwroot . '/mod/programming/datafile/list.php?id=' . $cm->id, get_string('datafile', 'programming'));
        $row[] = new tabobject('presetcode', $CFG->wwwroot . '/mod/programming/presetcode/list.php?id=' . $cm->id, get_string('presetcode', 'programming'));
        $row[] = new tabobject('validator', $CFG->wwwroot . '/mod/programming/validator/edit.php?id=' . $cm->id, get_string('validator', 'programming'));

        $tabs[] = $row;
    }

    if ($currenttab == 'reports') {
        $row = array();
        $inactive[] = 'reports';
        $row[] = new tabobject('reports-summary', $CFG->wwwroot . '/mod/programming/reports/summary.php?id=' . $cm->id, get_string('summary', 'programming'));
        $row[] = new tabobject('reports-detail', $CFG->wwwroot . '/mod/programming/reports/detail.php?id=' . $cm->id . '&amp;latestonly=1', get_string('detail', 'programming'));
        $row[] = new tabobject('reports-best', $CFG->wwwroot . '/mod/programming/reports/best.php?id=' . $cm->id, get_string('bestprograms', 'programming'));
        $row[] = new tabobject('reports-judgeresultchart', $CFG->wwwroot . '/mod/programming/reports/judgeresultchart.php?id=' . $cm->id, get_string('judgeresultcountchart', 'programming'));
        $tabs[] = $row;
    }

    if ($currenttab == 'resemble') {
        $row = array();
        $inactive[] = 'resemble';
        $row[] = new tabobject('resemble-view', $CFG->wwwroot . '/mod/programming/resemble/view.php?id=' . $cm->id, get_string('personal', 'programming'));
        if (has_capability('mod/programming:editresemble', $context)) {
            $row[] = new tabobject('resemble-edit', $CFG->wwwroot . '/mod/programming/resemble/edit.php?id=' . $cm->id, get_string('edit'));
        }
        if (has_capability('mod/programming:updateresemble', $context)) {
            $row[] = new tabobject('resemble-analyze', $CFG->wwwroot . '/mod/programming/resemble/analyze.php?id=' . $cm->id, get_string('analyze', 'programming'));
        }
        $tabs[] = $row;
    }

    /*     * ***************************
     * stolen code from quiz report
     * *************************** */
    if ($currenttab == 'templates' and isset($mode)) {
        $inactive[] = 'templates';
        $templatelist = array('listtemplate', 'singletemplate', 'addtemplate', 'rsstemplate', 'csstemplate');

        $row = array();
        $currenttab = '';
        foreach ($templatelist as $template) {
            $row[] = new tabobject($template, "templates.php?d=$data->id&amp;mode=$template", get_string($template, 'data'));
            if ($template == $mode) {
                $currenttab = $template;
            }
        }
        $tabs[] = $row;
    }

    $tab = new StdClass;
    $tab->tabs = $tabs;
    $tab->currenttab = $currenttab;
    $tab->active = empty($currenttab2) ? null : array($currenttab2);
    $tab->inactive = $inactive;

    return $tab;
}

?>
