package com.hammurapi.reasoning.impl;

import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

import com.hammurapi.reasoning.ExceptionHandler;

public class AcceptMethodConditionNode<F> extends ConditionNode<F> {

	
	private List<Integer> parameterMap;
	
	public void setParameterMap(List<Integer> parameterMap) {
		this.parameterMap = parameterMap;
	}
	
	/**
	 * Evaluate condition as soon as all condition arguments are available.
	 */
	@SuppressWarnings("unchecked")
	@Override
	protected boolean partialJoin(List<com.hammurapi.reasoning.impl.InferenceToken<F>>[] inputs, int index) throws Exception {
		if (index==lastConditionPin) {
			Object[] cArgs = new Object[parameterTypes.length];
			Iterator<Integer> pmit = parameterMap.iterator();
			Z: for (int i=0; pmit.hasNext(); ++i) {
				Integer pIdx = pmit.next();
				for (int j=0; j<=index; ++j) {
					List<TokenInfo> pinConfig = (List<TokenInfo>) getInputPins().get(j).config;
					if (pinConfig.size()!=inputs[j].size()) {
						throw new IllegalArgumentException("Number of input tokens is different from number of token infos");
					}
					for (int k=0, l=pinConfig.size(); k<l; ++k) {
						TokenInfo ti = pinConfig.get(k);
						if (ti.getParameterIndex() == pIdx) {
							InferenceToken<F> it = inputs[j].get(k);
							if (it.isConsumed()) {
								return false;
							}
							
							cArgs[i] = knowledgeBase.get(it.getHandle());
							if (cArgs[i]==null) {
								return false;
							}
							continue Z;
						}
					}
				}				
			}
			
			try {
				return Boolean.TRUE.equals(method.invoke(rule, cArgs));
			} catch (Exception e) {
				ExceptionHandler exceptionHandler = knowledgeBase.getExceptionHandler();
				if (exceptionHandler==null) {
					throw e;
				}
				exceptionHandler.handleException(e);
				return false;
			}
		}
		
		return true;
	}
	
	private boolean hasConditionArgument(PinEntry<F> pin) {
		for (Object o: pin.config) {
			TokenInfo ti = (TokenInfo) o;
			for (Integer pIdx: parameterMap) {
				if (pIdx==ti.getParameterIndex()) {
					return true;
				}
			}
		}
		
		return false;
	}
	
	private int lastConditionPin=-1;

	/**
	 * Sort input pins to move pins with conditon arguments to first positions.
	 */
	@Override
	protected void sortInputs() {
		Collections.sort(getInputPins(), new Comparator<PinEntry<F>>() {
			
			@Override
			public int compare(PinEntry<F> pe1, PinEntry<F> pe2) {
				if (hasConditionArgument(pe1)) {
					if (hasConditionArgument(pe2)) {
						return pe1.index-pe2.index;
					}
					return -1; // pe1 shall be before pe2
				}
				
				if (hasConditionArgument(pe2)) {
					return 1; // pe1 shall be after pe2.
				}
				
				return pe1.index - pe2.index;
			}
		});
		
		for (int i=0; i<getInputPins().size(); ++i) {
			if (hasConditionArgument(getInputPins().get(i))) {
				lastConditionPin = i;
			}
		}
	}

}
