
/**
 * This function contains all code related to the pad animation diagram. It is called when the chart is initiated in theme.js
 * @param {*} sequencectx 
 * @param {*} sequenceChart 
 */
function initPadDiagram(sequencectx, sequenceChart){

    this.padArray = []; // Holds the pad objects for the ongoing game
    var state = 0; // Maintains the current state, meaning if state is different at any point in the animation, animation should stop
    var offset = 0; // Starting point for the animation, ex. if paused on 4th pad, when played offset would be set to 4 and anim starts at 4
    var redOrGreen = false; // Keeps track if the anim was paused when glow was red or green rather than yellow (need this to skip yellow phase on resume)
    var pausedInMiddle = false; // Don't think this is used lol
    
    const c = $('#myCanvas');
    const ctx = c[0].getContext('2d');
    var game = null;

    /** Sets up what happens when any bar on the chart is clicked */
    sequenceChart.options.onClick = function(c, i){
        state++;
        offset = i[0]._index;
        redOrGreen = false;
        playSequence();
    }

    /** These are the gradients used on the bar chart. More can easily by added */
    var background_1 = sequencectx.createLinearGradient(0, 300, 0, 0);
    background_1.addColorStop(0, "rgba(0, 191, 255, 0)");
    background_1.addColorStop(1, "rgba(0, 191, 255, 0.5)");

    var background_2 = sequencectx.createLinearGradient(0, 300, 0, 0);
    background_2.addColorStop(0, "rgba(210, 105, 30, 0)");
    background_2.addColorStop(1, "rgba(210, 105, 30, 0.5)");

    var background_3 = sequencectx.createLinearGradient(0, 700, 0, 0);
    background_3.addColorStop(0, "rgba(34, 139, 34, 0)");
    background_3.addColorStop(1, "rgba(34, 139, 34, 0.5)");

    var background_4 = sequencectx.createLinearGradient(0, 500, 0, 0);
    background_4.addColorStop(0, "rgba(147, 112, 219, 0)");
    background_4.addColorStop(1, "rgba(147, 112, 219, 0.5)");

    var background_5 = sequencectx.createLinearGradient(0, 500, 0, 0);
    background_5.addColorStop(0, "rgba(255, 182, 193, 0)"); //this color doesn't exactly match PaleVioletRed but it better differentiates from Chocolate
    background_5.addColorStop(1, "rgba(255, 182, 193, 0.5)");

    var barColors = [background_1, background_2, background_3, background_4, background_5];

    /* These 3 functions below handle the drop down menus on the game sequence chart. They appropriately
    handle what should be done whenever one of the drop downs is changed */
    $('.session-select').change(function(){
        var newSession = $(this).children("option:selected").val().toString();
        var gamesDropDown = $(".game-select");
        gamesDropDown.children(".dropdown-option").remove();
        $('.pad-select').prop("selectedIndex", 0);

        var data = sequenceChart.config.data;
        if(newSession == "All"){
        clearCanvas(true);
        data.datasets[0].data = [];
        data.labels = [];
        $('.spec-val').each(function(i, spec) {
            spec.innerHTML = "";
        });
        }
        else{
        for(var i = 0; i < allGames[newSession]["dates"].length; i++){
            gamesDropDown.append('<option class="dropdown-option" value="' + allGames[newSession]["dates"][i] + '">' + allGames[newSession]["dates"][i] + '</option>');
        }
        }
        sequenceChart.update();
    });

    $('.game-select').change(function(){
        var newGame = $(this).children("option:selected").val().toString();
        let selectedSession = $('.session-select').children("option:selected").val().toString();
        $('.pad-select').prop("selectedIndex", 0);

        var data = sequenceChart.config.data;
        if(newGame == "All"){
        clearCanvas(true);
        data.datasets[0].data = [];
        data.labels = [];
        $('.spec-val').each(function(i, spec) {
            spec.innerHTML = "";
        });
        }
        else{
        data.datasets[0].data = allGames[selectedSession]["games"][newGame]["time"];
        const map = allGames[selectedSession]["games"][newGame]["pad"].map(x => barColors[parseInt(x) - 1]);
        data.datasets[0].backgroundColor = map;
        //data.datasets[1].data = allGames[selectedSession]["games"][newGame]["miss"];
        data.labels = allGames[selectedSession]["games"][newGame]["pad"];
        state++;
        offset = 0;
        redOrGreen = false;

        var specVals = $('.spec-val');
        $('.spec-attr').each(function(i, spec) {
            specVals[i].innerHTML = allGames[selectedSession]["games"][newGame]["specs"][$(spec).data("spec")];
        });
        createCanvas();
        }
        sequenceChart.update();
    });

    $('.pad-select').change(function(){
        var newPad = $(this).children("option:selected").val().toString();
        let selectedSession = $('.session-select').children("option:selected").val().toString();
        let selectedGame = $('.game-select').children("option:selected").val().toString();
        var data = sequenceChart.config.data;
        if(newPad == "All"){
        clearCanvas(true);
        data.datasets[0].data = allGames[selectedSession]["games"][selectedGame]["time"];
        const map = allGames[selectedSession]["games"][selectedGame]["pad"].map(x => barColors[parseInt(x) - 1]);
        data.datasets[0].backgroundColor = map;
        data.labels = allGames[selectedSession]["games"][selectedGame]["pad"];
        }
        else{
        var filteredData = [];
        var newLabels = [];
        //use complete data instead of preexisting chart data
        allGames[selectedSession]["games"][selectedGame]["time"].forEach(function(x, index){
            if(allGames[selectedSession]["games"][selectedGame]["pad"][index] === newPad){
            filteredData.push(x);
            newLabels.push(newPad);
            }
        });
        data.datasets[0].data = filteredData;
        data.datasets[0].backgroundColor = barColors[parseInt(newPad) - 1];
        data.labels = newLabels;
        }
        sequenceChart.update();
    });

    /** Class to create Pad objects with. Contains all data needed to appropriately display a pad on the canvas */
    class Pad {
        constructor(name, x, y, rot, padColor, barColor) {
        this.name = name;
        this.x = x;
        this.y = y;
        this.rot = rot;
        this.color = padColor;
        this.glow = false;
        this.glow_color = "yellow";
        this.bar_color = barColor;
        }
    }
    
    /** Creates a brand new pad array based off current game data. Used for new games/resets */
    function createNewPadArray(game){
        //Gets the pad arrangement for selected game and parses the pad arrangement string to find and place pads on the canvas.
        this.padArray = [];
    
        const padColors = ["DeepSkyBlue", "Chocolate", "ForestGreen", "MediumPurple", "PaleVioletRed"]
        let curPad = 0;
        let split = game["specs"]["setup"].split(";");
        split.forEach(function(pad) {
            let data = pad.split(",");
            let name = data[0];
            let x = data[1];
            let y = data[2];
            let rot = data[3];
            let padColor = padColors[curPad];
            let barColor = barColors[curPad++];
            this.padArray.push(new Pad(name, x, y, rot, padColor, barColor));
        });

        var data = sequenceChart.config.data;
        data.datasets[0].backgroundColor.forEach(function(color, i){
        // Makes sure to reset bar colors if glow was on.
        if(color === "yellow" || color === "lime" || color === "red"){
            data.datasets[0].backgroundColor[i] = padArray[parseInt(game["pad"][i]) - 1].bar_color;
            sequenceChart.update();
        }
        });
        
        console.log(this.padArray);
    }

    /** This is the function that is called to actually draw on the canvas. Loops through the
     * pad array, creating a base rectangle and a color rectangle for each pad along with the proper text.
    */
    function draw(ctx, skipClear){
        if(!skipClear){
            clearCanvas(false);
        }

        let resizeRatio = ctx.canvas.width / 600; 
        let width = 75 * resizeRatio;
        let height = 15 * resizeRatio;
        
        this.padArray.forEach(function(pad){
            let name = pad.name;
            let x = ctx.canvas.width * pad.x;
            let y = ctx.canvas.height * pad.y;
            let rotDeg = pad.rot;
            let rotRad = rotDeg * Math.PI / 180;
            let color = pad.color;
            let glow_color = pad.glow_color;
            let glow = pad.glow;
            
            ctx.beginPath();
            ctx.arc(x + width/2, y + height/2, 50, 0, 2 * Math.PI);
            if(glow){
                ctx.fillStyle = glow_color;
            }
            else{
                ctx.fillStyle = "#d4d4d4";
            }
            ctx.fill();

            ctx.save();

            ctx.beginPath();
            ctx.translate(x + width / 2, y + height / 2);
            ctx.rotate(rotRad);
            
            ctx.fillStyle = color;
            ctx.fillRect(-width / 2, -height / 2, width, height);

            ctx.restore();

            ctx.save();

            ctx.beginPath();
            ctx.translate(x + width / 2, y + height / 2);
            ctx.rotate(rotRad);
            ctx.fillStyle = "black";
            ctx.fillRect(-width / 2, -height / 2, width, height / 3);

            ctx.restore();

            ctx.font = "30px Arial";
            ctx.fillStyle = color;
            ctx.strokeStyle = "black";
            
            ctx.fillText(name, (x + width / 4) + 40 * Math.sin(-rotRad), (y + height / 2) + 40 * Math.cos(rotRad));
            ctx.strokeText(name, (x + width / 4) + 40 * Math.sin(-rotRad), (y + height / 2) + 40 * Math.cos(rotRad));
        });
    }

    /** This function is what creates the glow effect for a pad when called. Changes to the appropriate glow
     * color and then sets up a delay to turn the glow off. Checks with state to make sure the animation wasn't
     * paused during the delay.
     */
    async function togglePadGlow(ctx, index, curTime, curPad, savedState){
        activePad = curPad;
        if(curTime != null){
            this.padArray[curPad].glow_color = "lime";
        }
        else{
            this.padArray[curPad].glow_color = "red";
        }
        redOrGreen = true;
        sequenceChart.config.data.datasets[0].backgroundColor[index] = "lime";
        sequenceChart.update();
        this.padArray[curPad].glow = true;
        draw(ctx);

        setTimeout(function() {
        if(savedState == state){
            offset++;
            redOrGreen = false;
            this.padArray[curPad].glow_color = "yellow";
            sequenceChart.config.data.datasets[0].backgroundColor[index] = this.padArray[curPad].bar_color;
            sequenceChart.update();
            this.padArray[curPad].glow = false;
            draw(ctx);
        }
        }, 1500); //this number determines how long the pad should stay "glowing", can be tweaked
    }

    /** This is the main function that actually runs the animation. It runs the animation for the current pad, checks with state
     * to make sure the game wasn't changed/restarted, then recursively calls itself with the next part of the sequence until the
     * sequence is over.
     */
    function nextPad(ctx, sequence, index, gameDelay, waitTime, pads, savedState, skipInitialYellow){
        if(index >= sequence.length){
            offset = 0;
            return;
        }

        setTimeout(function(){
        if(savedState != state){
            return;
        }
        offset = index;
        let curPad = parseInt(pads[index]) - 1;
        let responseTime = sequence[index] || 2000;

        if(!skipInitialYellow){
            this.padArray[curPad].glow = true;
            sequenceChart.config.data.datasets[0].backgroundColor[index] = "yellow";
            sequenceChart.update();
            draw(ctx);
        }
        else{
            responseTime = 0;
        }

        setTimeout(function(){
            if(savedState != state){
                return;
            }
            togglePadGlow(ctx, index, sequence[index], curPad, savedState);
            if(sequence[index] != null){
                nextPad(ctx, sequence, index + 1, gameDelay, gameDelay - sequence[index], pads, savedState, false);
            }
            else{
                nextPad(ctx, sequence, index + 1, gameDelay, gameDelay - 2000, pads, savedState, false);
            }
        }, responseTime);
        }, waitTime);
    }
    
    /**
     * Sets up for playing the animation and initiates it.
     */
    async function playSequence(){
        playButton.text("Pause") 
        let selectedSession = $('.session-select').children("option:selected").val().toString();
        let selectedGame = $('.game-select').children("option:selected").val().toString();
        game = allGames[selectedSession]["games"][selectedGame];

        let preOffset = offset;
        state++;
        let savedState = state;
        resetColors();
        let pads = game["pad"]
        let sequence = game["time"];
        let delay = game["specs"]["delay_time"];
        let totalTime = 0;

        let curPad = parseInt(pads[offset]) - 1;
        if(redOrGreen){
            nextPad(ctx, sequence, offset, delay * 1000, 0, pads, savedState, true);
        }
            else{
            nextPad(ctx, sequence, offset, delay * 1000, 0, pads, savedState, false);
        }
    }

    /** 
     * Resets all colors on pads and in bar chart then redraws the canvas. Used if need a fresh redraw
     */
    function resetColors(){
        createNewPadArray(game);
        draw(ctx);
    }

    /** 
     * Clears everything off of the canvas, essentially an absolute clear. Optional text can be set to show.
     */
    function clearCanvas(addText){
        var c = $('#myCanvas');
        var ctx = c[0].getContext('2d');
        
        ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
        ctx.fillStyle = "#d4d4d4";
        ctx.fillRect(0,0,600,600);

        if(addText){
            ctx.font = "37px Arial";
            ctx.fillStyle = "black";
            ctx.fillText("Please select a session and game!", 0, 285);
        }
    }

    /** 
     * This function crates the initial (empty) canvas on the dashboard
     */
    function createCanvas(){
        //var lightbox = lity('<canvas id="myCanvas" height= style="border:1px solid #124543;"></canvas>');
        clearCanvas(false);
        let selectedSession = $('.session-select').children("option:selected").val().toString();
        let selectedGame = $('.game-select').children("option:selected").val().toString();
        const game = allGames[selectedSession]["games"][selectedGame];

        var c = $('#myCanvas');
        var ctx = c[0].getContext('2d');

        ctx.fillStyle = "#d4d4d4";
        ctx.fillRect(0,0,600,600);

        // Creates a circle in the center of the image so that the standing point of the player can be visualized.
        ctx.beginPath();
        ctx.arc(ctx.canvas.width * 0.5, ctx.canvas.height * 0.5, 6, 0, 2 * Math.PI);
        ctx.strokeStyle = "green";
        ctx.stroke();

        createNewPadArray(game);
        draw(ctx);
    }

    /**
     * Resets variables to original values and initiates the play sequence functions
     * @param {} startFresh 
     */
    function preparePlay(startFresh){
        if(startFresh){
            offset = 0;
            redOrGreen = false;
        }
        playSequence();
    }
    
    /**
     * These just handle what should happen when the play/pause and restart buttons are clicked respectively.
     */
    var playButton = $('#control-play');
    playButton.click(function(){
        if(playButton.text() === "Play"){
        preparePlay(false);
        }
        else{
        playButton.text("Play"); 
        state++;
        }
    });

    $('#control-restart').click(function(){
        resetColors();
        preparePlay(true);
    });

    clearCanvas(true); // Initial clear canvas to set everything up properly
}