/*
 * Copyright 2018 Scytl Secure Electronic Voting SA
 *
 * All rights reserved
 *
 * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package
 */
package com.scytl.products.ov.commons.correctness.builder;

import static com.scytl.products.ov.commons.correctness.CorrectnessError.*;

public class CorrectnessUtil {

	public static final String EVALUATE_CORRECTNESS_WITH_FEEDBACK = "evaluateCorrectnessWithFeedback";

	public static final String EVALUATE_CORRECTNESS = "evaluateCorrectness";

	private CorrectnessUtil() {
		super();
	}

	public static void createCorrectnessFunctions(StringBuilder sb) {
		sb.append("var CorrectnessFunctions = {");
		createRelevantSelectionExtractingFunction(sb);
		sb.append(",");
		createIsFullBlankFunction(sb);
		sb.append(",");
		createMaxMatchEvaluator(sb);
		sb.append(",");
		createMinMatchEvaluator(sb);
		sb.append(",");
		createMinWithBlankMatchEvaluator(sb);
		sb.append(",");
		createAreAllSelectionsUnique(sb);
		sb.append(",");
		createFusionCheck(sb);
		sb.append("};");
	}

	private static void createMaxMatchEvaluator(StringBuilder sb) {
		sb.append("isMaxMatch : function (selectionRelevantVotingOptions, maxReq, selectionName, callbackFunction){");
		sb.append("var maxMatch = maxReq == selectionRelevantVotingOptions.length;");
		sb.append("if (!maxMatch) {");
		sb.append("callbackFunction('" + MAX_ERROR + "', B64.decode(selectionName));");
		sb.append("}");
		sb.append("return maxMatch;");
		sb.append("}");
	}

	private static void createMinMatchEvaluator(StringBuilder sb) {
		sb.append("isMinMatch : function (selectionRelevantVotingOptions, minReq, selectionName, callbackFunction){");
		sb.append("var minMatch = minReq <= selectionRelevantVotingOptions.length;");
		sb.append("if (!minMatch) {");
		sb.append("callbackFunction('" + MIN_ERROR + "', B64.decode(selectionName));");
		sb.append("}");
		sb.append("return minMatch;");
		sb.append("}");
	}

	private static void createMinWithBlankMatchEvaluator(StringBuilder sb) {
		sb.append("isMinWithBlankMatch: function (selectionRelevantVotingOptions, minReq, allBlanksSelected, fullBlank, blanks, selectionName, callbackFunction) {");
		sb.append("var partialResult = true;");
		sb.append("var encounteredBlanks = [];");
		sb.append("var optionsWithoutBlanks = [];");
		sb.append("for (i = 0; i < selectionRelevantVotingOptions.length; i++) {");
		sb.append("if (blanks.indexOf(selectionRelevantVotingOptions[i]) > -1) {");
		sb.append("encounteredBlanks.push(selectionRelevantVotingOptions[i]);");
		sb.append("} else {");
		sb.append("optionsWithoutBlanks.push(selectionRelevantVotingOptions[i]);");
		sb.append("}");
		sb.append("}");
		sb.append("if ((allBlanksSelected && (!fullBlank && minReq != 0)) || (minReq > optionsWithoutBlanks.length && !allBlanksSelected)) {");
		sb.append("callbackFunction('MIN_NON_BLANK_ERROR', B64.decode(selectionName));");
		sb.append("partialResult = false;");
		sb.append("}");
		sb.append("return partialResult;");
		sb.append("}");
	}

	private static void createRelevantSelectionExtractingFunction(StringBuilder sb) {
		sb.append("getRelevantVotingOptions : function (votingOptions, selection){");
		sb.append("var selectionRelevantVotingOptions = [];");
		sb.append("for (i = 0; i < selection.length; i++) {");
		sb.append("if (votingOptions.indexOf(selection[i]) > -1) {");
		sb.append("selectionRelevantVotingOptions.push(selection[i]);");
		sb.append("}");
		sb.append("}");
		sb.append("return selectionRelevantVotingOptions;");
		sb.append("}");
	}

	private static void createIsFullBlankFunction(StringBuilder sb) {
		sb.append("isFullBlank : function (selection, allBlanks){");
		sb.append("var encounteredBlanks = [];");
		sb.append("var encounteredInAll = true;");
		sb.append("for (i = 0; i < allBlanks.length; i++) {");
		sb.append("var encounteredInSelection = false;");
		sb.append("for (j = 0; j < allBlanks[i].length; j++) {");
		sb.append("if (selection.indexOf(allBlanks[i][j]) > -1) {");
		sb.append("encounteredBlanks.push(allBlanks[i][j]);");
		sb.append("encounteredInSelection = true;");
		sb.append("}");
		sb.append("}");
		sb.append("encounteredInAll = encounteredInAll && encounteredInSelection");
		sb.append("}");
		sb.append("return encounteredBlanks.length == selection.length && encounteredInAll;");
		sb.append("}");
	}

	private static void createAreAllSelectionsUnique(StringBuilder sb) {
		sb.append("areAllSelectionsUnique: function (encodedSelection, callbackFunction) {");
		sb.append("var partialResult = true;");
		sb.append("var duplicates = encodedSelection.reduce(function(acc, el, i, arr) {");
		sb.append("if (arr.indexOf(el) !== i && acc.indexOf(el) < 0) acc.push(el); return acc;");
		sb.append("}, []);");
		sb.append("if (duplicates.length != 0) {");
		sb.append("callbackFunction('" + MULTIPLE_SELECTION_ERROR + "', B64.decode(encodedSelection[duplicates[0]]));");
		sb.append("partialResult = false;");
		sb.append("}");
		sb.append("return partialResult;");
		sb.append("}");
	}

	private static void createFusionCheck(StringBuilder sb) {
		sb.append("checkFusion: function (fusionVotingOptions, cumulNumber, encodedSelection, callbackFunction) {");
		sb.append("var partialResult = true;");
		sb.append("for (i = 0; i < encodedSelection.length; i++) {");
		sb.append("for (j = 0; j < fusionVotingOptions.length; j++) {");
		sb.append("if (fusionVotingOptions[j].indexOf(encodedSelection[i]) > -1){");
		sb.append("var count = 0;");
		sb.append("for (k = 0; k < fusionVotingOptions[j].length; k++) {");
		sb.append("if (encodedSelection.indexOf(fusionVotingOptions[j][k]) > -1) {");
		sb.append("count++;");
		sb.append("}");
		sb.append("}");
		sb.append("if (count > cumulNumber){");
		sb.append("callbackFunction('" + ACCUMULATION_ERROR + "', B64.decode(encodedSelection[i]));");
		sb.append("partialResult = false;");
		sb.append("}");
		sb.append("}");
		sb.append("}");
		sb.append("}");
		sb.append("return partialResult;");
		sb.append("}");
	}
}
