/*
 * 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.options;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Base64.Encoder;

import com.scytl.products.ov.commons.correctness.builder.CorrectnessBuilder;
import com.scytl.products.ov.commons.correctness.builder.CorrectnessUtil;

import java.util.List;
import java.util.Scanner;

public class VotingOptionCorrectnessBuilder implements CorrectnessBuilder{
	
	private List<SelectionCorrectnessBuilder> selectionCorrectnessBuilders = new ArrayList<>();
	
	private static final String B64_ENCODER;

	private boolean fullBlank = false;

	static {
		try (Scanner s = new Scanner(VotingOptionCorrectnessBuilder.class.getResourceAsStream("/b64.js")).useDelimiter("\\A")){
			B64_ENCODER = s.hasNext() ? s.next() : "";
		}
	}
	
	public SelectionCorrectnessBuilder withSelectionCorrectness(String selectionId) {
		SelectionCorrectnessBuilder selectionCorrectnessBuilder = new SelectionCorrectnessBuilder(this, selectionId);
		selectionCorrectnessBuilders.add(selectionCorrectnessBuilder);
		return selectionCorrectnessBuilder;
	}



	@Override
	public String build() {
		return build(false);
	}

	private String build(boolean isBase64Encoded) {
		StringBuilder sb = new StringBuilder();
		sb.append("function " + CorrectnessUtil.EVALUATE_CORRECTNESS + "(selection, callbackFunction){"
				+ "if (typeof(callbackFunction)==='undefined') { callbackFunction = function (type, reference, extra) {};};");
		if(isBase64Encoded) {
			sb.append(B64_ENCODER);
		} else {
			sb.append(createFakeBase64Encoder());
		}
		sb.append("var encodedSelection = [];");
		sb.append("for (i = 0; i < selection.length; i++) {");
		sb.append("encodedSelection.push(B64.encode(selection[i]));");
		sb.append("}");
		CorrectnessUtil.createCorrectnessFunctions(sb);
		sb.append("var allBlanks = [");
		Encoder encoder = Base64.getUrlEncoder();
		for (int i = 0; i < selectionCorrectnessBuilders.size(); i++) {
			List<String> blanks = selectionCorrectnessBuilders.get(i).getBlanks();
			sb.append("[");
			for (int j = 0; j < blanks.size(); j++) {
				String value = blanks.get(j);
				if(isBase64Encoded) {
					value = encoder.encodeToString(value.getBytes(StandardCharsets.UTF_8));
				}
				sb.append("'").append(value).append("'");
				if (j < blanks.size() - 1) {
					sb.append(", ");
				}
			}
			sb.append("]");
			if (i < selectionCorrectnessBuilders.size() - 1) {
				sb.append(", ");
			}
		}
		sb.append("];");
		sb.append("var allBlanksSelected = CorrectnessFunctions.isFullBlank(encodedSelection, allBlanks);");
		
		sb.append("var result = true;");
		for (int i = 0; i < selectionCorrectnessBuilders.size(); i++) {
			sb.append("result = ").append(selectionCorrectnessBuilders.get(i).build(fullBlank, isBase64Encoded)).append(" && result;");
		}
		sb.append(";");

		if (!selectionCorrectnessBuilders.isEmpty()) {
			addCheckForSelectedVotingOptionsAreValid(sb, isBase64Encoded);
		}
		
		sb.append("return result;");
		sb.append("}");
		return sb.toString();
	}
	
	private String createFakeBase64Encoder() {
		return "var B64 = {encode: function (r) {return r;},decode: function (r) {return r;}};";
	}

	@Override
	public String buildWithSafeEncoding() {
		return build(true);
	}

	private void addCheckForSelectedVotingOptionsAreValid(StringBuilder sb, boolean isBase64Encoded) {
		List<String> votingOptions = new ArrayList<>();
		for (SelectionCorrectnessBuilder selectionCorrectnessBuilder : selectionCorrectnessBuilders) {
			votingOptions.addAll(selectionCorrectnessBuilder.getVotingOptions());
		}
		sb.append("var globalVotingOptions = [");
		Encoder encoder = Base64.getUrlEncoder();
		for (int i = 0; i < votingOptions.size(); i++) {
			String value = votingOptions.get(i);
			if(isBase64Encoded) {
				value = encoder.encodeToString(votingOptions.get(i).getBytes(StandardCharsets.UTF_8));
			}
			sb.append("'").append(value).append("'");
			if (i < votingOptions.size() - 1) {
				sb.append(", ");
			}
		}
		sb.append("];");
		sb.append("result = CorrectnessFunctions.areAllSelectionsUnique(encodedSelection, callbackFunction) && result;");
	}



	public VotingOptionCorrectnessBuilder withFullBlankEnabled(boolean fullBlank) {
		this.fullBlank = fullBlank;
		return this;
	}

	
}
