var POLLING_FREQUENCY = 500; // milliseconds
var POLLING_ATTEMPTS  = 50;
var PASSED_STATUS     = 0;
var VALID_STATUS      = 1;
var FAILED_STATUS     = 2;
var NOT_RUN_STATUS    = 3;
var STATUS_NAMES      = ['passed', 'was validated by', 'failed', 'was not run by'];
var RESULT_ICONS      = ['correct.png', 'correct.png', 'incorrect.png'];
var RESULT_CLASSES    = ['passed', 'valid', 'failed'];

// ----------------------------------
// Sliders
// ----------------------------------
function slideAndShowQuestionSets(s) {
  $(s.prefix + 'questions_container').morph('left: 40px', {duration: 0.5});
  $(s.prefix + 'question_container').morph('left: 795px', {duration: 0.5});
  $(s.prefix + 'question_solution_container').morph('left: 1590px', {duration: 0.5});
}

function slideAndShowQuestion(s) {
  $(s.prefix + 'questions_container').morph('left: -755px', {duration: 0.5});
  $(s.prefix + 'question_container').morph('left: 40px', {duration: 0.5});
  $(s.prefix + 'question_solution_container').morph('left: 795px', {duration: 0.5});
}

function slideAndShowQuestionSolution(s) {
  $(s.prefix + 'questions_container').morph('left: -1550px', {duration: 0.5});
  $(s.prefix + 'question_container').morph('left: -755px', {duration: 0.5});
  $(s.prefix + 'question_solution_container').morph('left: 40px', {duration: 0.5});
}

// ----------------------------------
// Question Sets
// ----------------------------------
var LOAD_QUESTIONS_ERROR_MSG = "An error occurred while loading your questions. The server may be experiencing problems. Please log out, and try using the site again in a few minutes.";
function loadQuestionSets(s) {
  spinnerShow(s.prefix + 'questions_indeterminate');
  ajaxWrapper('api/questions/', {course: courseID, user: s.user}, function (json) { return json.question_sets; }, loadQuestionSetsSuccess.curry(s), loadQuestionSetsFailure.curry(s));
}

function loadQuestionSetsSuccess(s, json) {
  drawQuestionSets(s, json.question_sets);
  spinnerHide(s.prefix + 'questions_indeterminate');
}

function loadQuestionSetsFailure(s, transport, reason) {
  alert(reason || LOAD_QUESTIONS_ERROR_MSG);
  spinnerHide(s.prefix + 'questions_indeterminate');
}


// UI
var QUESTION_SET_TEMPLATE = new Template("<h2>#{name}</h2><table id='#{__prefix}question_set_#{number}' class='question_set'></table>");
var QUESTION_TEMPLATE = new Template("<td class='icon'>#{required_image}</td><td class='description'><div class='name'>#{number}. #{name}</div><div class='meta'></div></td><td class='count'></td>");

function drawQuestionSets(s, questionSets) {
  if (questionSets.length == 0) {
    $(s.prefix + 'questions_no_questions').show();
    return;
  }

  $(s.prefix + 'questions_no_questions').hide();
  $$('#' + s.prefix + 'questions_container .table_container *').each(function(elem) {
    elem.remove();
  });

  var heading = $(s.prefix + 'questions_heading');
  if (s.user_name) 
    heading.innerHTML = 'Questions for ' + s.user_name;
  
  var container = $$('#' + s.prefix + 'questions_container .table_container').first();
  questionSets.each(function(questionSet) {
    // render the questionset template
    questionSet['__prefix'] = s.prefix;
    container.insert(QUESTION_SET_TEMPLATE.evaluate(questionSet));
    
    // render each of the questions in the question set
    var table = $(s.prefix + 'question_set_' + questionSet.number);
    tbody = new Element('tbody');
    table.appendChild(tbody);
    questionSet.questions.each(function(question) {
      tr = new Element('tr');
      tbody.appendChild(tr);
      tr.id = s.prefix + 'question_' + question.id;
      if (question.required)
        question.required_image = '<img src="images/required.png" alt="" title="This is a required problem">';
      tr.insert(QUESTION_TEMPLATE.evaluate(question));
      tr.observe('click', function(event) {
        loadQuestion(s, question);
      });
      
      var points = 0;
      if (question.status != null) {
        if (question.status == PASSED_STATUS) {
          tr.className = 'question_passed';
          points = question.npoints;
        } 
        else if (question.status == VALID_STATUS)
          tr.className = 'question_valid';
        else
          tr.className = 'question_failed';
      }
      else
        tr.className = 'question_na';

      if (question.points != 0)
        $$('#' + tr.id + ' .count').first().innerHTML = points + ' / ' + question.points;
      
      var msg = 'No submissions';
      if (question.nsubmissions) {
        msg = question.nsubmissions + ' submission';
        if (question.nsubmissions != 1)
          msg += 's';
      }
      $$('#' + tr.id + ' .meta').first().innerHTML = msg + ' made.';
    });
  });
}




// ----------------------------------
// Individual Question
// ----------------------------------
var LOAD_QUESTION_ERROR_MSG = "An error occurred while loading your question. The server may be experiencing problems. Please log out, and try using the site again in a few minutes.";
function loadQuestion(s, question) {
  spinnerShow(s.prefix + 'question_indeterminate');
  ajaxWrapper('api/questions/show', {course: courseID, user: s.user, id: question.id}, function(json) { return json.question; }, loadQuestionSuccess.curry(s), loadQuestionFailure.curry(s));
}

function loadQuestionSuccess(s, json) {
  slideAndShowQuestion(s);
  drawQuestion(s, json.question);
  drawQuestionSubmissions(s, json.submissions);
  spinnerHide(s.prefix + 'question_indeterminate');
}

function loadQuestionFailure(s, transport, reason) {
  alert(reason || LOAD_QUESTION_ERROR_MSG);
  spinnerHide(s.prefix + 'question_indeterminate');
}


function drawQuestion(s, question) {
  $(s.prefix + 'question_heading').innerHTML = question.name;
  $(s.prefix + 'question_description').innerHTML = question.description;
  MathJax.Hub.Queue(['Typeset', MathJax.Hub, s.prefix + 'question_description']);
  
  // bind the back button
  var back = $(s.prefix + 'question_back');
  back.stopObserving('click');
  back.observe('click', function(event) {
    loadQuestionSets(s);
    slideAndShowQuestionSets(s);
  });
  
  // if the question set instance has a submission area
  var e = $(s.prefix + 'question_code');
  if (e) {
    $(s.prefix + 'question_submission_loading').hide();
    $(s.prefix + 'question_code').value = '';
    $(s.prefix + 'question_submit').stopObserving('click');
    $(s.prefix + 'question_submit').observe('click', submitQuestion.curry(s, question));
  }
  
  // show solution button if it should be visible
  var solution = $(s.prefix + 'question_solution');
  if (question.show_solution) {
    solution.stopObserving('click');
    solution.observe('click', loadQuestionSolution.curry(s, question));
    solution.show();
  }
  else
    solution.hide();
}


function drawQuestionSubmissions(s, submissions) {
  $(s.prefix + 'question_submissions').update();
  submissions.each(function(submission) {
    drawQuestionSubmission(s, submission);
  });
}


// ----------------------------------
// Submit Question
// ----------------------------------
var SUBMIT_QUESTION_ERROR_MSG = "An error occurred while submitting your question. The server may be experiencing problems. Please log out, and try using the site again in a few minutes.";

// REST API
function submitQuestion(s, question, event) {
  event.stop();
  var code = $('question_code').value;
  if (code.strip().empty()) {
    alert("You don't have any code to submit. Try submitting again once you add some code to the textbox.");
  }
  else {
    spinnerShow(s.prefix + 'question_submission_loading');
    ajaxWrapper('api/submissions/create', {course: courseID, id: question.id, code: code}, function(json) { return json.id; }, submitQuestionSuccess.curry(s), submitQuestionFailure.curry(s));
  }
}

function submitQuestionSuccess(s, json) {
  requestQuestionResults(s, json.id, 0);
}

function submitQuestionFailure(s, transport, reason) {
  alert(reason || SUBMIT_QUESTION_ERROR_MSG);
  spinnerHide(s.prefix + 'question_submission_loading');
}




// ----------------------------------
// Response Polling
// ----------------------------------
var POLL_SUBMISSION_ERROR_MSG = "An error occurred while requesting the status of your submission. The server may be experiencing problems. Please log out, and try using the site again in a few minutes.";

function requestQuestionResults(s, submissionID, attempts) {
  setTimeout(function(){ new Ajax.Request('api/submissions/show', {parameters: {course: courseID, id: submissionID}, onSuccess: pollQuestionSubmissionSuccessful.curry(s, attempts), onFailure: pollQuestionSubmissionFailed}); }, POLLING_FREQUENCY);
}

function pollQuestionSubmissionSuccessful(s, attempts, transport) {
  var response = transport.responseJSON.response;
  if (attempts >= POLLING_ATTEMPTS) {
    alert("Your code failed to submit successfully. Please try again or contact a system adminstrator.");
    spinnerHide(s.prefix + 'question_submission_loading');
  }
  else {
    if (transport.responseJSON.success && response) {
      if (response.status == NOT_RUN_STATUS)
        requestQuestionResults(s, response.id, attempts + 1);
      else {
        drawQuestionSubmission(s, response);
        spinnerHide(s.prefix + 'question_submission_loading');
      }
    } 
    else {
      if (transport.responseJSON.reason)
        alert(transport.responseJSON.reason);
      else
        alert(POLL_SUBMISSION_ERROR_MSG);
      spinnerHide(s.prefix + 'question_submission_loading');
    }
  }
}

function pollQuestionSubmissionFailed(transport) {
  alert(POLL_SUBMISSION_ERROR_MSG);
}

var SUBMISSION_TEMPLATE = new Template("<div class='submission #{style}' id='#{__prefix}submission_#{id}'><div class='status'>#{status}<span class='timestamp'>Submitted at #{timestamp}</span></div>Your submission was:<div class='code'>#{submission}</div></div>");
var FEEDBACK_BLOCK = "<h3 class='submission_subtitle'>Feedback:</h3><div class='feedbacks'></div>";
var RESULTS_BLOCK = "<h3 class='submission_subtitle'>Test cases:</h3><div class='results'></div></div>";
var SUBMISSION_RESULT_TEMPLATE = new Template("<div class='#{style}'>#{message}</div>");
var SUBMISSION_FEEDBACK_TEMPLATE = new Template("<div class='feedback confluence'>#{message}</div>");
function drawQuestionSubmission(s, submission) {
  if(submission.assessed){
    submission.style = 'assessed';
    submission.status = 'This submission has been manually reviewed';
  }
  else{
    submission.status = 'This submission ' + STATUS_NAMES[submission.status] + ' our automatic testing';
  }
  
  submission['__prefix'] = s.prefix;
  $(s.prefix + 'question_submissions').insert({top: SUBMISSION_TEMPLATE.evaluate(submission)});
  var block = $(s.prefix + 'submission_' + submission.id);

  if (submission.feedback.size() != 0) {
    block.insert({bottom: FEEDBACK_BLOCK});
    var feedbacks = $$('#' + s.prefix + 'submission_' + submission.id + ' .feedbacks').first();
    submission.feedback.each(function(fb) {
      feedbacks.insert(SUBMISSION_FEEDBACK_TEMPLATE.evaluate(fb));
    });
  }

  if (submission.results.size() != 0){
    block.insert({bottom: RESULTS_BLOCK});
    var results = $$('#' + s.prefix + 'submission_' + submission.id + ' .results').first();
    submission.results.each(function(result) {
      result.style = RESULT_CLASSES[result.status];
      results.insert(SUBMISSION_RESULT_TEMPLATE.evaluate(result));
    });
  }
}



// ----------------------------------
// Question Solution
// ----------------------------------
var LOAD_SOLUTION_ERROR_MSG = "An error occurred while loading the sample solution to the question. The server may be experiencing problems. Please log out, and try using the site again in a few minutes.";
function loadQuestionSolution(s, question) {
  spinnerShow(s.prefix + 'question_solution_indeterminate');
  ajaxWrapper('api/questions/solution', {course: courseID, id: question.id}, function(json) { return json.solution; }, loadQuestionSolutionSuccess.curry(s), loadQuestionSolutionFailure.curry(s));
}

function loadQuestionSolutionSuccess(s, json) {
  slideAndShowQuestionSolution(s);
  drawQuestionSolution(s, json.solution);
  spinnerHide(s.prefix + 'question_solution_indeterminate');
}

function loadQuestionSolutionFailure(s, transport, reason) {
  $('question_solution_indeterminate').fade({duration: 0.5});
  spinnerHide(s.prefix + 'question_solution_indeterminate');
}

function drawQuestionSolution(s, solution) {
  $(s.prefix + 'question_solution_notes').innerHTML = solution;
  MathJax.Hub.Queue(['Typeset', MathJax.Hub, s.prefix + 'question_solution_notes']);

  var back = $(s.prefix + 'question_solution_back');
  back.stopObserving('click');
  back.observe('click', function(event) {
    slideAndShowQuestion(s);
  });
}


