<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.

/**
 * Plays the game "Snakes and Ladders".
 *
 * @package    mod_game
 * @subpackage snakes
 * @copyright  2007 Vasilis Daloukas
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

/**
 * Plays the game "Snakes and Ladders".
 *
 * @param stdClass $cm
 * @param stdClass $game
 * @param stdClass $attempt
 * @param stdClass $snakes
 * @param stdClass $context
 * @param stdClass $course
 * @return null
 * @throws moodle_exception
 * @package mod_game
 *
 */
function game_snakes_continue(
        $cm,
        $game,
        $attempt,
        $snakes,
        $context,
        $course
) {
    if ($attempt !== false && $snakes !== false) {
        game_snakes_play($cm, $game, $attempt, $snakes, $context, $course);
        return;
    }

    if ($attempt === false) {
        $attempt = game_addattempt($game);
    }

    $newrec = new stdClass();
    $newrec->id = $attempt->id;
    $newrec->snakesdatabaseid = $game->param3;
    if ($newrec->snakesdatabaseid == 0) {
        $newrec->snakesdatabaseid = 1;
    }
    $newrec->position = 1;
    $newrec->queryid = 0;
    $newrec->dice = rand(1, 6);
    if (!game_insert_record('game_snakes', $newrec)) {
        throw new moodle_exception('snakes_error', 'game', 'game_snakes_continue: error inserting in game_snakes');
    }

    return game_snakes_play($cm, $game, $attempt, $newrec, $context, $course);
}

/**
 * Plays the game "Snakes and Ladders".
 *
 * @param stdClass $cm
 * @param stdClass $game
 * @param stdClass $attempt
 * @param stdClass $snakes
 * @param stdClass $context
 * @param stdClass $course
 * @throws coding_exception
 * @throws dml_exception
 * @throws moodle_exception
 * @package mod_game
 *
 */
function game_snakes_play($cm, $game, $attempt, $snakes, $context, $course) {
    global $CFG, $DB;

    $board = game_snakes_get_board($game);
    $showboard = false;

    if ($snakes->position > $board->usedcols * $board->usedrows && $snakes->queryid <> 0) {
        echo '<B>' . get_string('win', 'game') . '</B><BR>';
        echo '<br>';
        echo "<a href=\"$CFG->wwwroot/mod/game/attempt.php?id={$cm->id}\">" .
            get_string('nextgame', 'game') . '</a> &nbsp; &nbsp; &nbsp; &nbsp; ';
        echo "<a href=\"$CFG->wwwroot/course/view.php?id=$cm->course\">" .
                get_string('finish', 'game') . '</a> ';

        $gradeattempt = 1;
        $finish = 1;
        game_updateattempts($game, $attempt, $gradeattempt, $finish, $cm, $course);
    } else {
        $finish = false;
        if ($snakes->queryid == 0) {
            game_snakes_computenextquestion($game, $snakes, $query);
        } else {
            $query = $DB->get_record('game_queries', [ 'id' => $snakes->queryid]);
        }
        if ($game->toptext != '') {
            echo $game->toptext . '<br>';
        }
        $showboard = true;
    }

    if ($showboard && $game->param8 == 0) {
        game_snakes_showquestion($cm->id, $game, $snakes, $query, $context);
    }
?>
    <script language="javascript" event="onload" for="window">
    <!--
    let retVal = [];
    const elements = document.getElementsByTagName("*");
    for (const item of elements) {
        if(item.type == 'text'){
            item.focus();
            break;
        }
    }
    -->
    </script>

    <table>
    <tr>
        <td>
<div id="board" STYLE="position:relative; left:0px;top:0px;
    width:<?php p($board->width); ?>px; height:<?php p($board->height); ?>px;">
<img src="<?php echo $board->imagesrc; ?>">
</div>

    <?php
    if (!$finish) {
        game_snakes_showdice($snakes, $board);
    }
    ?>

    </td>
    </tr>
    </table>

    <?php
    if ($game->bottomtext != '') {
        echo '<br>' . $game->bottomtext;
    }

    if ($showboard && $game->param8 != 0) {
        game_snakes_showquestion($cm->id, $game, $snakes, $query, $context);
    }
}

/**
 * Show dice
 *
 * @package mod_game
 *
 * @param stdClass $snakes
 * @param boolean $board
 */
function game_snakes_showdice($snakes, $board) {
    $pos = game_snakes_computeplayerposition($snakes, $board);
?>
<div ID="player1" STYLE="position:relative; left:<?php p($pos->x);?>px; top:<?php p($pos->y);?>px;" >
<img src="snakes/1/player1.png"
alt="<?php print_string('snakes_player', 'game', ($snakes->position + 1)); /*Accessibility. */ ?>"
width="<?php echo $pos->width; ?>"
height="<?php echo $pos->height; ?>"/>
</div>

<div ID="dice" STYLE="position:relative;
left:<?php p($board->width + round($board->width / 3)); ?>px;
top:<?php p(-2 * round($board->height / 3));?>px; ">
    <img src="snakes/1/dice<?php p($snakes->dice);?>.png" alt="<?php print_string('snakes_dice', 'game', $snakes->dice) ?>" />
    </div>
    <?php
}

/**
 * Computes player's position.
 *
 * @package mod_game
 *
 * @param stdClass $snakes
 * @param stdClass $board
 */
function game_snakes_computeplayerposition($snakes, $board) {
    $x = ($snakes->position - 1) % $board->usedcols;
    $y = floor(($snakes->position - 1) / $board->usedcols);

    $cellwidth = ($board->width - $board->headerx - $board->footerx) / $board->usedcols;
    $cellheight = ($board->height - $board->headery - $board->footery) / $board->usedrows;

    $pos = new stdClass();
    $pos->width = 22;
    $pos->height = 22;

    $pos->ofsx = 0;
    $pos->ofsy = $pos->height;

    switch ($board->direction) {
        case 1:
            if (($y % 2) == 1) {
                $x = $board->usedcols - $x - 1;
            }
            $pos->x = $board->headerx + $x * $cellwidth + ($cellwidth - $pos->width) / 2 + $pos->ofsx;
            $pos->y = $board->footery + $y * $cellheight + ($cellheight - $pos->height) / 2 + $pos->ofsy;
            $pos->x = round($pos->x);
            $pos->y = round(-$pos->y);
            break;
    }

    return $pos;
}

/**
 * Computes next question.
 *
 * @param stdClass $game
 * @param stdClass $snakes
 * @param stdClass $query
 * @return bool
 * @throws coding_exception
 * @throws dml_exception
 * @throws moodle_exception
 * @package mod_game
 *
 */
function game_snakes_computenextquestion($game, &$snakes, &$query) {
    global $DB, $USER;

    // Retrieves CONST_GAME_TRIES_REPETITION words and select the one which is used fewer times.
    if (($recs = game_questions_selectrandom($game, 1, CONST_GAME_TRIES_REPETITION)) == false) {
        return false;
    }

    $minnum = 0;
    $query = new stdClass();
    foreach ($recs as $rec) {
        $a = [ 'gameid' => $game->id, 'userid' => $USER->id,
                'questionid' => $rec->questionid, 'glossaryentryid' => $rec->glossaryentryid];
        if (($rec2 = $DB->get_record('game_repetitions', $a, 'id,repetitions AS r')) != false) {
            if (($rec2->r < $minnum) || ($minnum == 0)) {
                $minnum = $rec2->r;
                $query->glossaryentryid = $rec->glossaryentryid;
                $query->questionid = $rec->questionid;
            }
        } else {
            $query->glossaryentryid = $rec->glossaryentryid;
            $query->questionid = $rec->questionid;
            break;
        }
    }

    if (($query->glossaryentryid == 0) && ($query->questionid == 0)) {
        return false;
    }
    $query->gamekind = $game->gamekind;
    $query->attemptid = $snakes->id;
    $query->gameid = $game->id;
    $query->userid = $USER->id;
    $query->sourcemodule = $game->sourcemodule;
    $query->score = 0;
    $query->timelastattempt = time();
    if (!($query->id = $DB->insert_record('game_queries', $query))) {
        throw new moodle_exception('snakes_error', 'game', 'Can\'t insert to table game_queries');
    }

    $snakes->queryid = $query->id;

    $updrec = new stdClass();
    $updrec->id = $snakes->id;
    $updrec->queryid = $query->id;
    $updrec->dice = $snakes->dice = rand(1, 6);

    if (!$DB->update_record('game_snakes', $updrec)) {
        throw new moodle_exception('snakes_error', 'game', 'game_questions_selectrandom: error updating in game_snakes');
    }

    game_update_repetitions($game->id, $USER->id, $query->questionid, $query->glossaryentryid);

    return true;
}

/**
 * Shows the question.
 *
 * @param int $id
 * @param stdClass $game
 * @param stdClass $snakes
 * @param stdClass $query
 * @param stdClass $context
 * @throws coding_exception
 * @throws moodle_exception
 * @package mod_game
 *
 */
function game_snakes_showquestion($id, $game, $snakes, $query, $context) {
    if ($game->sourcemodule == 'glossary') {
        game_snakes_showquestion_glossary($id, $snakes, $query, $game);
    } else {
        game_snakes_showquestion_question($game, $id, $snakes, $query, $context);
    }
}

/**
 * Shows the question.
 *
 * @param stdClass $game
 * @param int $id
 * @param stdClass $snakes
 * @param stdClass $query
 * @param stdClass $context
 * @throws coding_exception
 * @throws moodle_exception
 * @package mod_game
 *
 */
function game_snakes_showquestion_question($game, $id, $snakes, $query, $context) {
    global $CFG;

    $questionlist = $query->questionid;
    $questions = game_sudoku_getquestions($questionlist);

    // Start the form.
    echo "<form id=\"responseform\" method=\"post\" " .
        "action=\"{$CFG->wwwroot}/mod/game/attempt.php\" onclick=\"this.autocomplete='off'\">\n";
    echo "<center><input type=\"submit\" name=\"finishattempt\" value=\"" . get_string('sudoku_submit', 'game') . "\"></center>\n";

    // Add a hidden field with the quiz id.
    echo '<input type="hidden" name="id" value="' . s($id) . "\" />\n";
    echo '<input type="hidden" name="action" value="snakescheck" />';
    echo '<input type="hidden" name="queryid" value="' . $query->id . "\" />\n";

    // Print all the questions.
    foreach ($questions as $question) {
        game_print_question($game, $question, $context);
    }
    // Add a hidden field with questionids.
    echo '<input type="hidden" name="questionids" value="' . $questionlist . "\" />\n";

    echo "</form>\n";
}

/**
 * Show a glossary question.
 *
 * @param int $id
 * @param stdClass $snakes
 * @param stdClass $query
 * @param stdClass $game
 * @throws coding_exception
 * @throws dml_exception
 * @package mod_game
 *
 */
function game_snakes_showquestion_glossary($id, $snakes, $query, $game) {
    global $CFG, $DB;

    $entry = $DB->get_record('glossary_entries', ['id' => $query->glossaryentryid]);

    // Start the form.
    echo "<form id=\"responseform\" method=\"post\" " .
        "action=\"{$CFG->wwwroot}/mod/game/attempt.php\" onclick=\"this.autocomplete='off'\">\n";
    echo "<center><input type=\"submit\" name=\"finishattempt\" value=\"" . get_string('sudoku_submit', 'game') . "\"></center>\n";

    // Add a hidden field with the queryid.
    echo '<input type="hidden" name="id" value="' . s($id) . "\" />\n";
    echo '<input type="hidden" name="action" value="snakescheckg" />';
    echo '<input type="hidden" name="queryid" value="' . $query->id . "\" />\n";

    // Print all the questions.

    // Add a hidden field with glossaryentryid.
    echo '<input type="hidden" name="glossaryentryid" value="' . $query->glossaryentryid . "\" />\n";

    $sql = "SELECT id,course FROM {$CFG->prefix}glossary WHERE id={$game->glossaryid}";
    $glossary = $DB->get_record_sql($sql);
    $cmglossary = get_coursemodule_from_instance('glossary', $game->glossaryid, $glossary->course);
    $contextglossary = game_get_context_module_instance($cmglossary->id);
    $s = game_filterglossary(
        str_replace('\"', '"', $entry->definition),
        $query->glossaryentryid,
        $contextglossary->id,
        $game->course
    );
    echo $s . '<br>';

    echo get_string('answer') . ': ';
    echo "<input type=\"text\" name=\"answer\" size=30 /><br>";

    echo "</form>\n";
}

/**
 * Checks if answer is correct.
 *
 * @param stdClass $cm
 * @param stdClass $game
 * @param stdClass $attempt
 * @param stdClass $snakes
 * @param stdClass $context
 * @param stdClass $course
 * @throws coding_exception
 * @throws dml_exception
 * @throws moodle_exception
 * @package mod_game
 *
 */
function game_snakes_check_questions($cm, $game, $attempt, $snakes, $context, $course) {
    global $DB;

    $responses = data_submitted();

    if ($responses->queryid != $snakes->queryid) {
        game_snakes_play($cm, $game, $attempt, $snakes, $context, $course);
        return;
    }

    $questionlist = $DB->get_field('game_queries', 'questionid', [ 'id' => $responses->queryid]);

    $questions = game_sudoku_getquestions($questionlist);
    $correct = false;
    $query = '';
    foreach ($questions as $question) {
        $query = new stdClass();
        $query->id = $snakes->queryid;

        $grade = game_grade_responses($question, $responses, 100, $answertext, $answered);
        if ($grade < 99) {
            // Wrong answer.
            game_update_queries($game, $attempt, $query, 0, $answertext);
            continue;
        }

        // Correct answer.
        $correct = true;

        game_update_queries($game, $attempt, $query, 1, '');
    }

    // Set the grade of the whole game.
    game_snakes_position($cm, $game, $attempt, $snakes, $correct, $query, $context, $course);
}

/**
 * Checks if the glossary answer is correct.
 *
 * @param stdClass $cm
 * @param stdClass $game
 * @param stdClass $attempt
 * @param stdClass $snakes
 * @param stdClass $context
 * @param stdClass $course
 * @throws coding_exception
 * @throws dml_exception
 * @throws moodle_exception
 * @package mod_game
 *
 */
function game_snakes_check_glossary($cm, $game, $attempt, $snakes, $context, $course) {
    global $DB;

    $responses = data_submitted();

    if ($responses->queryid != $snakes->queryid) {
        game_snakes_play($cm, $game, $attempt, $snakes, $context, $course);
        return;
    }

    $query = $DB->get_record('game_queries', [ 'id' => $responses->queryid]);

    $glossaryentry = $DB->get_record('glossary_entries', [ 'id' => $query->glossaryentryid]);

    $useranswer = $responses->answer;

    if (game_upper($useranswer) != game_upper($glossaryentry->concept)) {
        // Wrong answer.
        $correct = false;
        game_update_queries($game, $attempt, $query, 0, $useranswer); // Last param is grade.
    } else {
        // Correct answer.
        $correct = true;

        game_update_queries($game, $attempt, $query, 1, $useranswer); // Last param is grade.
    }

    // Set the grade of the whole game.
    game_snakes_position($cm, $game, $attempt, $snakes, $correct, $query, $context, $course);
}

/**
 * Computes the position.
 *
 * @param stdClass $cm
 * @param stdClass $game
 * @param stdClass $attempt
 * @param stdClass $snakes
 * @param boolean $correct
 * @param stdClass $query
 * @param stdClass $context
 * @param stdClass $course
 * @throws coding_exception
 * @throws dml_exception
 * @throws moodle_exception
 * @package mod_game
 *
 */
function game_snakes_position($cm, $game, $attempt, $snakes, $correct, $query, $context, $course) {
    global $DB;

    $data = $DB->get_field('game_snakes_database', 'data', [ 'id' => $snakes->snakesdatabaseid]);

    if ($correct) {
        if (($next = game_snakes_foundlander($snakes->position + $snakes->dice, $data))) {
            $snakes->position = $next;
        } else {
            $snakes->position = $snakes->position + $snakes->dice;
        }
    } else {
        if (($next = game_snakes_foundsnake($snakes->position, $data))) {
            $snakes->position = $next;
        }
    }

    $updrec = new stdClass();
    $updrec->id = $snakes->id;
    $updrec->position = $snakes->position;
    $updrec->queryid = 0;

    if (!$DB->update_record('game_snakes', $updrec)) {
        throw new moodle_exception('snakes_error', 'game', 'game_snakes_position: Can\'t update game_snakes');
    }

    $board = $DB->get_record_select('game_snakes_database', "id=$snakes->snakesdatabaseid");
    $gradeattempt = $snakes->position / ($board->usedcols * $board->usedrows);
    $finished = ($snakes->position > $board->usedcols * $board->usedrows ? 1 : 0);

    game_updateattempts($game, $attempt, $gradeattempt, $finished, $cm, $course);

    game_snakes_computenextquestion($game, $snakes, $query);

    game_snakes_play($cm, $game, $attempt, $snakes, $context, $course);
}

/**
 * In lander go forward.
 *
 * @package mod_game
 *
 * @param int $position
 * @param string $data
 */
function game_snakes_foundlander($position, $data) {
    preg_match("/L$position-([0-9]*)/", $data, $matches);

    if (count($matches)) {
        return $matches[1];
    }

    return 0;
}

/**
 * In snake go backward.
 *
 * @package mod_game
 *
 * @param int $position
 * @param string $data
 */
function game_snakes_foundsnake($position, $data) {
    preg_match("/S([0-9]*)-$position,/", $data . ',', $matches);

    if (count($matches)) {
        return $matches[1];
    }

    return 0;
}

/**
 * Removes attempt data.
 *
 * @param int $questionusageid
 * @param int $questionid
 * @throws dml_exception
 * @package mod_game
 *
 */
function game_snakes_remove_attemptdata($questionusageid, $questionid) {
    global $DB;

    $sql = "SELECT qas.id
              FROM mdl_question_attempts qa
         LEFT JOIN mdl_question_attempt_steps qas
                ON qa.id=qas.questionattemptid
             WHERE questionusageid = $questionusageid
               AND questionid = $questionid
               AND state != 'todo'";

    if ($stepdata = $DB->get_records_sql($sql)) {
        foreach ($stepdata as $step) {
            if ($step->id > 0) {
                $DB->delete_records('question_attempt_step_data', ['attemptstepid' => $step->id]);
                $DB->get_records_sql("update {question_attempt_steps} set state='todo' where id = {$step->id}");
            }
        }
    }
}
