/*
Copyright (C) 2016
Developed at University of Toronto

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

const common = require('./common.js');

const successMsg = {success:true, err:'Validation Passed'}
const failMsg = {success:false, err:common.getError(3022)}

/**
 * Send back specific error message by question type
 *
 * @param {string} message
 * @returns {object}
 */
var qTypeFailMsg = function(message) {
    return {success:false, err:message};
}

/**
 * Validate all question fields on first entry to db
 *
 * @param {object} info
 * @returns {object}
 */
exports.questionCreationValidation = function(info) {
    for (var key in common.questionAttributes.DEFAULT) {
        if (!(key in info) || !validateAttributeType(info[key], key, 'DEFAULT')) {
            return failMsg;
        }
    }
    const result = validateDefaultQuestionValues(info);
    if(!result.success){
        return result;
    }
    return validateQuestionAttributesByType(info,info.type);
}

/**
 * validate default question values
 *
 * @param {number} questionData
 * @returns {object}
 */
var validateDefaultQuestionValues = function(questionData){
    if ('minpoints' in questionData && 'maxpoints' in questionData){
        if (questionData.minpoints < 0
            || questionData.minpoints > questionData.maxpoints
            || questionData.maxpoints < 0){
            return qTypeFailMsg(common.getError(3024));
        }
    }
    return successMsg;
}

/**
 * Validate all fields that will be modified
 *
 * @param {object} question
 * @param {string} type
 * @returns {object}
 */
exports.validateAttributeFields = function(question,type) {
    var extraAttributes = false;

    for (var key in question) {
        // check const attributes
        if (key in common.questionAttributes.DEFAULT) {
            if (!validateAttributeType(question[key], key, 'DEFAULT')) {
                return failMsg;
            }
        // not common field found
        } else {
            extraAttributes = true;
        }
    }

    const result = validateDefaultQuestionValues(question);
    if(!result.success){
        return result;
    }

    // check by question type to validate the extra fields
    if (extraAttributes) {
        return validateQuestionAttributesByType(question,type);
    }
    return successMsg;
}

/**
 * validate question attributes by type
 *
 * @param {object} question
 * @param {string} type
 * @returns {object}
 */
var validateQuestionAttributesByType = function(question, type) {
    var result;

    switch (type) {
        case common.questionTypes.REGULAR.value:
            result = regexAttributeValidator(question);
            break;

        case common.questionTypes.MULTIPLECHOICE.value:
            result = multipleChoiceAttributeValidator(question);
            break;

        case common.questionTypes.TRUEFALSE.value:
            result = trueAndFalseAttributeValidator(question);
            break;

        case common.questionTypes.MATCHING.value:
            result = matchingAttributeValidator(question);
            break;

        case common.questionTypes.CHOOSEALL.value:
            result = chooseAllAttributeValidator(question);
            break;
        case common.questionTypes.ORDERING.value:
            result = orderingAttributeValidator(question);
            break;
        default:
            result = failMsg;
            break;
    }
    return result;
}

/**
 * regex attribute validator
 *
 * @param {object} question
 * @returns {object}
 */
var regexAttributeValidator = function(question) {
    if (!validateAllAttributesInGroup(question,'REGULAR')) {
        return qTypeFailMsg(commmon.getError(3022));
    }
    return successMsg;
}

/**
 * multiple choice attribute validator
 *
 * @param {object} question
 * @returns {object}
 */
var multipleChoiceAttributeValidator = function(question) {
    if (!validateAllAttributesInGroup(question,'MULTIPLECHOICE')) {
        return qTypeFailMsg(commmon.getError(3022));
    }
    if (!validateArrayObject(question.choices,'String')) {
        return failMsg;
    }
    if (question.choices.length < 2) {
        return qTypeFailMsg(common.getError(3025));
    }
    return successMsg;
}

/**
 * true and false attribute validator
 *
 * @param {object} question
 * @returns {object}
 */
var trueAndFalseAttributeValidator = function(question) {
    if (!validateAllAttributesInGroup(question,'TRUEFALSE')) {
        return qTypeFailMsg(commmon.getError(3022));
    }
    if (question.answer !== 'true' && question.answer !== 'false' ) {
        return qTypeFailMsg(common.getError(3026));
    }
    return successMsg;
}

/**
 * matching attribute validator
 *
 * @param {object} question
 * @returns {object}
 */
var matchingAttributeValidator = function(question) {
    if (!validateAllAttributesInGroup(question,'MATCHING')) {
        return qTypeFailMsg(commmon.getError(3022));
    }
    if (!validateArrayObject(question.leftSide,'String') || !validateArrayObject(question.rightSide,'String')) {
        return failMsg;
    }
    if (question.leftSide.length < 2 || question.rightSide.length < 2) {
        return qTypeFailMsg(common.getError(3025));
    }
    return successMsg;
}

/**
 * choose all attribute validator
 *
 * @param {object} question
 * @returns {object}
 */
var chooseAllAttributeValidator = function(question) {
    if (!validateAllAttributesInGroup(question,'CHOOSEALL')) {
        return qTypeFailMsg(commmon.getError(3022));
    }
    if (!validateArrayObject(question.choices,'String') || !validateArrayObject(question.answer,'String')) {
        return failMsg;
    }
    if (question.choices.length < 2) {
        return qTypeFailMsg(common.getError(3025));
    }

    if (question.answer.length < 1) {
        return qTypeFailMsg(common.getError(3026));
    }
    return successMsg;
}

/**
 * ordering attribute validator
 *
 * @param {object} question
 * @returns {object}
 */
var orderingAttributeValidator = function(question) {
    if (!validateAllAttributesInGroup(question,'ORDERING')) {
        return qTypeFailMsg(commmon.getError(3022));
    }
    if (!validateArrayObject(question.answer,'String')) {
        return failMsg;
    }
    if (question.answer.length < 2) {
        return qTypeFailMsg(common.getError(3025));
    }
    return successMsg;
}

/**
 * Validate specific value to it's attributeType in DB
 *
 * @param {*} valueToCheck
 * @param {string} key
 * @param {string} attributeType
 * @returns {boolean}
 */
var validateAttributeType = function(valueToCheck, key, attributeType) {
    return Object.prototype.toString.call(valueToCheck) === common.questionAttributes[attributeType][key].type;
}
exports.validateAttributeType = validateAttributeType;

/**
 * Validate all attributes in Object being passed and has correct field types
 *
 * @param {object} objectToCheck
 * @param {string} attributeType
 * @returns {boolean}
 */
var validateAllAttributesInGroup = function(objectToCheck, attributeType) {
    for (var key in common.questionAttributes[attributeType]) {
        if (!(key in objectToCheck) || !validateAttributeType(objectToCheck[key], key, attributeType)) {
            return false;
        }
    }
    return true;
}

/**
 * Validate an Array object to contain specific value types
 *
 * @param {list} arrayObject
 * @param {string} typeOfvalue
 * @returns {boolean}
 */
var validateArrayObject = function(arrayObject,typeOfvalue) {
    for (var i = 0; i < arrayObject.length; i++) {
        if (!validateAttributeType(arrayObject[i],typeOfvalue,'DATATYPES')) {
            return false;
        }
    }
    return true;
}
exports.validateArrayObject = validateArrayObject;
