angular.module('beamng.apps')
.directive('scenarioEndScreen', [function () {
  return {
    templateUrl: '../../modules/apps/ScenarioEndScreen/templateBMNG.html',
    replace: true,
    link: function (scope) {
      'use strict';

      function resetValues () {
        scope.$evalAsync(function () {
          const element = document.getElementById("scInfo");
          if (element) element.style.display = "none";
        });
      }

      scope.$on('gravitationalRacingScenarioEndScreen', function (event, data) {
        /*
        *Adds extra zeros onto the front of a number until it is n digits long
        */
        function format(value, numDigits) {
          let strVal = value.toString();
          let digits = strVal.length;

          if (digits < numDigits) {
            for (let i = digits; i < numDigits; i++) {
              strVal = "0" + strVal;
            }
          }

          return strVal;
        }

        /*
        * Loads a given scenario
        */
        function loadScenario(filePath) {
          filePath = bngApi.serializeToLua(filePath);
          let cmd = "scenario_scenariosLoader.start(scenario_scenariosLoader.loadScenario(" + filePath + ", nil, " + filePath + "))";
          bngApi.engineLua(cmd);
        }

        function loadHubWorld() {
          loadScenario("/levels/smallgrid/scenarios/gravitationalRacing/hubworld.json");
        }

        /*
        *Restarts the current scenario
        */
        function restartScenario() {
          bngApi.engineLua("scenario_scenarios.restartScenario()");
        }

        /*
        Shows the buttons
        */
        function showButtons() {
          let buttons = document.getElementById("chButtons");
          buttons.style.display = "flex";

          //Championships cannot have a round restarted after the end screen
          if (champName && !champFinished) {
              buttons.innerHTML += "<li class='button' id='ch_" + nextScenario.name + "' style='border:2px solid " + nextScenario.colour + "'>NEXT</li>";
              let filePathNext = nextScenario.filePath;
              document.getElementById(nextScenario.name).addEventListener("click", function() {loadScenario(filePathNext)}, false);
          }
        }

        /*
        Shows the unlocks
        */
        function showUnlocks() {
          let unlocks = document.getElementById("unlocks");

          let div = "<div>";

          for (let i = 0; i < 3; i++) {
            if (unlocked[i]) {
              let scenario = unlocked[i].name;
              let difficulty = unlocked[i].difficulty;

              div += "<span style='color:" + difficultyToColour(difficulty.replace(/["'\(\)]/g, "")) + "'>NEW TRACK UNLOCKED: " + scenario.toUpperCase() + " (" + difficulty.toUpperCase() + ")</span><br>";
            } else {
              //Add empty line
              div += "<br>";
            }
          }

          div += "</div>";
          unlocks.innerHTML = div;

          showButtons();

          shownUnlocks = true;
        }

        /*
        *Compares the best, set and goal times
        */
        function compare(tSet, tGoal, tBest, alreadyAchieved, elementID) {
          let anim;

          if (tSet <= tGoal) {
            document.getElementById(elementID).style.color = "lightgreen";

            if (!alreadyAchieved) {
              let medalID = elementID.replace("Data", "Medal");
              let medalObj = document.getElementById(medalID);
              medalObj.style.opacity = "1";
              medalObj.style.animation = "fadeScale 2.5s, shimmer 2s infinite forwards";
            }

            anim = "shimmerGreenPurple";
          } else {
            document.getElementById(elementID).style.color = "red";
            anim = "shimmerRedPurple";
          }

          //If tBest is not set (ie. new save fie / scenario not tried yet), the values could be 0 (time) or -1 (resets)
          if (tSet < tBest || (elementID.search("time") === 0 && tBest === 0) || (elementID.search("resets") === 0 && tBest === -1)) {
            //Display a purple shimmer effects
            let text = document.getElementById(elementID);
            text.style.color = "purple";
            text.style.animation = anim + " 2s infinite forwards";
            document.getElementById(elementID.replace("Data", "Title")).innerHTML += "<span class='pb'>(PB)</span>";
          }

          if (!shownUnlocks) {
            showUnlocks();
          }
        }

        /*
        *toggles the UI from view
        */
        function toggleUI() {
          let info = champName ? document.getElementById("chInfo") : document.getElementById("scInfo");
          let button = document.getElementById("toggle");

          if (info.style.visibility === "visible") {
            info.style.visibility = "hidden";

            button.innerHTML = "&nbsp;show&nbsp;";
            button.className = "show";
          } else {
            info.style.visibility = "visible";

            button.innerHTML = "&nbsp;hide&nbsp;";
            button.className = "hide";
          }
        }

        /*
        *Animates the data graphic to show the value gotten and the target
        *If the value achieved is better than the target, it turns the value green,
        *else turns the value red (to indicate not achieved)
        *@param targetValueFormatted - optional (only applies to times)
        */
        function animateGraphic(achievedTime, targetValue, currentBest, type, elementID, targetValueFormatted){
          //The total time it should take to animate the timer
          const REAL_TIME = 3;

          let incrementMultiplier = 0;
          let t = 0;
          let currentValue = 0;
          let pendEnd = false;

          let timer = setInterval(function() {
            //Since this function updates every 10ms
            t += 1/100;

            //f(t) = (t/K - 1)³ + 1
            incrementMultiplier = Math.pow((t/REAL_TIME - 1), 3) + 1;

            //Account for the x³ approaching 1 unlikely to ever be 1 exactly
            if (Math.abs(incrementMultiplier-1) <= Math.pow(10, -6)) {
              incrementMultiplier = 1;
              pendEnd = true;
            }

            currentValue = achievedTime*incrementMultiplier;

            if (type === "time") {
              //Format for time
              let milliseconds = format(Math.floor(currentValue) % 1000, 3);
              let seconds      = format(Math.floor((currentValue / 1000) % 60), 2);
              let minutes      = Math.floor(currentValue / 1000 / 60).toString();

              document.getElementById(elementID).innerHTML = minutes + ":" + seconds + "." + milliseconds + "/" + targetValueFormatted;
            } else if (type === "resets") {
              document.getElementById(elementID).innerHTML = parseInt(currentValue) + "/" + targetValue;
            }

            if (pendEnd) {
              clearInterval(timer);
              compare(currentValue, targetValue, currentBest, achievedMedalsPrev[type], elementID);
            }
          }, 10);
        }

        function displayResults(){
          document.getElementById("scInfo").removeEventListener("animationend", displayResults);

          bngApi.engineLua('Engine.Audio.playOnce("AudioGui", "event:>UI>Scenario End Counting")')

          animateGraphic(totalTime, timeToBeat, bestTime, "time", "timeData", timeToBeatFormatted);
          animateGraphic(resetsUsed, resetsToBeat, bestResets, "resets", "resetsData");

          document.getElementById("toggle").addEventListener("click", toggleUI, false);
        }

        function findDriverPosition(driver, standings) {
          for (let i = 0; i < standings.length; i++) {
            if (standings[i].driver.toLowerCase() === driver.toLowerCase()) {
              return i;
            }
          }

          console.error("No driver named: ", driver, " in standings ", standings);
        }

        function positionChangeToString(changes) {
          if      (changes === 0) return "-";
          else if (changes < 0)   return "<span style='color:rgb(255, 0, 0)'>" + "v".repeat(Math.abs(changes)) + "</span>";
          else                    return "<span style='color:rgb(0, 255, 0)'>" + "^".repeat(changes) + "</span>";
        }

        function showMedals(tableRows) {
          return new Promise(function(resolve) {
            //Resolve early since it doesn't need to do anything
            if (!champFinished) {
              resolve(tableRows);
              return
            }

            let index = 0;

            let timer = setInterval(function() {
              let colour;

              //Show colours for final standings (diamond, gold, silver, bronze)
              switch(index) {
                case 0:
                  if (whiteWash) colour = "rgba(185, 242, 255, 0.5)";
                  else           colour = "rgba(255, 215, 0, 0.5)";
                  break;
                case 1:
                  colour = "rgba(192, 192, 192, 0.5)";
                  break;
                case 2:
                  colour = "rgba(255, 126, 0, 0.5)";
                  break;
                default:
                  colour = "";
                  break;
              }

              let row = tableRows[index+1];

              if (colour !== "") {
                row.style.backgroundColor = colour;
                row.style.animation = "fadeIn 1s linear forwards";
              }

              if (roundStandings[index].driver.toLowerCase() === "player") {
                row.style.animation += ", shimmer 2s infinite forwards";
              }

              index++;

              if (index >= roundStandings.length) {
                clearInterval(timer);
                resolve(tableRows);
              }
            }, 250);
          });
        }

        function showOverallDif(tableRows) {
          return new Promise(function(resolve) {
            let index = 0;

            let timer = setInterval(function() {
              let dif = roundStandings[index].dif;
              tableRows[index+1].cells[3].innerHTML = dif !== "-" ? "+" + dif : dif;

              index++;

              if (index >= roundStandings.length) {
                clearInterval(timer);
                resolve(tableRows);
              }
            }, 250);
          });
        }

        function adjustRows(tableRows) {
          return new Promise(function(resolve) {
            for (let i = 0; i < newStandingsOrdered.length; i++) {
              let currentDriver = newStandings[i].driver;
              let newDriver = newStandingsOrdered[i].driver;
              let currentRow = i+1;

              //If a driver has been promoted or demoted in the standings
              if (currentDriver !== newDriver) {
                let newRow = findDriverPosition(currentDriver, newStandingsOrdered) + 1;

                //Will be negative for demotions
                let positionChange = currentRow - newRow

                let newTableRow = tableRows[newRow];
                let tableCells = newTableRow.cells;
                //Overwrite old data with new
                tableCells[0].innerHTML = currentDriver;
                tableCells[1].innerHTML = positionChangeToString(positionChange);
                tableCells[2].innerHTML = newStandings[i].total;

                if (currentDriver.toLowerCase() === "player") {
                  //Reset previous table row
                  tableRows[currentRow].style.backgroundColor = "";
                  //Update new table row
                  newTableRow.style.backgroundColor = "rgba(128, 128, 128, 0.5)";
                }
              } else {
                tableRows[currentRow].cells[1].innerHTML = positionChangeToString(0);
              }
            }

            setTimeout(function() {
              resolve(tableRows);
            }, 1000);
          });
        }

        //Adds the scenario result to the new
        function addTotalStandings(tableRows) {
          return new Promise(function(resolve) {
            let index = 0;

            let timer = setInterval(function() {
              tableRows[index+1].cells[2].innerHTML = "<span style='animation:glowWhite 0.5s linear forwards;'>" + newStandings[index].total + "</span>";

              index++;

              if (index >= roundStandings.length) {
                clearInterval(timer);

                setTimeout(function() {
                  resolve(tableRows);
                }, rowTotalTime - 1500);
              }
            }, 250);
          });
        }

        function showPreviousStandings(tableRows) {
          return new Promise(function(resolve) {
            let index = 0;

            let headerRowCells = tableRows[0].cells;
            //Fade to add "total" in headers
            headerRowCells[2].animation = "fadeIn 2s linear forwards";
            headerRowCells[3].animation = "dadeIn 2s linear forwards";

            headerRowCells[2].innerHTML = "TOTAL " + headerRowCells[2].innerHTML
            headerRowCells[3].innerHTML = "TOTAL " + headerRowCells[3].innerHTML

            let timer = setInterval(function() {
              let current = previousStandings[index];
              let driverName = current.driver.toUpperCase();
              let total = current.total;

              let row = tableRows[index+1];
              let cells = row.cells;

              //Update the row's cells
              cells[0].innerHTML = "<span style='animation:fadeIn 1s linear forwards'>" + driverName + "</span>";
              cells[2].innerHTML = "<span style='animation:fadeIn 1s linear forwards'>" + total + "</span>";

              if (driverName.toLowerCase() === "player") {
                row.style.backgroundColor = "rgba(128, 128, 128, 0.5)";
              }

              index++;

              if (index >= previousStandings.length) {
                clearInterval(timer);

                setTimeout(function() {
                  resolve(tableRows);
                }, rowTotalTime);
              }
            }, 250);
          });
        }

        //Fades out the scenario results
        function fadeOutScenarioResults(tableRows) {
          return new Promise(function(resolve) {
            for (let i = 0; i < roundStandings.length; i++) {
              tableRows[i+1].style.animation = "fadeOut 2s linear forwards";
            }

            //Fade to add "total" in headers
            tableRows[0].cells[2].animation = "fadeOut 2s linear forwards";
            tableRows[0].cells[3].animation = "fadeOut 2s linear forwards";

            //Pause before continuing
            setTimeout(function() {
              for (let i = 0; i < roundStandings.length; i++) {

                let row = tableRows[i+1];
                //Reset animation
                row.style.animation = "";
                row.style.backgroundColor = "";

                //Reset cells
                let cells = row.cells;
                for (let j = 0; j < 4; j++) {
                  //Skip position-change column
                  if (j === 1) continue;

                  cells[j].innerHTML = "&nbsp";
                }
              }

              resolve(tableRows);
            }, 2000);
          });
        }

        //Shows the differential between results in this scenario
        function showScenarioDif(tableRows) {
          return new Promise(function(resolve) {
            let index = 0;

            let timer = setInterval(function() {
              //Fade in the differentials
              let dif = roundStandings[index].dif;
              let formattedDif = dif !== "-" ? "+" + dif : dif;
              // +1 as first row is header
              tableRows[index+1].cells[3].innerHTML = "<span style='animation:fadeIn 1s linear forwards;'>" + formattedDif + "</span>";

              index++;

              if (index >= roundStandings.length) {
                clearInterval(timer);
                //Pause before continuing
                setTimeout(function() {
                  resolve(tableRows);
                }, 3000);
              }
            }, 250);
          });
        }

        //Shows the driver results
        function showScenarioResults() {
          return new Promise(function(resolve) {
            let index = 0;
            let tableRows = document.getElementById("champTable").rows;

            let timer = setInterval(function() {
              let current = roundStandings[index];
              let driverName = current.driver.toUpperCase();
              let result = current.result;

              let row = tableRows[index+1];
              let cells = row.cells;

              if (driverName.toLowerCase() === "player") {
                row.style.backgroundColor = "rgba(128, 128, 128, 0.5)";
              }

              row.style.animation = "fadeIn 1s linear forwards";

              //Update the row's cells
              cells[0].innerHTML = driverName;
              cells[2].innerHTML = result;

              index++;

              if (index >= roundStandings.length) {
                clearInterval(timer);

                setTimeout(function() {
                  resolve(tableRows);
                }, rowTotalTime);
              }
            }, 250);
          });
        }

        function displayDriverResults() {
          document.getElementById("chInfo").removeEventListener("animationend", displayDriverResults);
          document.getElementById("toggle").addEventListener("click", toggleUI, false);

          showScenarioResults()
          .then(showScenarioDif)
          .then(fadeOutScenarioResults)
          .then(showPreviousStandings)
          .then(addTotalStandings)
          .then(adjustRows)
          .then(showOverallDif)
          .then(showMedals)
          .then(showButtons);
        }

        function setupChampionshipScreen() {
          setupScreen("ch");
          //Add in the championship specific details to the results div
          document.getElementById("championshipName").innerHTML = "<b>" + champName + " <span style='color:" + colour + ";'></span></b>";

          document.getElementById("ch_hubWorld").addEventListener("click", loadHubWorld, false);

          let table = document.getElementById("champTable");

          let tableHeader = table.rows[0].cells;
          //Change headers accordingly
          tableHeader[2].innerHTML = scoringType === "times" ? "TIME" : "RESETS";

          let difHeader = tableHeader[3];
          difHeader.innerHTML = "<b>" + (scoringType === "times" ? "TIME DIF." : "RESET DIF.") + "</b>";
          difHeader.className = "chHeader";

          //Add each row at the top
          for (let i = 0; i < roundStandings.length; i++) {
            let newRow = table.insertRow(-1);
            newRow.id = "row_" + i;
            newRow.className = "row";

            //Add in the cells to the row
            let classes = ["chTitle", "chPosChange", "chValue", "chDif"];
            for (let j = 0; j < classes.length; j++) {
              let cell = newRow.insertCell(-1);
              cell.innerHTML = "&nbsp";
              cell.className = classes[j];
            }
          }
        }

        function setupScenarioScreen() {
          setupScreen("sc");

          document.getElementById("sc_hubWorld").addEventListener("click", loadHubWorld, false);
          document.getElementById("sc_retry").addEventListener("click", restartScenario, false);

          //Add in the scenario specific details to the results div
          document.getElementById("scenarioName").innerHTML = "<b>" + scenarioName + " <span style='color:" + colour + ";'>(" + difficulty + ")</span></b>";
          document.getElementById("unlocksHeading").style["background-color"] = colour;

          //Add in specific data to beat
          document.getElementById("timeData").innerHTML += timeToBeatFormatted;
          document.getElementById("resetsData").innerHTML += resetsToBeat;

          //Show already achieved medals
          document.getElementById("timeMedal")  .style.opacity = achievedMedalsPrev.time   ? "1" : "0.25";
          document.getElementById("resetsMedal").style.opacity = achievedMedalsPrev.resets ? "1" : "0.25";
        }

        function setupScreen(prefix) {
          document.getElementById(prefix + "ResultsHeading").style["background-color"] = colour;

          //Animate finish div
          let finish = document.getElementById("finish");
          finish.style.display = "block";
          finish.style.animation = "slideDownOut 3s linear forwards";

          //Animate results div after the finish div
          let info = document.getElementById(prefix + "Info");
          info.style.display = "block";
          info.style.animation = "slideDownPause 1.75s linear 2.5s forwards";

          //Add event listener to call the function for animating the results after the animation has finished
          if (prefix === "ch") {
            info.addEventListener("animationend", displayDriverResults, false);
          } else if (prefix === "sc") {
            info.addEventListener("animationend", displayResults, false);
          }

          //Animate the button to do with the results div
          let button = document.getElementById("toggle");
          button.style.display = "block";
          button.style.animation = "slideDownPause 1.75s linear 2.5s forwards";
        }

        /*
        Returns the colour of the difficulty level
        */
        function difficultyToColour(difficulty) {
          difficulty = difficulty.toUpperCase()
          if (difficulty === "TUTORIAL") {
            return "white";
          } else if (difficulty === "BASIC") {
            return "lightgreen";
          } else if (difficulty === "ADVANCED") {
            return "gold";
          } else if (difficulty === "EXPERT") {
            return "red";
          } else if (difficulty === "INSANE") {
            return "dodgerblue";
          }
        }

        //General information
        let scenarioName = data.scenarioName;
        let colour = data.difficulty.colour;

        // Only defined for scenarios
        let difficulty = null, unlocked = null, shownUnlocks = null, achievedMedalsPrev = null, totalTime = null, bestTime = null, timeToBeat = null, timeToBeatFormatted = null, resetsUsed = null, bestResets = null, resetsToBeat = null
        // Only defined for championships
        let rowTotalTime = null, roundStandings = null, previousStandings = null, newStandings = null, newStandingsOrdered = null, champFinished = null, whiteWash = null, scoringType = null, nextScenario = null;

        let champName = data.champName;

        //Specified only in championships
        if (champName) {
          champFinished = data.champFinished;
          nextScenario = data.nextScenario;
          scoringType = data.champScoringType;
          whiteWash = data.whiteWash;
          roundStandings = data.roundStandings;
          previousStandings = data.previousStandings;
          newStandings = data.newStandings;
          newStandingsOrdered = data.newStandingsOrdered;

          /*The total time the rows will take to show
          (total row time + the last row with fade time of 1s)*/
          rowTotalTime = 250*roundStandings.length + 1000

          setupChampionshipScreen();
        } else {
          difficulty = data.difficulty.type;
          unlocked = data.unlocks;
          shownUnlocks = false;

          achievedMedalsPrev = data.medalsGottenAlready;

          totalTime           = data.times.set;
          bestTime            = data.times.best;
          timeToBeat          = data.times.target;
          timeToBeatFormatted = data.times.targetFormatted;

          resetsUsed   = data.resets.set;
          bestResets   = data.resets.best;
          resetsToBeat = data.resets.target;

          setupScenarioScreen();
        }
      });

      scope.$on('ScenarioResetTimer', resetValues);
    }
  };
}]);
