/**
 * <copyright>
 * Copyright (c) 2010-2014 Henshin developers. All rights reserved. 
 * This program and the accompanying materials are made available 
 * under the terms of the Eclipse Public License v1.0 which 
 * accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * </copyright>
 */
package org.eclipse.emf.henshin.interpreter.matching.conditions;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

import javax.script.ScriptEngine;

import org.eclipse.emf.henshin.model.util.ScriptEngineWrapper;

/**
 * Condition handler.
 * 
 * @author Enrico Biermann, Christian Krause
 */
public class ConditionHandler {
	
	// Attribute conditions:
	final Collection<AttributeCondition> attributeConditions;
	
	// Involved conditions:
	final Map<String, Collection<AttributeCondition>> involvedConditions;
	
	// Assigned parameters:
	final Collection<String> assignedParameters;

	// Used script engine:
	final ScriptEngineWrapper scriptEngine;

	/**
	 * Default constructor.
	 * @param conditionParameters Condition parameters.
	 * @param scriptEngine Script engine,
	 * @param localJavaImports 
	 * @param javaImports 
	 */
	public ConditionHandler(
			Map<String, Collection<String>> conditionParameters,
			ScriptEngineWrapper scriptEngine, List<String> localJavaImports) {
		
		this.attributeConditions = new ArrayList<AttributeCondition>();
		this.involvedConditions = new HashMap<String, Collection<AttributeCondition>>();
		this.assignedParameters = new HashSet<String>();
		this.scriptEngine = scriptEngine;

		for (String condition : conditionParameters.keySet()) {
			Collection<String> usedParameters = conditionParameters.get(condition);
			AttributeCondition attCondition = new AttributeCondition(condition, usedParameters, scriptEngine, localJavaImports);
			attributeConditions.add(attCondition);

			// Create a map for easy lookup of conditions a parameter is involved in:
			for (String usedParameter : usedParameters) {
				Collection<AttributeCondition> conditionList = involvedConditions.get(usedParameter);
				if (conditionList == null) {
					conditionList = new ArrayList<AttributeCondition>();
					involvedConditions.put(usedParameter, conditionList);
				}
				conditionList.add(attCondition);
			}
		}
	}

	public ConditionHandler(HashMap<String, Collection<String>> conditionParameters, ScriptEngine engine) {
		this(conditionParameters, new ScriptEngineWrapper(engine, new String[0]), Collections.emptyList());
	}

	/**
	 * Set the value for a parameter.
	 * @param paramName Parameter name.
	 * @param value Value.
	 * @return <code>true</code> if it was set.
	 */
	public boolean setParameter(String paramName, Object value) {
		boolean result = true;
		if (assignedParameters.add(paramName)) {
			scriptEngine.put(paramName, value);
			Collection<AttributeCondition> conditionList = involvedConditions.get(paramName);
			if (conditionList != null) {
				for (AttributeCondition condition : conditionList) {
					condition.parameters.remove(paramName);
					result = result && condition.eval();
				}
			}
		}
		return result;
	}

	/**
	 * Unset a parameter value.
	 * @param paramName Parameter name.
	 */
	public void unsetParameter(String paramName) {
		if (assignedParameters.remove(paramName)) {
			Collection<AttributeCondition> conditionList = involvedConditions.get(paramName);
			if (conditionList != null) {
				for (AttributeCondition condition : involvedConditions.get(paramName)) {
					condition.parameters.add(paramName);
				}
			}
		}
	}

	/**
	 * Check whether a parameter is set.
	 * @param paramName Parameter name.
	 * @return <code>true</code> if it is set.
	 */
	public boolean isSet(String paramName) {
		return assignedParameters.contains(paramName);
	}

	/**
	 * Get the value for a parameter.
	 * @param paramName Name of the parameter.
	 * @return The value.
	 */
	public Object getParameter(String paramName) {
		return scriptEngine.get(paramName);
	}

	/**
	 * Get all parameter values.
	 * @return Map with all parameter values.
	 */
	public Map<String, Object> getParameterValues() {
		Map<String, Object> paramValues = new HashMap<String, Object>();
		for (String paramName : assignedParameters) {
			paramValues.put(paramName, scriptEngine.get(paramName));
		}
		return paramValues;
	}
}
