var COMPETITION_PROBLEMS; // Queue of problems for this competition
var USER_CURRENT_PROBLEM; // Index of current problem in COMPETITION_PROBLEMS
var OPPONENT_CURRENT_PROBLEM; // Index of opponent's problem in COMPETITION_PROBLEMS
var USER_COMPETITION_POINTS; // Number of points the user has in this competition
var OPPONENT_COMPETITION_POINTS; // Number fof points opponent has in this competition
var MAX_REMATCH_TIMEOUT = 1; // Maximum number of seconds to wait for rematch request
var PREGAME_TIMER = 3; // Seconds to count down before starting the game
var GAME_TIMER = 30; // Seconds to put on the countdown timer
var WIN_BONUS = 100; // Bonus you get for winning
var RELOAD_INPUT_INTERVAL = 1; // Seconds interval between refreshing input
var COMPETITION_FINISHED;
var DONE_PROCESSING_OPPONENT_INPUT;
var OPPONENT_INPUT;
var OPPONENT_INPUT_INDEX; // The current location of OPPONENT_INPUT array
var PROCESS_OPPONENT_INPUT_INTERVAL = 0.1; // Seconds interval between processing opponent input
var SYNCHRONIZING_INTERVAL = 0.1; // Seconds interval when synchronizing
var SYNCHRONIZING_MAX_WAIT = 5; // Max seconds to wait to synchronize
var PENDING_POINTS;

// Adds callback for DOM ready
$(document).ready(competeDomReady);

// Function to call when the DOM is ready for manipulation.
function competeDomReady() {
  if (location.pathname == "/play") {
    setupCompetitionButtons();
  }
}

// Function to call when the DOM is ready for manipulation.
function setupCompetition() {
  CURRENT_GAME_MODE = COMPETE_MODE;
  changeStatus(STATUS_BUSY, true);  
  switchStylesheet('compete');
  resetCompetition();
  getNewCompetition();
  updateCompetitionProgress();
}

// Function to setup click handlers for all buttons
function setupCompetitionButtons() {
  $(".endCompetition").click(cleanupCompetition);
  $("#offerRematch").click(offerRematch);
}

function resetCompetition() {
  COMPETITION_PROBLEMS = []; 
  USER_CURRENT_PROBLEM = 0; 
  OPPONENT_CURRENT_PROBLEM = 0;
  USER_COMPETITION_POINTS = 0;
  OPPONENT_COMPETITION_POINTS = 0;
  COMPETITION_FINISHED = false;
  DONE_PROCESSING_OPPONENT_INPUT = false;
  USER_INPUT = [];
  OPPONENT_INPUT = [];
  OPPONENT_INPUT_INDEX = 0;
  PENDING_POINTS = 0;
}

function cleanupCompetition() {
  $(".jqmWindow:visible").jqmHide();
  setupPractice();
  showRelevantFinishedDialog();  
}

// Function to generate the new problems to be used in this competition
function getNewCompetition() {
  params = {
    competition_id: COMPETITION_ID
  };
  $.getJSON("/competitions/get_problems", params, receivedNewCompetition);
}

// Callback after receiving all the new problems for this competition
function receivedNewCompetition(json) {
  COMPETITION_PROBLEMS = json;
  startPregameTimer();
}

function startPregameTimer() {
  $(".jqmWindow").jqmHide();
  $("#pregameTimer").html(PREGAME_TIMER);
  MODAL_SHOWN_REASON = "Competition Start";
  $("#preGameContainer").jqmShow();
  setTimeout("pregameCountdown()", 1000);
}

function pregameCountdown() {
  count = parseInt($("#pregameTimer").html());
  $("#pregameTimer").html(count - 1);
  if (count > 1) {
    setTimeout("pregameCountdown()", 1000);    
  }
  else {
    $("#preGameContainer").jqmHide();
    showCompetitionProblems();
  }
}

// Start showing problems in the competition
function showCompetitionProblems() {
  if (WORKSPACE == null || OPPONENT == null) {
    setTimeout("showCompetitionProblems()", 100);
  }
  else {
    WORKSPACE.setPadding(100, 0, 300, 0);
    OPPONENT.setPadding(100, 0, 300, 0);
    startGameTimer();            
    updateProblem("user", WORKSPACE, COMPETITION_PROBLEMS[USER_CURRENT_PROBLEM]);
    updateProblem("opponent", OPPONENT, COMPETITION_PROBLEMS[OPPONENT_CURRENT_PROBLEM]);    

    if (OPPONENT_INFO.ai && 
        OPPONENT_INFO.ai_minimum_response_time != null &&
        OPPONENT_INFO.ai_maximum_response_time != null &&
        OPPONENT_INFO.ai_error_rate != null) {
      startAIOpponent();
    }
    else {
      refreshInputUntilFinished();
      processOpponentInputUntilFinished();
    }
  }
}

function competeProblemInput(eventObject) {
  USER_COMPETITION_POINTS += eventObject.getPoints();
  PENDING_POINTS += eventObject.getPoints();
  updateCompetitionProgress();
}

function refreshInputUntilFinished() {
  if (!COMPETITION_FINISHED) {
    refreshInput();
    setTimeout("refreshInputUntilFinished()", RELOAD_INPUT_INTERVAL * 1000);
  }
}

function refreshInput() {
  params = {
    user_id: USER.user_id,
    opponent_id: OPPONENT_INFO.user_id,
    user_input: $.toJSON(USER_INPUT)
  };
  $.postJSON("/competitions/refresh_input", params, refreshInputResponse);
}

function refreshInputResponse(json) {
  if (json.length > OPPONENT_INPUT.length) {
    OPPONENT_INPUT = json;
  }
}

function processOpponentInputUntilFinished() {
  if (!COMPETITION_FINISHED) {
    processOpponentInput();
    setTimeout("processOpponentInputUntilFinished()", PROCESS_OPPONENT_INPUT_INTERVAL * 1000);
  }
}

function processOpponentInput() {
  if (OPPONENT_INPUT_INDEX < OPPONENT_INPUT.length) {
    input = OPPONENT_INPUT[OPPONENT_INPUT_INDEX];
    if (typeof(input) == "number") {
      response = OPPONENT.handleKeyboardInput(input);
    }
    else if (typeof(input) == "object") {
      scaledX = (input[0] * OPPONENT.getScaleX()) + OPPONENT.getPaddingLeft();
      scaledY = (input[1] * OPPONENT.getScaleY()) + OPPONENT.getPaddingTop();
      response = OPPONENT.handleMouseInput(scaledX, scaledY);
    }
    else if (input == "done") {
      DONE_PROCESSING_OPPONENT_INPUT = true;
      response = null;
    }
    
    if (response != null) {
      opponentInput(response);
      OPPONENT_INPUT_INDEX++;
    }
  }
}

function competitionProblemFinishedAfterTimeout() {
  if (USER_CURRENT_PROBLEM < COMPETITION_PROBLEMS.length) {
    USER_CURRENT_PROBLEM++;
    updateProblem("user", WORKSPACE, COMPETITION_PROBLEMS[USER_CURRENT_PROBLEM]);    
    trackEvent("Problem", "Finished Competition Problem", SELECTED_PROBLEM_TYPE, PENDING_POINTS);
    PENDING_POINTS = 0;
    USER.problems_solved++;
    updateCompetitionProgress();
  }
}

function opponentProblemFinishedAfterTimeout() {
  if (OPPONENT_CURRENT_PROBLEM < COMPETITION_PROBLEMS.length) {
    OPPONENT_CURRENT_PROBLEM++;
    updateProblem("opponent", OPPONENT, COMPETITION_PROBLEMS[OPPONENT_CURRENT_PROBLEM]);
    updateCompetitionProgress();
  }
}

function updateCompetitionProgress() {
  $("#userCarrots").html(USER_COMPETITION_POINTS);
  $("#opponentCarrots").html(OPPONENT_COMPETITION_POINTS);
}

function updateProblem(type, workspace, problem) {
  spacedProblemType = addSpaceBeforeCap(problem.problem_type);
  level = problem.level;
  $("." + type + "Level").html(spacedProblemType + " Level " + level);
  runProblem(workspace, problem);
}

// Function to start the timer for the match
function startGameTimer() {
  $("#timer").html(GAME_TIMER);
  $("#timerContainer").show();
  $("#divider").show();
  setTimeout("gameCountdown()", 1000);
}

// Function to start the opponent making input
function startAIOpponent() {
  setTimeout("aiInput()", randomOpponentInputDelay());
}

function randomOpponentInputDelay() {
  return randomInRange(OPPONENT_INFO.ai_minimum_response_time, 
                       OPPONENT_INFO.ai_maximum_response_time);
}

function aiInput() {
  if (parseInt($("#timer").html()) > 0) {
    if (Math.random() < OPPONENT_INFO.ai_error_rate) {
      response = OPPONENT.generateInput(false);
    }
    else {
      response = OPPONENT.generateInput(true);
    }
    
    opponentInput(response);

    setTimeout("aiInput()", randomOpponentInputDelay());        
  }
}

function opponentInput(response) {
  if (response) {
    OPPONENT_COMPETITION_POINTS += response.getPoints();
    updateCompetitionProgress();    
  }  
}

// Function to decrement the countdown timer by one second
function gameCountdown() {
  count = parseInt($("#timer").html());
  $("#timer").html(count - 1);
  if (count > 1) {
    setTimeout("gameCountdown()", 1000);    
  }
  else {
    USER_INPUT.push("done");
    refreshInput();    
    setTimeout("synchronizingMaxWait()", SYNCHRONIZING_MAX_WAIT * 1000);
    finishCompetition();
  }
}

function synchronizingMaxWait() {
  DONE_PROCESSING_OPPONENT_INPUT = true;
}

function finishCompetition() {  
  if (!OPPONENT_INFO.ai && !DONE_PROCESSING_OPPONENT_INPUT) {
    MODAL_SHOWN_REASON = "Competition Finished";
    $("#synchronizingWithOpponent").jqmShow();
    setTimeout("finishCompetition()", SYNCHRONIZING_INTERVAL * 1000);
  }
  else {
    COMPETITION_FINISHED = true;
    if (CURRENT_USER_IS_CHALLENGER) {
      saveCompetitionOutcome();
    }
    $("#synchronizingWithOpponent").jqmHide();  
    showOutcome();
  }
}

function saveCompetitionOutcome() {
  params = {
    competition_id: COMPETITION_ID,
    user_problems_solved: USER_CURRENT_PROBLEM,
    user_points: USER_COMPETITION_POINTS,
    opponent_problems_solved: OPPONENT_CURRENT_PROBLEM,
    opponent_points: OPPONENT_COMPETITION_POINTS,
    bonus: WIN_BONUS
  };
  $.postJSON("/competitions/competition_finished", params);

  totalPoints = USER_COMPETITION_POINTS + OPPONENT_COMPETITION_POINTS + WIN_BONUS;
  trackEvent("Problem", "Finished Competition", COMPETITION_PROBLEMS[0].problem_type, totalPoints);
}

// Function to show the user whether they won, lost, or tied.
function showOutcome() {
  if (USER_COMPETITION_POINTS > OPPONENT_COMPETITION_POINTS) {
    $("#outcome").html("Congratulations, you won!");      
    $("#winBonus").html(WIN_BONUS);
    $("#winBonusMessage").show();
    USER.points += WIN_BONUS;
  }
  else if (USER_COMPETITION_POINTS < OPPONENT_COMPETITION_POINTS) {
    $("#outcome").html("Sorry, you lost.");
    $("#winBonusMessage").hide();
  }
  else {
    $("#outcome").html("You tied.");   
    $("#winBonusMessage").hide();
  }
  
  USER.points += USER_COMPETITION_POINTS;    
  
  $("#carrotsEarned").html(USER_COMPETITION_POINTS);
  WORKSPACE.hideProblem();
  OPPONENT.hideProblem();
  $("#divider").hide();
  MODAL_SHOWN_REASON = "Competition Finished";
  $("#outcomeContainer").jqmShow();
}

function offerRematch() {
  // TODO(dsiroker): Implement this by refreshing a new competition ACCEPTED or REJECTED
  resetCompetition();
  updateCompetitionProgress();
  $("#outcomeContainer").jqmHide();
  $("#waitingForRematch").jqmShow();
  setTimeout("rematchAccepted()", MAX_REMATCH_TIMEOUT * 1000);
}

function rematchAccepted() {
  getNewCompetition();
}


/*
 *  Helper functions for competition mode
 */
 
// Function to generate a random integer from min to max (inclusive)
function randomInRange(min, max) {
  return min + Math.floor(Math.random() * (max - min));
}

// Function to generate a random integer near the num provided
function randomNearby(num) {
  return Math.floor(num * Math.random() * 2);
}
