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

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

import org.slf4j.Logger;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.scytl.products.ov.commons.correctness.builder.CorrectnessBuilder;
import com.scytl.products.ov.commons.correctness.builder.CorrectnessUtil;
import com.scytl.products.ov.commons.correctness.feedback.CorrectnessFeedback;
import com.scytl.products.ov.commons.correctness.feedback.ReportedError;

public abstract class CorrectnessEvaluator<T> {
	private static final String JAVA_SCRIPT_ENGINE_NAME = "JavaScript";
	private final ScriptEngineManager factory;
	private final ScriptEngine engine;
	private final Logger logger;
	private static final CorrectnessFeedback GENERIC_SCRIPT_EXECUTION_ERROR;
	final ObjectMapper mapper = new ObjectMapper();
	private boolean isBase64Encoded = true;
	static {
		GENERIC_SCRIPT_EXECUTION_ERROR = new CorrectnessFeedback();
		GENERIC_SCRIPT_EXECUTION_ERROR.setResult(Boolean.FALSE);
	}

	public CorrectnessEvaluator(Logger logger) {
		this.logger = logger;
		factory = new ScriptEngineManager();
		engine = factory.getEngineByName(JAVA_SCRIPT_ENGINE_NAME);
	}

	public CorrectnessEvaluator(Logger logger, boolean isBase64Encoded) {
		this.logger = logger;
		this.isBase64Encoded = isBase64Encoded;
		factory = new ScriptEngineManager();
		engine = factory.getEngineByName(JAVA_SCRIPT_ENGINE_NAME);
	}

	public CorrectnessEvaluator(Logger logger, CorrectnessBuilder correctness, boolean isBase64Encoded) throws ScriptException {
		this.logger = logger;
		this.isBase64Encoded = isBase64Encoded;
		factory = new ScriptEngineManager();
		engine = factory.getEngineByName(JAVA_SCRIPT_ENGINE_NAME);
		engine.eval(CorrectnessFeedback.decorateRule(correctness, isBase64Encoded));
	}

	public CorrectnessEvaluator(Logger logger, String correctness) throws ScriptException {
		this.logger = logger;
		factory = new ScriptEngineManager();
		engine = factory.getEngineByName(JAVA_SCRIPT_ENGINE_NAME);
		engine.eval(correctness);
	}

	public CorrectnessFeedback checkCorrectness(Collection<T> selections, CorrectnessBuilder correctness) {
		try {
			engine.eval(CorrectnessFeedback.decorateRule(correctness, isBase64Encoded));
			String returnable = (String) ((Invocable) engine)
					.invokeFunction(CorrectnessUtil.EVALUATE_CORRECTNESS_WITH_FEEDBACK, selections);
			return convertResponse(returnable);
		} catch (ScriptException | IOException | NoSuchMethodException ex) {
			logger.error(ex.getMessage(), ex);
		}
		return GENERIC_SCRIPT_EXECUTION_ERROR;
	}

	private CorrectnessFeedback convertResponse(String returnable)
			throws IOException, JsonParseException, JsonMappingException {
		final ObjectNode node = mapper.readValue(returnable, ObjectNode.class);
		CorrectnessFeedback readValue = mapper.readValue(returnable, CorrectnessFeedback.class);
		JsonNode jsonNode = node.get("errors");
		ReportedError[] errors = mapper.readValue(jsonNode.toString(), ReportedError[].class);
		if (errors != null && errors.length != 0) {
			List<ReportedError> errorList = new ArrayList<>();
			for (ReportedError errors2 : errors) {
				errorList.add(errors2);
			}
			readValue.setErrors(errorList);
		}
		return readValue;
	}

	public CorrectnessFeedback checkCorrectness(Collection<T> selections) {
		try {
			String returnable = (String) ((Invocable) engine)
					.invokeFunction(CorrectnessUtil.EVALUATE_CORRECTNESS_WITH_FEEDBACK, selections);
			return convertResponse(returnable);
		} catch (ScriptException | IOException | NoSuchMethodException ex) {
			logger.error(ex.getMessage(), ex);
		}
		return GENERIC_SCRIPT_EXECUTION_ERROR;
	}

	
}
