/* Client side JavaScript for CASI.Server.Web.QuestionPanel web control
 * Contains data validation functions */

// array containing jagged arrays of answer radio button ids for each question
var questionAnswerControls = new Array();
// array containing jagged arrays of additional data field ids for each answer
var answerFieldControls = new Array();

// array containing parent answer guids for each additional data field
var fieldAnswers = new Array();
// array containing parent question guids for each answer
var answerQuestions = new Array();

// array containing rules for each additional data field
var fieldRules = new Array();
// array containing validation reports for each additional dat afield
var fieldReports = new Array();

// holds answer state for each question
var questionState = new Array();
// holds answer state for each answer
var answerState = new Array();

// answer states are 0 - unanswered, 1 - answered, 2 - partially complete

// holds last modified field element
var lastModifiedFieldElement;
// holds last answer element chosen;
var lastChosenAnswerElement;

function dataFieldModified(fieldElement) {
  lastModifiedFieldElement = fieldElement;
  setTimeout("dataFieldModifiedCallback();", 100);
  }
  
function dataFieldModifiedCallback() {
  var fieldElement = lastModifiedFieldElement;
  // convert dollars in field name to underscores so compatible with id
  var fieldElementName = fieldElement.name.replace(/\$/g, "_");
  
  var rules = fieldRules[fieldElementName];
  var answerGuid = fieldAnswers[fieldElementName];
  var questionGuid = answerQuestions[answerGuid];
  
  // set answer state -- always assume complete
  //answerState[answerGuid] = 1;
  
  // validate data
  var result = ebValidateRules(fieldRules[fieldElementName], fieldElement.value);
  fieldReports[fieldElementName] = result;
  if (result.isValid) {
    clearReport(fieldElement.id); }
  else {
    showReport(fieldElement.id, result.report); }

  var someFieldsAnswered = false;

  if ((fieldElement.type == "checkbox" && fieldElement.checked) ||
       (fieldElement.type != "checkbox" && fieldElement.value != "")) {
    // this field is filled out, so assume answer is complete
    someFieldsAnswered = true;
  }
  else if (fieldElement.type == "checkbox") {
    // check if other checkboxes in the same group are ticked
    var controlGroup = document.getElementsByName(fieldElement.name);
    for (var i = 0; i < controlGroup.length; i++) {
      if (controlGroup[i].type == "checkbox" && controlGroup[i].checked) someFieldsAnswered = true;
    }
  }
  else {
    // check to see if any other additional data fields have been filled out
    var missingElements = 0;
    for (var i = 0; i < answerFieldControls[answerGuid].length; i++) {
      var dataField = answerFieldControls[answerGuid][i];
      if (dataField != fieldElement.id) {
        var otherElement = document.getElementById(dataField)
        if (otherElement) {
          // first a basic check to see if field is filled out
          if ((otherElement.type == "checkbox" && otherElement.checked) ||
            (otherElement.type != "checkbox" && otherElement.value != "")) someFieldsAnswered = true;
        }
        else {
          missingElements += 1;
        }
      }
    }
  }
    // if none of the additional data can be checked then assume some fields answered
    if (answerFieldControls[answerGuid].length == missingElements) someFieldsAnswered = true;
    
  // check to see if all additional data for this answer is valid
  var validationResult = true;
  for(var i = 0; i < answerFieldControls[answerGuid].length; i++)  {
    var dataField = answerFieldControls[answerGuid][i];
    // now check validation report
    if (fieldReports[dataField]) {
      validationResult = validationResult && fieldReports[dataField].isValid;
      }
    }
      
  // basic check to ensure some data has been filled out, in lieu of rules
  if (someFieldsAnswered) { answerState[answerGuid] = 1 } else { answerState[answerGuid] = 0; }
  // answer is incomplete if validation has not been passed
  if (!validationResult) { answerState[answerGuid] = 2; }
  
  switch (questionState[questionGuid]) {
    case undefined :
      // set to zero
      questionState[questionGuid] = 0;
    case 0 :
      // question is unanswered as no answer has been explicitly chosen, so
      // show a reminder to choose an answer
      if (answerState[answerGuid] > 0) {
        questionState[questionGuid] = 2;
        if (questionAnswerControls[questionGuid].length == 1) {
          showAlert(questionGuid, "warning", "Remember to indicate that your answer is complete");
          }
        else {
          showAlert(questionGuid, "warning", "Remember to choose the appropriate answer.");
          }
        }
      break;
    case 1 :
    case 2 :
      if (document.getElementById("q" + questionGuid + "_a" + answerGuid).checked) {
        // only worry about changing question state if the edited field belongs to the currently
        // chosen answer
        switch (answerState[answerGuid]) {
          case 0 :
            // no additional data filled out, so show reminder
            questionState[questionGuid] = 1;
            showAlert(questionGuid, "info", "Remember to fill out all the required fields.");
            break;
          case 1 :
            // answer is "complete"
            questionState[questionGuid] = 1;
            showAlert(questionGuid, "complete", "This answer is complete.");
            break;
          case 2 :
            // answer is incomplete as it has failed validation
            questionState[questionGuid] = 2;
            showAlert(questionGuid, "warning", "You must correct the problems below:");
            break;
          }
        }
      else {
        // question has been partially answered.
        if (questionAnswerControls[questionGuid].length > 1) {
          // question has multiple answers
          questionState[questionGuid] = 1;
          showAlert(questionGuid, "info", "Make sure you've chosen the appropriate answer.");
          }
        else {
          // question has single answer, and it is unchecked
          if (answerState[answerGuid] == 0) {
            questionState[questionGuid] = 0;
            hideAlert(questionGuid);
            }
          else {
            questionState[questionGuid] = 1;
            showAlert(questionGuid, "warning", "Remember to indicate that your answer is complete");
            }
          }  
        break;
        }
      break;
    }
  }
  
function answerChosen(answerElement) {
  lastChosenAnswerElement = answerElement;
  setTimeout("answerChosenCallback();", 100);
  }
  
function answerChosenCallback() {
  var answerElement = lastChosenAnswerElement;
  var guidIdx = answerElement.id.indexOf("_a") + 2
  var answerGuid = answerElement.id.substr(guidIdx, answerElement.id.length - guidIdx);
  var questionGuid = answerQuestions[answerGuid];
  
  if (answerElement.checked) {
    // an answer has been selected, update question state
    switch (answerState[answerGuid]) {
      case undefined :
        // set to zero
        answerState[answerGuid] = 0;
      case 0 :
        if (answerFieldControls[answerGuid].length == 0) {
          // no additional data, so question is complete
          questionState[questionGuid] = 1;
          showAlert(questionGuid, "complete", "This answer is complete.");
          }
        else {
          //  chosen answer is unaswered, show warning but treat as complete
          // (was chosen answer is unanswered, set question state to incomplete [2])
          questionState[questionGuid] = 1;
          showAlert(questionGuid, "info", "Remember to fill out all the required fields.");
          }
        break;
      case 1 :
        // chosen answer has been somewhat answered, set question state to answered
        questionState[questionGuid] = 1;
        showAlert(questionGuid, "complete", "This answer is complete.");
        break;
      case 2 :
        // answer is incomplete as it has failed validation
        questionState[questionGuid] = 2;
        showAlert(questionGuid, "warning", "You must correct the problems below:");
        break;
      }
    }
  else {
    // answer has been deselected, update question state.
    // can only go to one of two states: unanswered or incomplete
    switch (answerState[answerGuid]) {
      case undefined :
        // set to zero
        answerState[answerGuid] = 0;
      case 0 :
        questionState[questionGuid] = 0;
        hideAlert(questionGuid);
        break;
      case 1 :
      case 2 :
        if (answerFieldControls[answerGuid].length == 0) {
          // no additional data, so mark question unanswered
          questionState[questionGuid] = 0;
          hideAlert(questionGuid);
          }
        else {
          // mark question incomplete
          questionState[questionGuid] = 2;
          showAlert(questionGuid, "warning", "Remember to indicate that your answer is complete");
          }
        }  
    }
  }
  
function showAlert(questionGuid, cssClass, messageText) {
  var alertDiv = document.getElementById("q" + questionGuid + "_alert");
  alertDiv.className = "alert " + cssClass;
  alertDiv.innerHTML = messageText;
  }
  
function hideAlert(questionGuid) {
  var alertDiv = document.getElementById("q" + questionGuid + "_alert");
  alertDiv.className = "";
  alertDiv.innerHTML = "";
  }
  
function showReport(fieldId, messageText) {
  var reportDiv = document.getElementById(fieldId + "_report");
  if (reportDiv) {
    reportDiv.innerHTML = messageText;
    reportDiv.style.display = "block";
    }
  }
  
function clearReport(fieldId) {
  var reportDiv = document.getElementById(fieldId + "_report");
  if (reportDiv) {
    reportDiv.style.display = "none";
    }
  }

function validateAudit(source, arguments) {
  var foundIncomplete = false;
  var foundUnanswered = false;
  var questionGuid;
  
  for (questionGuid in questionAnswerControls) {
    // Find chosen answer for this question
    var chosenAnswerGuid = "";
    for (controlIndex in questionAnswerControls[questionGuid]) {
      var answerElement = document.getElementById(questionAnswerControls[questionGuid][controlIndex]);
      if (answerElement.checked) {
        var guidIdx = answerElement.id.indexOf("_a") + 2
        chosenAnswerGuid = answerElement.id.substr(guidIdx, answerElement.id.length - guidIdx);
        }
    }
    if (chosenAnswerGuid != "") {
      // Do final validation of all additional data fields for chosen answer
      for (fieldId in fieldAnswers) {
        if (fieldAnswers[fieldId] == chosenAnswerGuid) {
          // this field is for the answer we're examining so validate field
          lastModifiedFieldElement = document.getElementById(fieldId);
          if (lastModifiedFieldElement) { dataFieldModifiedCallback(); }
        }
      }
    }
    if (questionState[questionGuid] == 2) {
      // question is incomplete
      foundIncomplete = true;
      }
    else if (questionState[questionGuid] != 1) {
      // question is unanswered
      foundUnanswered = true;
      }
    }
  
  if (foundIncomplete == true) {
    window.alert("You have partially completed answering one or more questions.\n\n" +
      "Finish answering questions marked with a red warning\nbefore submitting your responses.");
    arguments.IsValid = false;
    }
  else if (foundUnanswered == true) {
    arguments.IsValid = window.confirm("You have not answered all questions.\n\n" + 
      "Submit responses for the questions you have answered?");
    }
  else {
    arguments.IsValid = true;
    }
    
  if (arguments.IsValid) {
    // set hidden form variable to override server-side validation result
    document.forms[0].elements["AuditValidator_override"].value = "true";
    }
  }


/* TEMPORARY INCLUDE FROM /eblock/newadmin/inline/inc/fieldeditor.js */

// validates a value against some rules
function ebValidateRules(rules, value) {
  var result = {value: value, report: "", isValid: true};
  // return valid if no rules
  if (!rules) return result;
  
  // evaluate rules in turn
  for (var i = 0; i < rules.length; i++) {
    var rule = rules[i];
    switch (rule.rule) {
      
      // check value is supplied
      case "required":
        if (value == "") {
          result.isValid = false;
          result.report = rule.report;
          }
        break;
      
      // check value is between given limits
      case "limit":
        if ((rule.lower && value < rule.lower) || (rule.upper && value > rule.upper)) {
          result.isValid = false;
          result.report = rule.report;
        }
        break;
      
      // check value is between given lengths
      case "length":
        if ((rule.min > 0 && value.toString().length < rule.min) || (rule.max > 0 && value.toString().length > rule.max)) {
          result.isValid = false;
          result.report = rule.report;
        }
        break;
      
      // check value matches text or expression
      case "match":
        var hasMatch = false;
        switch (rule.op) {
          case "contains" :
            hasMatch = (String(value).indexOf(rule.text) >= 0);
            break;
          case "equals" :
            hasMatch = (String(value) == rule.text);
            break;
          case "expression" :
            var ex = new RegExp("^" + rule.text + "$", "");
            hasMatch = (ex.test(value) ||  (value == ""));
            break;
        }
        if (!hasMatch) {
          result.isValid = false;
          result.report = rule.report;
        }
        break;
      }
    if (!result.isValid) return result;
    }
  return result;
  }

