<?php
// This file is part of VPL for Moodle - http://vpl.dis.ulpgc.es/
//
// VPL for 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.
//
// VPL for 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 VPL for Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
/**
 * Submission class definition
 *
 * @package mod_vpl
 * @copyright 2012 onwards Juan Carlos Rodríguez-del-Pino
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 * @author Juan Carlos Rodríguez-del-Pino <jcrodriguez@dis.ulpgc.es>
 */
 
/**
 * Module instance files
 * path= vpl_data//vpl_instance#
 * Submission info
 * path/usersdata/userid#/submissionid#/submittedfiles.lst
 * path/usersdata/userid#/submissionid#/submittedfiles/
 * path/usersdata/userid#/submissionid#/grade_comments.txt
 * path/usersdata/userid#/submissionid#/teachertest.txt
 * path/usersdata/userid#/submissionid#/studenttest.txt
 */
defined('MOODLE_INTERNAL') || die();
require_once(dirname(__FILE__).'/vpl.class.php');
require_once(dirname(__FILE__).'/views/sh_factory.class.php');
require_once(dirname(__FILE__).'/views/show_hide_div.class.php');
require_once($CFG->dirroot . '/grade/grading/lib.php');
 
// Non static due to usort error.
function vpl_compare_filenamebylengh($f1, $f2) {
    return strlen( $f2 ) - strlen( $f1 );
}
class mod_vpl_submission {
    protected $vpl;
    protected $instance;
 
    /**
     * Internal var object to submitted file group manager
     *
     * @var object of file group manager
     */
    protected $submittedfgm;
 
    /**
     * Constructor
     *
     * @param $vpl object
     *            vpl instance
     * @param $mix submission
     *            instance object or id
     */
    public function __construct(mod_vpl $vpl, $mix = false) {
        global $DB;
        $this->vpl = $vpl;
        if (is_object( $mix )) {
            $this->instance = $mix;
        } else if ($mix === false) {
            throw new Exception( 'vpl_submission id error' );
        } else {
            $this->instance = $DB->get_record( 'vpl_submissions', array (
                    'id' => $mix
            ) );
            if (! $this->instance) {
                throw new Exception( 'vpl_submission id error' );
            }
        }
        $this->submittedfgm = null;
    }
    
      /**
     * get submission instance
     *
     * @return object submission instance
     */
    public function get_instance() {
        return $this->instance;
    }
 
    /**
     * get vpl object related
     *
     * @return object vpl
     */
    public function get_vpl() {
        return $this->vpl;
    }
    /**
     * Return the proper userid
     *
     * @return user id
     */
    public function get_userid() {
        if ($this->vpl->is_group_activity()) {
            $users = $this->vpl->get_group_members($this->instance->groupid);
            if ( count($users) == 0 ) {
                return $this->instance->userid;
            }
            $user = reset( $users );
            return $user->id;
        }
        return $this->instance->userid;
    }
    /**
     * get path to data submission directory
     *
     * @return string submission data directory
     */
    public function get_data_directory() {
        return $this->vpl->get_users_data_directory() . '/' . $this->instance->userid . '/' . $this->instance->id . '/';
    } 
     
    /**
     * get path to files submission directory
     *
     * @return string files submission directory
     */
    public function get_submission_directory() {
        return $this->get_data_directory() . 'submittedfiles/';
    }
 
    /**
     * get absolute path to name of file with list of submitted files
     *
     * @return string file name
     */
    public function get_submissionfilelistname() {
        return $this->get_data_directory() . 'submittedfiles.lst';
    }
 
    /**
     *
     * @return object file group manager for submitted files
     */
    public function get_submitted_fgm() {
        if (! $this->submittedfgm) {
            $this->submittedfgm = new file_group_process( $this->get_submissionfilelistname(), $this->get_submission_directory() );
        }
        return $this->submittedfgm;
    }
 
    /**
     * get absolute path to the grade comments file name
     *
     * @return string file name
     */
    public function get_gradecommentsfilename() {
        return $this->get_data_directory() . 'grade_comments.txt';
    }
     
    /**
     * get submitted files
     *
     * @return array of array 'name' and 'data'
     */
    public function get_submitted_files() {
        $fg = $this->get_submitted_fgm();
        return $fg->getallfiles();
    }
    public function set_submitted_file($files, $othersub = false) {
        $fg = $this->get_submitted_fgm();
        if ($othersub != false) {
            $otherdir = $othersub->get_submission_directory();
            $otherfln = $othersub->get_submissionfilelistname();
            $fg->addallfiles( $files, $otherdir, $otherfln);
        } else {
            $fg->addallfiles($files);
        }
    }
    public function is_equal_to(&$files, $comment = '') {
        if ($this->instance->comments != $comment) {
            return false;
        }
        $subfiles = $this->get_submitted_files();
        if (count( $files ) != count( $subfiles )) {
            return false;
        }
        $oldnames = array_keys( $subfiles );
        $newnames = array_keys( $files );
        foreach ($oldnames as $pos => $name) {
            if ( $name != $newnames [$pos] ) {
                return false;
            }
        }
        foreach ($files as $name => $data) {
            if ( ! isset ($subfiles [$name]) ) {
                return false;
            }
            if ($subfiles [$name] != $data ) {
                return false;
            }
        }
             return true;
    }
 
    /**
     * Delete submitted files and own record
     *
     * @return void
     *
     */
    public function delete() {
        global $DB;
        \mod_vpl\event\submission_deleted::log( $this );
        vpl_delete_dir( $this->get_data_directory() );
        $DB->delete_records( 'vpl_submissions', array (
                'id' => $this->instance->id
        ) );
    }
 
    /**
     * Return if submission has been graded
     *
     * @return bool
     */
    public function is_graded() {
        return $this->instance->dategraded > 0;
    }
 
    /**
     * Remove grade
     *
     * @return true if removed and false if not
     */
    public function remove_grade() {
        global $USER;
        global $CFG;
        global $DB;
        ignore_user_abort( true );
        if ($this->vpl->is_group_activity()) {
            $usersid = array ();
             foreach ($this->vpl->get_group_members( $this->instance->groupid ) as $user) {
                $usersid [] = $user->id;
            }
        } else {
            $usersid = array (
                    $this->instance->userid
            );
        }
        $grades = array ();
        $gradeinfo = array ();
        $gradeinfo ['userid'] = $this->instance->userid;
        $gradeinfo ['rawgrade'] = null;
        $gradeinfo ['feedback'] = '';
        foreach ($usersid as $userid) {
            $gradeinfo ['userid'] = $userid;
            $grades [$userid] = $gradeinfo;
        }
        $vplinstance = $this->vpl->get_instance();
        if (vpl_grade_item_update( $vplinstance, $grades ) != GRADE_UPDATE_OK) {
            return false;
        }
        if (! empty( $CFG->enableoutcomes )) {
            foreach ($usersid as $userid) {
                $gradinginfo = grade_get_grades( $this->vpl->get_course()->id
                                                 , 'mod'
                                                 , 'vpl'
                                                 , $this->vpl->get_instance()->id
                                                 , $userid );
                if (! empty( $gradinginfo->outcomes )) {
                    $outcomes = array ();
                    foreach ($gradinginfo->outcomes as $oid => $dummy) {
                        $outcomes [$oid] = null;
                    }
                    grade_update_outcomes( 'mod/vpl'
                                          , $this->vpl->get_course()->id
                                          , 'mod'
                                          , VPL
                                          , $this->vpl->get_instance()->id
                                          , $userid, $outcomes );
                }
            }
        }
        $this->instance->grader = 0;
         $this->instance->dategraded = 0;
        $this->instance->grade = null;
        $fn = $this->get_gradecommentsfilename();
        if (! $DB->update_record( 'vpl_submissions', $this->instance )) {
            print_error( 'DB error updating submission grade info' );
        } else {
            if (file_exists( $fn )) {
                unlink( $fn );
            }
        }
        return true;
    }
 
    /**
     * get current grade reduction
     *
     * @param & $reduction
     *          value or factor
     * @param & $percent bool
     *          if true then $reduction is factor
     * @return grade reduction string
     */
    public function grade_reduction(& $reduction, & $percent) {
        $reduction = 0;
        $percent = false;
        $vplinstance = $this->vpl->get_instance();
        if (! ($vplinstance->reductionbyevaluation > 0) ||
            $vplinstance->freeevaluations >= $this->instance->nevaluations ) {
            return;
        }
        $mul = $this->instance->nevaluations - $vplinstance->freeevaluations;
        if ( substr($vplinstance->reductionbyevaluation, -1, 1) == '%' ) {
            $reduction = substr($vplinstance->reductionbyevaluation, 0, -1);
            $reduction = pow( (100.0 - $reduction) / 100.0, $mul);
            $percent = true;
        } else {
            $reduction = $vplinstance->reductionbyevaluation * $mul;
        }
    }
 /**
     * String with the reduction policy
     *
     * @param $grade
     *            optional grade value
     * @return grade reduction string
     */
    public function reduce_grade_string() {
        global $OUTPUT;
        $vplinstance = $this->vpl->get_instance();
        if ( ! ($vplinstance->reductionbyevaluation > 0 ) ) {
            return '';
        }
        $this->grade_reduction($reduction, $percent);
        $value = $reduction;
        if ($percent) {
            $value = (100 - ( $value * 100 ) );
            $value = format_float($value, 2, true, true) . '%';
        } else {
            $value = format_float($value, 2, true, true);
        }
        $vplinstance = $this->vpl->get_instance();
        $html = $this->vpl->str_restriction('finalreduction', $value);
        $html .= ' [' . $this->instance->nevaluations;
        $html .= ' / ' . $vplinstance->freeevaluations;
        $html .= ' -' . $vplinstance->reductionbyevaluation . ']';
        $html .= $OUTPUT->help_icon('finalreduction', VPL);
        return $html;
    }
 
    /**
     * Reduce grade based en number of evaluations
     *
     * @param $grade
     *            grade value
     * @return new grade
     */
     public function reduce_grade($grade) {
        $this->grade_reduction($reduction, $percent);
        if ($reduction > 0) {
            if ($percent) {
                return $grade * $reduction;
            } else {
                return $grade - $reduction;
            }
        }
        return $grade;
    }
 
    /**
     * Set/update grade
     *
     * @param $info object
     *            with grade and comments fields
     * @param $automatic if
     *            automatic grading (default false)
     * @return boolean
     *            true => OK
     */
    public function set_grade($info, $automatic = false) {
        global $USER;
        global $CFG;
        global $DB;
        ignore_user_abort( true );
        $scaleid = $this->vpl->get_grade();
        if ($scaleid == 0 && empty( $CFG->enableoutcomes )) { // No scale no outcomes.
            return false;
        }
        if ($automatic) { // Who grade.
            $this->instance->grader = 0;
        } else {
            $this->instance->grader = $USER->id;
        }
        if ($this->vpl->is_group_activity()) {
            $usersid = array ();
            foreach ($this->vpl->get_group_members( $this->instance->groupid ) as $user) {
                $usersid [] = $user->id;
            }
        } else {
         $usersid = array (
                    $this->instance->userid
            );
        }
        $this->instance->dategraded = time();
        if ($scaleid != 0) {
            // Sanitize grade.
            if ($scaleid > 0) {
                 $floatn = unformat_float($info->grade);
                if ( $floatn !== null && $floatn !== false ) {
                    $info->grade = $floatn;
                }
            } else {
                $info->grade = ( int ) $info->grade;
            }
            $this->instance->grade = $info->grade;
            // Save assessment comments.
            $comments = $info->comments;
            $fn = $this->get_gradecommentsfilename();
            if ($comments) {
                $fp = vpl_fopen( $fn );
                fwrite( $fp, $comments );
                fclose( $fp );
            } else if (file_exists( $fn )) {
                unlink( $fn );
            }
            // Update gradebook.
            $gradinginstance = $this->get_grading_instance();
            if ($gradinginstance) {
                $advancedgrading = $gradinginstance->submit_and_get_grade($info->advancedgrading,
                                                                       $this->instance->id);
            }
            $grades = array ();
            $gradeinfo = array ();
            // If no grade then don't set rawgrade and feedback.
            if ( $scaleid != 0 ) {
                $gradeinfo ['rawgrade'] = $this->reduce_grade($info->grade);
                $gradeinfo ['feedback'] = $this->result_to_html( $comments, false );
                $gradeinfo ['feedbackformat'] = FORMAT_HTML;
            }
           if ($this->instance->grader > 0) { // Don't add grader if automatic.
                $gradeinfo ['usermodified'] = $this->instance->grader;
            } else { // This avoid to use an unexisting userid (0) in the gradebook.
                $gradeinfo ['usermodified'] = $USER->id;
            }
            $gradeinfo ['datesubmitted'] = $this->instance->datesubmitted;
            $gradeinfo ['dategraded'] = $this->instance->dategraded;
            foreach ($usersid as $userid) {
                $gradeinfo ['userid'] = $userid;
                $grades [$userid] = $gradeinfo;
            }
            if (vpl_grade_item_update( $this->vpl->get_instance(), $grades ) != GRADE_UPDATE_OK ) {
                return false;
            }
            // The function vpl_grade_item_update say OK but may be overridden.
            // Check if grade is overridden by comparing save time.
            // Other option is checking the grade_item state.
            $vplinstance = $this->vpl->get_instance();
            $gradesaved = grade_get_grades($vplinstance->course, 'mod', 'vpl',
                    $vplinstance->id, $usersid[0]);
            try {
                $dategraded = $gradesaved->items[0]->grades[$usersid[0]]->dategraded;
            } catch (Exception $e) {
                return false;
            }
            if ($dategraded != $gradeinfo ['dategraded']) {
                return false;
            }
        }
        if (! empty( $CFG->enableoutcomes )) {
            foreach ($usersid as $userid) {
                $gradinginfo = grade_get_grades( $this->vpl->get_course()->id, 'mod'
                                                , 'vpl', $this->vpl->get_instance()->id, $userid );
                if (! empty( $gradinginfo->outcomes )) {
                    $outcomes = array ();
                    foreach ($gradinginfo->outcomes as $oid => $dummy) {
                        $field = 'outcome_grade_' . $oid;
                        if (isset( $info->$field )) {
                            $outcomes [$oid] = $info->$field;
                        } else {
                            $outcomes [$oid] = null;
                        }
                    }
                     $ret = grade_update_outcomes( 'mod/vpl'
                                           , $this->vpl->get_course()->id
                                           , 'mod'
                                           , VPL
                                           , $this->vpl->get_instance()->id
                                           , $userid, $outcomes );
                    if ( ! $ret ) {
                        return false;
                    }
                }
            }
        }
        if (! $DB->update_record( 'vpl_submissions', $this->instance )) {
            print_error( 'DB error updating submission grade info' );
        }
        return true;
    }
 
    /**
     * Get grade comments
     *
     * @return string
     */
    public function get_grade_comments() {
        $ret = '';
        $fn = $this->get_gradecommentsfilename();
        if (file_exists( $fn )) {
            $ret = file_get_contents( $fn );
            // Remove grade reduction information from titles [-*(-#)] .
            if ( ! $this->vpl->has_capability(VPL_GRADE_CAPABILITY) ) {
                $ret = preg_replace('/^(-.*)(\([ \t]*-[0-9]*\.?[0-9]+[ \t]*\)[ \t]*)$/m', '$1', $ret);
            }
        }
        return $ret;
    }
 
    /**
     * is visible this submission instance
     *
     * @return bool
     */
    public function is_visible() {
        global $USER;
        $cm = $this->vpl->get_course_module();
        $instance = $this->instance;
        $ret = $this->vpl->is_visible();
        // Submission owner?
        $ret = $ret && ($USER->id == $instance->userid);
        if ($ret) {
            // Is last submission?
            $lastsub = $this->vpl->last_user_submission( $instance->userid );
            $ret = $ret && ($instance->id == $lastsub->id);
        }
        $ret = $ret || $this->vpl->has_capability( VPL_GRADE_CAPABILITY );
        return $ret;
    }
 
    /**
     * is possible to grade/update this submission instance
     *
     * @return bool
     */
    public function is_grade_able() {
        global $USER;
        if ($this->vpl->get_grade() == 0) { // Is grade_able the instance.
            return false;
        }
        $instance = $this->instance;
        $ret = $this->vpl->has_capability( VPL_GRADE_CAPABILITY );
        // New grade or update if grader.
        $ret = $ret && ($instance->dategraded || $USER->id == $instance->grader);
        $ret = $ret || $this->vpl->has_capability( VPL_MANAGE_CAPABILITY );
        if ($ret) {
            // Is last submission?
            $lastsub = $this->vpl->last_user_submission( $instance->userid );
            $ret = $ret && ($instance->id == $lastsub->id);
        }
        return $ret;
    }
 
    /**
     *
     * @var array of users(graders) objects
     */
    protected static $graders = array ();
 
    /**
     * Return user from DB with cache (automatic grader info for $id===0)
     *
     * @param
     *            $id
     * @return FALSE/user object
     */
    public static function get_grader($id) {
        global $DB;
        if ($id === null) {
            $id = 0;
        }
        if (isset( self::$graders [$id] )) {
            $graderuser = self::$graders [$id];
        } else {
            if ($id <= 0) { // Automatic grading.
                $graderuser = new StdClass();
                if (function_exists( 'get_all_user_name_fields' )) {
                    $fields = get_all_user_name_fields();
                    foreach ($fields as $name => $value) {
                        $graderuser->$name = '';
                    }
                }
                $graderuser->firstname = '';
                $graderuser->lastname = get_string( 'automaticgrading', VPL );
            } else {
                $graderuser = $DB->get_record( 'user', array (
                        'id' => $id
                ) );
            }
            self::$graders [$id] = $graderuser;
        }
        return $graderuser;
    }
    
     /**
     * Get an instance of a grading form if advanced grading is enabled.
     * This is specific to the assignment, marker and student.
     *
     * @return mixed gradingform_instance|null $gradinginstance
     */
     public function get_grading_instance() {
        global $CFG, $USER;
 
        $grademenu = make_grades_menu($this->get_instance()->grade);
        $allowgradedecimals = $this->get_instance()->grade > 0;
 
        $advancedgradingwarning = false;
        $gradingmanager = get_grading_manager($this->vpl->get_context(), 'mod_vpl', 'submissions');
        $gradinginstance = null;
        if ($gradingmethod = $gradingmanager->get_active_method()) {
            $controller = $gradingmanager->get_controller($gradingmethod);
            if ($controller->is_form_available()) {
                $instanceid = optional_param('advancedgradinginstanceid', 0, PARAM_INT);
                $gradinginstance = $controller->get_or_create_instance($instanceid,
                                                                       $USER->id,
                                                                       $this->instance->id);
            } else {
                $advancedgradingwarning = $controller->form_unavailable_notification();
            }
        }
        if ($gradinginstance) {
            $gradinginstance->get_controller()->set_grade_range($grademenu, $allowgradedecimals);
        }
        return $gradinginstance;
    }
 
    /**
     * Get core grade @parm optional grade to show
     *
     * @return string
     */
     public function get_grade_core($grade = null) {
        global $CFG;
        $ret = '';
        $inst = $this->instance;
        if ($inst->dategraded > 0 || $grade != null) {
            $vplinstance = $this->vpl->get_instance();
            $scaleid = $this->vpl->get_grade();
            $options = array ();
            if ($scaleid == 0) {
                return get_string( 'nograde' );
            } else if ($grade == null) {
                if (! function_exists( 'grade_get_grades' )) {
                    require_once($CFG->libdir . '/gradelib.php');
                }
                $userid = $this->get_userid();
                $grades = grade_get_grades($vplinstance->course, 'mod', 'vpl',
                        $vplinstance->id, $userid);
                try {
                    $gradeobj = $grades->items[0]->grades[$userid];
                    $gradestr = $gradeobj->str_long_grade;
                    if ( $this->vpl->has_capability(VPL_GRADE_CAPABILITY) ) {
                        $gradestr .= $gradeobj->hidden ? (' <b>' . get_string( 'hidden', 'core_grades' )) . '</b>' : '';
                        $gradestr .= $gradeobj->locked ? (' <b>' . get_string( 'locked', 'core_grades' )) : '</b>';
                        $gradestr .= $gradeobj->overridden ? (' <b>' . get_string( 'overridden', 'core_grades' )) . '</b>' : '';
                    }
                    return $gradestr;
                } catch ( Exception $e ) {
                    $error = $e;
                    // This try will avoid many checking.
                }
            }
            if ($grade === null) {
                return '';
            }
            if ($scaleid > 0) {
                $grade = format_float($this->reduce_grade($grade), 2, true, true);
                $ret = $grade . ' / ' . $scaleid;
            } else if ($scaleid < 0) {
                $scaleid = - $scaleid;
                $grade = ( int ) $grade;
                if ($scale = $this->vpl->get_scale()) {
                    $options = array ();
                    $options [- 1] = get_string( 'nograde' );
                    $options = $options + make_menu_from_list( $scale->scale );
                    if (isset( $options [$grade] )) {
                        $ret = $options [$grade];
                    }
                }
            }
        }
        return $ret;
    }
 
    /**
     * Print sudmission grade
     *
     * @param $detailed true
     *            show detailed grade (default false)
     * @param $return true
     *            return string/ false print grade (default false)
     * @return mix string/void
     */
    public function print_grade($detailed = false, $return = false) {
        global $CFG, $OUTPUT,$PAGE,$USER;
        $ret = '';
        $inst = $this->instance;
        if ($inst->dategraded > 0) {
            $grader = $this->get_grader( $inst->grader );
            $a = new stdClass();
            $a->date = userdate( $inst->dategraded );
            $a->gradername = fullname( $grader );
            $ret .= get_string( 'gradedonby', VPL, $a ) . '<br />';
            if ($this->vpl->get_grade() != 0) {
                $ret .= $this->vpl->str_restriction('grade', $this->get_grade_core(), true) . '<br>';
                if ($detailed) {
                 $ret .= $this->reduce_grade_string() . '<br>';
                    $feedback = $this->get_grade_comments();
                    if ($feedback) {
                        $div = new vpl_hide_show_div( true );
                        $tagid = 'gradercomments' . $this->get_instance()->id;
                        $ret .= '<b>' . get_string( 'gradercomments', VPL ) . $div->generate( true ) . '</b><br />';
                        $ret .= $div->begin_div( true ) . s($feedback) . $div->end_div( true );
                        $ret .= "<script>VPL_Util.addResults('{$div->get_div_id()}', false, true);</script>";
                    }
                }
            }
            $cangrade = has_capability(VPL_GRADE_CAPABILITY, $this->vpl->get_context());
            // Only show the grade if it is not hidden in gradebook.
            $userid = (has_capability( VPL_GRADE_CAPABILITY, $this->vpl->get_context() ) || has_capability(
                        VPL_MANAGE_CAPABILITY, $this->vpl->get_context() )) ? null : $USER->id;
            require_once($CFG->libdir . '/gradelib.php');
            $gradeinfo = grade_get_grades( $this->vpl->get_course()->id, 'mod', 'vpl', $this->vpl->get_instance()->id, $userid );
            $gradinginstance = $this->get_grading_instance();
            if ($gradinginstance) {
                $ret .= $gradinginstance->get_controller()->render_grade($PAGE,
                                                             $this->instance->id,
                                                             $gradeinfo,
                                                             $this->instance->grade,
                                                             $cangrade);
            }
            if (! empty( $CFG->enableoutcomes )) {
                // Bypass unknow gradelib not load.
                if (! function_exists( 'grade_get_grades' )) {
                    require_once($CFG->libdir . '/gradelib.php');
                }
                $gradinginfo = grade_get_grades( $this->vpl->get_course()->id, 'mod'
                                                 , 'vpl', $this->vpl->get_instance()->id
                                                 , $this->instance->userid );
                if (! empty( $gradinginfo->outcomes )) {
                    $ret .= '<b>' . get_string( 'outcomes', 'grades' ) . '</b><br />';
                    foreach ($gradinginfo->outcomes as $oid => $outcome) {
                        $ret .= s( $outcome->name );
                        $ret .= ' ' . s( $outcome->grades [$inst->userid]->str_grade ) . '<br />';
                    }
                }
            }
        }
        if ($return) {
          return $ret;
        } else {
            if ($ret) {
                echo $OUTPUT->box( $ret );
 
            }
        }
    }
 
    /**
     * Print sudmission info
     */
    public function print_info($autolink = false) {
        // TODO improve show submission info.
        global $OUTPUT, $DB;
        $id = $this->vpl->get_course_module()->id;
        $userid = $this->instance->userid;
        $submissionid = $this->instance->id;
        if ($autolink) {
            $url = vpl_mod_href( 'forms/submissionview.php', 'id', $id, 'userid', $userid, 'submissionid', $submissionid );
            echo '<a href="' . $url . '">';
        }
        $subdate = userdate( $this->instance->datesubmitted );
        p( get_string( 'submittedonp', VPL, $subdate ) );
        if ($autolink) {
            echo '</a>';
        }
        $url = vpl_mod_href( 'views/downloadsubmission.php', 'id', $id, 'userid', $userid, 'submissionid', $submissionid );
        echo ' (<a href="' . $url . '">' . get_string( 'download', VPL );
        echo '</a>)';
        // Show evaluation link.
        $ce = $this->getce();
        if ($this->vpl->get_instance()->evaluate && ! $this->is_graded()) {
            $url = vpl_mod_href( 'forms/evaluation.php', 'id', $id, 'userid', $userid );
            echo ' (<a href="' . $url . '">' . get_string( 'evaluate', VPL );
            echo '</a>)';
        }
        echo '<br />';
        if ( $this->vpl->is_group_activity() ) {
            $user = $DB->get_record( 'user', array (
                        'id' => $userid
                ) );
            if ( $user ) {
                $userinfo = $OUTPUT->user_picture( $user ) . ' '  . fullname( $user );
                echo get_string( 'submittedby', VPL, $userinfo );
            }
            $users = $this->vpl->get_group_members($this->instance->groupid);
            foreach ($users as $u) {
                if ( $u->id != $userid ) {
                    echo '<br>';
                    break;
                }
            }
            foreach ($users as $u) {
                if ( $u->id != $userid ) {
                    echo $OUTPUT->user_picture( $u ) . ' ' . fullname( $u );
                }
            }
        }
        $commmets = $this->instance->comments;
        if ($commmets > '') {
            echo '<br>';
            echo '<h4>' . get_string( 'comments', VPL ) . '</h4>';
            echo $OUTPUT->box( nl2br( s( $commmets ) ) );
        }
    }
 
    /**
     * Print compilation and execution
     *
     * @return void
     */
    public function print_ce() {
        global $OUTPUT;
        $ce = $this->getce();
        if ($ce ['compilation'] === 0) {
            return;
        }
        $this->get_ce_html( $ce, $compilation, $execution, $grade, true, true );
        if (strlen( $compilation ) + strlen( $execution ) + strlen( $grade ) > 0) {
            $div = new vpl_hide_show_div( ! $this->is_graded() || ! $this->vpl->get_visiblegrade() );
            echo '<h3>' . get_string( 'automaticevaluation', VPL ) . $div->generate( true ) . '</h3>';
            $div->begin_div();
            echo $OUTPUT->box_start();
            if (strlen( $grade ) > 0) {
                echo '<b>' . $grade . '</b><br>';
                echo $this->reduce_grade_string() . '<br>';
            }
            $compilation = $ce ['compilation'];
            if (strlen( $compilation ) > 0) {
                $div = new vpl_hide_show_div( true );
                echo '<b>' . get_string( 'compilation', VPL ) . $div->generate( true ) . '</b><br />';
                echo $div->begin_div( true ) . s($compilation) . $div->end_div( true );
                echo "<script>VPL_Util.addResults('{$div->get_div_id()}', false, true);</script>";
            }
            if (strlen( $execution ) > 0) {
                $proposedcomments = $this->proposedcomment( $ce ['execution'] );
                if (strlen( $execution )) {
                    $div = new vpl_hide_show_div( true );
                    echo '<b>' . get_string( 'comments', VPL ) . $div->generate( true ) . "</b><br />";
                    echo $div->begin_div( true ) . s($proposedcomments) . $div->end_div( true );
                    echo "<script>VPL_Util.addResults('{$div->get_div_id()}', false, true);</script>";
                }
            }
            echo $OUTPUT->box_end();
            $div->end_div();
        }
    }
 
    /**
     * Print sudmission
     */
    public function print_submission() {
        $this->print_info();
        // Not automatic graded show proposed evaluation.
        if (! $this->is_graded() || ! $this->vpl->get_visiblegrade() || $this->vpl->has_capability( VPL_GRADE_CAPABILITY )) {
            $this->print_CE();
        }
        $this->get_submitted_fgm()->print_files();
    }
    const GRADETAG = 'Grade :=>>';
    const COMMENTTAG = 'Comment :=>>';
    const BEGINCOMMENTTAG = '<|--';
    const ENDCOMMENTTAG = '--|>';
    public function proposedgrade($text) {
        $ret = '';
        $nl = vpl_detect_newline( $text );
        $lines = explode( $nl, $text );
        foreach ($lines as $line) {
            if (strpos( $line, self::GRADETAG ) === 0) {
                $ret = trim( substr( $line, strlen( self::GRADETAG ) ) );
            }
        }
        return $ret;
    }
    public function proposedcomment($text) {
        $incomment = false;
        $ret = '';
        $nl = vpl_detect_newline( $text );
        $lines = explode( $nl, $text );
        foreach ($lines as $line) {
            $line = rtrim( $line ); // Remove \r, spaces & tabs.
            $tline = trim( $line );
            if ($incomment) {
                if ($tline == self::ENDCOMMENTTAG) {
                    $incomment = false;
                } else {
                    $ret .= $line . "\n";
                }
            } else {
                if (strpos( $line, self::COMMENTTAG ) === 0) {
                    $ret .= substr( $line, strlen( self::COMMENTTAG ) ) . "\n";
                } else if ($tline == self::BEGINCOMMENTTAG) {
                    $incomment = true;
                }
            }
        }
        return $ret;
    }
 
    /**
     * Add link to file line format filename:linenumber:
     *
     * @param
     *            text to be converted
     * @return string text with links
     */
    public function add_filelink($text) {
      // Format filename:linenumber.
        $ret = '';
        $list = $this->get_submitted_fgm()->getFileList();
        usort( $list, 'vpl_compare_filenamebylengh' );
        $nl = vpl_detect_newline( $text );
        $lines = explode( $nl, $text );
        // Prepare reg expressions.
        $regexps = array ();
        foreach ($list as $filename) {
            $escapefilename = preg_quote( $filename, "/" );
            $regexps [] = '/(.*?)(' . $escapefilename . ')\:( *)([0-9]+)(.*)/';
        }
        // Process lines.
        foreach ($lines as $line) {
            foreach ($regexps as $regexp) {
                if (preg_match( $regexp, $line, $r )) {
                    $line = $r [1] . '<a href="#' . $r [2] . '.' . $r [4] . '">' . $r [2] . ':' . $r [3] . $r [4] . '</a>' . $r [5];
                    break;
                }
            }
            $ret .= $line . "\n";
        }
        return $ret;
    }
    /**
     * Convert compilation/execution result to HTML
     *
     * @param
     *            text to be converted
     * @return string HTML
     */
    private function get_last_comment($title, &$comment, $dropdown) {
        $html = '';
        if ($title > '') { // Previous comment.
            if ($comment == '' || ! $dropdown) {
                $html .= '<b>';
                $html .= s( $title );
                $html .= '</b><br />';
                $html .= $comment;
            } else {
             $div = new vpl_hide_show_div( false );
                $html .= $div->generate( true );
                $html .= '<b>';
                $html .= s( $title );
                $html .= '</b><br />';
                $html .= $div->begin_div( true ) . $comment . $div->end_div( true );
            }
        } else if ($comment > '') { // No title comment.
            $html .= $comment;
        }
        $comment = ''; // Reset comment.
        return $html;
    }
    /**
     * Convert compilation/execution result to HTML
     *
     * @param
     *            text to be converted
     * @return string HTML
     */
    public function result_to_html($text, $dropdown = true) {
        if ($text == '' || $text == null) {
            return '';
        }
        $html = ''; // Total html output.
        $title = ''; // Title of comment.
        $comment = ''; // Comment.
        $nl = vpl_detect_newline( $text );
        $lines = explode( $nl, $text );
        $casetoshow = ''; // Pre to show.
        foreach ($lines as $line) {
            $clean = trim( $line );
            // End of case?
            if (strlen( $casetoshow ) > 0 && ! (strlen( $clean ) > 0 && $clean [0] == '>')) {
                $comment .= '<pre><i>';
                $comment .= s( $casetoshow );
                $comment .= '</i></pre>';
                $casetoshow = '';
            }
            // Is title line.
            if (strlen( $line ) > 1 && $line [0] == '-') { // Title.
             $html .= $this->get_last_comment( $title, $comment, $dropdown );
                $line = trim( substr( $line, 1 ) );
                if ($line [strlen( $line ) - 1] == ')') { // Has grade?
                    $posopen = strrpos( $line, '(' );
                    if ($posopen !== false) {
                        $grade = substr( $line, $posopen + 1, strlen( $line ) - $posopen - 2 );
                        $grade = trim( $grade );
                        if ($grade < 0) {
                            $title = substr( $line, 0, $posopen );
                            // TODO implement grader information.
                            continue;
                        }
                    }
                }
                $title = $line;
            } else if (strlen( $clean ) > 0 && $clean [0] == '>') { // Case.
                $pos = strpos( $line, '>' );
                $rest = substr( $line, $pos + 1 );
                $casetoshow .= $rest . "\n";
            } else if (strlen( $clean ) > 8 && (substr( $clean, 0, 5 ) == "http:" || substr( $clean, 0, 6 ) == "https:")) {
                // Is url?
                // Output spaces.
                $nspaces = strpos( $line, 'h' );
                for ($i = 0; $i < $nspaces; $i ++) {
                    $comment .= '&nbsp;';
                }
                $spacepos = strpos( $clean, ' ' );
                if ($spacepos) {
                    $comment .= '<a href="';
                    $comment .= urlencode( substr( $clean, 0, $spacepos ) );
                    $comment .= '">';
                    $comment .= s( substr( $clean, $spacepos + 1, strlen( $clean ) - $spacepos - 1 ) );
                    $comment .= '</a>';
                } else {
                    $comment .= '<a href="';
                    $comment .= urlencode( $clean );
                    $comment .= '">';
                    $comment .= s( $clean );
                    $comment .= '</a>';
                }
                $comment .= '<br />';
            } else if (strlen( $line ) > 1 && $line [0] == '#') { // Teacher comment
             $html .= $this->get_last_comment( $title, $comment, $dropdown );
                
            }else{ // Regular text.
                $comment .= $this->add_filelink( s( $line ) ) . '<br />';
            }
        }
        if (strlen( $casetoshow ) > 0) {
            $comment .= '<pre><i>';
            $comment .= s( $casetoshow );
            $comment .= '</i></pre>';
        }
        $html .= $this->get_last_comment( $title, $comment, $dropdown );
        return $html;
    }
    /**
     * Add a new text to the list
     */
    public function filter_feedback_add(&$list, $text, $grade = 0) {
        $text = trim( $text );
        if (! isset( $list [$text] )) {
            $list [$text] = new StdClass();
            $list [$text]->count = 0;
            $list [$text]->grades = array ();
        }
        $list [$text]->count ++;
        $list [$text]->grades [$grade] = true;
    }
    /**
     * Filter Convert compilation/execution result to HTML
     *
     * @param
     *            text to be filter
     * @return array of mensajes
     */
    public function filter_feedback(&$list) {
     $text = $this->get_grade_comments();
        $nl = vpl_detect_newline( $text );
        $lines = explode( $nl, $text );
        foreach ($lines as $line) {
            $line = rtrim( $line );
            // Is title line.
            if (strlen( $line ) > 2 && $line [0] == '-') { // Title.
                $line = substr( $line, 1 );
                if ($line [strlen( $line ) - 1] == ')') { // Has grade?
                    $posopen = strrpos( $line, '(' );
                    if ($posopen !== false) {
                        $grade = substr( $line, $posopen + 1, strlen( $line ) - $posopen - 2 );
                        $grade = trim( $grade );
                        if ($grade < 0) {
                            $this->filter_feedback_add( $list, substr( $line, 0, $posopen ), $grade );
                            continue;
                        }
                    }
                }
                $this->filter_feedback_add( $list, $line, 0 );
            }
        }
    }
    const COMPILATIONFN = 'compilation.txt';
    const EXECUTIONFN = 'execution.txt';
    /**
     * Save Compilation Execution result to files
     *
     * @param $result array
     *            response from server
     * @return uvoid
     */
    public function savece($result) {
        global $DB;
        ignore_user_abort( true );
        $oldce = $this->getce();
        // Count new evaluaions.
        $newevaluation = false;
        if ( $oldce['executed'] == 0 && $result['executed'] > 0
             && ! $this->vpl->has_capability(VPL_GRADE_CAPABILITY) ) {
            $newevaluation = true;
        }
        // After first execution, keep execution state of the submission.
        if ( $oldce['executed'] > 0 && $result['executed'] == 0) {
            $result['executed'] = 1;
            $result['execution'] = '';
        }
 
        $compfn = $this->get_data_directory() . '/' . self::COMPILATIONFN;
        if (file_exists( $compfn )) {
            unlink( $compfn );
        }
        $execfn = $this->get_data_directory() . '/' . self::EXECUTIONFN;
        if (file_exists( $execfn )) {
            unlink( $execfn );
        }
        file_put_contents( $compfn, $result ['compilation'] );
        if ($result ['executed'] > 0) {
            file_put_contents( $execfn, $result ['execution'] );
        }
        if ( $newevaluation ) {
            $this->instance->nevaluations ++;
            $DB->update_record(VPL_SUBMISSIONS, $this->instance);
        }
    }
 
    /**
     * Get Compilation Execution information from files
     *
     * @return array with server response fields
     */
    public function getce() {
        $ret = array ();
        $compfn = $this->get_data_directory() . '/' . self::COMPILATIONFN;
        if (file_exists( $compfn )) {
            $ret ['compilation'] = file_get_contents( $compfn );
        } else {
            $ret ['compilation'] = 0;
        }
        $execfn = $this->get_data_directory() . '/' . self::EXECUTIONFN;
        if (file_exists( $execfn )) {
            $ret ['executed'] = 1;
            $ret ['execution'] = file_get_contents( $execfn );
        } else {
         $ret ['executed'] = 0;
        }
        $ret ['nevaluations'] = $this->instance->nevaluations;
        $vplinstance = $this->vpl->get_instance();
        $ret ['freeevaluations'] = $vplinstance->freeevaluations;
        $ret ['reductionbyevaluation'] = $vplinstance->reductionbyevaluation;
        return $ret;
    }
 
    /**
     * Get compilation, execution and proposed grade from array
     *
     * @param $response array
     *            response from server
     * @param
     *            $compilation
     * @param
     *            $execution
     * @param
     *            $grade
     * @return void
     */
    public function get_ce_html($response, &$compilation, &$execution, &$grade, $dropdown, $returnrawexecution = false) {
        $compilation = '';
        $execution = '';
        $grade = '';
        if ($response ['compilation']) {
            $compilation = $this->result_to_html( $response ['compilation'], $dropdown );
            if (strlen( $compilation )) {
                $compilation = '<b>' . get_string( 'compilation', VPL ) . '</b><br />' . $compilation;
            }
        }
        if ($response ['executed'] > 0) {
            $rawexecution = $response ['execution'];
            $proposedcomments = $this->proposedcomment( $rawexecution );
            $proposedgrade = $this->proposedgrade( $rawexecution );
            $execution = $this->result_to_html( $proposedcomments, $dropdown );
            if (strlen( $execution )) {
                $execution = '<b>' . get_string( 'comments', VPL ) . "</b><br />\n" . $execution;
            }
            if (strlen( $proposedgrade )) {
                $sgrade = $this->get_grade_core( $proposedgrade );
                $grade = get_string( 'proposedgrade', VPL, $sgrade );
            }
            // Show raw ejecution if no grade or comments.
            if (strlen( $rawexecution ) > 0 && (strlen( $execution ) + strlen( $proposedgrade ) == 0)) {
                $execution .= "<br />\n";
                $execution .= '<b>' . get_string( 'execution', VPL ) . "</b><br />\n";
                $execution .= '<pre>' . s( $rawexecution ) . '</pre>';
            } else if ($returnrawexecution && strlen( $rawexecution ) > 0
                       && ($this->vpl->has_capability( VPL_MANAGE_CAPABILITY ))) {
                // Show raw ejecution if manager and $returnrawexecution.
                $div = new vpl_hide_show_div();
                $execution .= "<br />\n";
                $execution .= '<b>' . get_string( 'execution', VPL ) . $div->generate( true ) . "</b><br />\n";
                $execution .= $div->begin_div( true );
                $execution .= '<pre>' . s( $rawexecution ) . '</pre>';
                $execution .= $div->end_div( true );
            }
        }
    }
    public function get_ce_for_editor($response = null) {
        $ce = new stdClass();
        $ce->compilation = '';
        $ce->evaluation = '';
        $ce->execution = '';
        $ce->grade = '';
        $ce->nevaluations = $this->instance->nevaluations;
        $vplinstance = $this->vpl->get_instance();
        $ce->freeevaluations = $vplinstance->freeevaluations;
        $ce->reductionbyevaluation = $vplinstance->reductionbyevaluation;
 
        if ($response == null) {
            $response = $this->getce();
        }
        if ($response ['compilation']) {
            $ce->compilation = $response ['compilation'];
        }
        if ($response ['executed'] > 0) {
            $rawexecution = $response ['execution'];
            $evaluation = $this->proposedcomment( $rawexecution );
            $proposedgrade = $this->proposedgrade( $rawexecution );
            $ce->evaluation = $evaluation;
            // TODO Important what to show to students about grade.
            if (strlen( $proposedgrade ) && $this->vpl->get_instance()->grade) {
                $sgrade = $this->get_grade_core( $proposedgrade );
                $ce->grade = get_string( 'proposedgrade', VPL, $sgrade );
            }
            // Show raw ejecution if no grade or comments.
            $manager = $this->vpl->has_capability( VPL_MANAGE_CAPABILITY );
            if ((strlen( $rawexecution ) > 0 && (strlen( $evaluation ) + strlen( $proposedgrade ) == 0)) || $manager) {
                $ce->execution = $rawexecution;
            }
        }
        return $ce;
    }
    public function get_detail() {
        $ret = '';
        $subf = $this->get_submitted_fgm();
        $filelist = $subf->getFileList();
        foreach ($filelist as $filename) {
            $data = $subf->getFileData( $filename );
            if ($ret > '') {
                $ret .= ', ';
            }
            // TODO too slow calculus.
            $nl = vpl_detect_newline( $data );
            $ret .= $filename . ' ' . strlen( $data ) . 'b ' . count( explode( $nl, $data ) ) . 'l';
        }
        return $ret;
    }
    public function get_ce_parms() {
        $response = $this->getce();
        $this->get_ce_html( $response, $compilation, $execution, $grade, false );
        $params = '';
        if (strlen( $compilation )) {
            $params .= vpl_param_tag( 'compilation', $compilation );
        }
            if (strlen( $execution )) {
            $params .= vpl_param_tag( 'evaluation', $execution );
        }
        if (strlen( $grade )) {
            $params .= vpl_param_tag( 'grade', $grade );
        }
        return $params;
    }
}