package dfatool.expressions;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import dfatool.operations.SetOperations;
import dfatool.strategy.elements.Context;
import dfatool.values.BoolValue;
import dfatool.values.SetValue;
import dfatool.values.ListValue;
import dfatool.values.Value;

public class ComprehensionExpression extends ListExpression{
	ListExpression enumExp;
	List<SetExtension> listExt;
	List<BinaryExpression> listBool;
	
	public ComprehensionExpression(ListExpression expEnum, List<SetExtension> listExt, List<BinaryExpression> listBool) {
		this.enumExp = expEnum;
		this.listExt = listExt;
		this.listBool = listBool;
		this.type = TypeConstants.SETCOMP;
	}
	
	public ListExpression getSetExp() {
		return enumExp;
	}

	public void setSetExp(ListExpression setExp) {
		this.enumExp = setExp;
	}

	public List<SetExtension> getListExt() {
		return listExt;
	}

	public void setListExt(List<SetExtension> listExt) {
		this.listExt = listExt;
	}

	public List<BinaryExpression> getListBool() {
		return listBool;
	}

	public void setListBool(List<BinaryExpression> listBool) {
		this.listBool = listBool;
	}

	@Override
	public String toString() {
		return "{ "+enumExp.toString()+" | "+listExt.toString()+" }";
	}
	
	@Override
	public Value evaluate(Context c) {
		List<String> lIds = new ArrayList<String>();
		Set<List<Value>> s = generateIdsAndValuesForBiding(c, lIds);
		Context lc = new Context(c);
		ListValue ret = new ListValue();
		for(List<Value> e : s){
			createLocalContext(lIds, lc, e);
			boolean b = evaluateBoolExpressions(lc);
			if(b){
				ret.addAll((ListValue) this.enumExp.evaluate(lc));
			}
		}
		return ret;
	}

	private boolean evaluateBoolExpressions(Context lc) {
		Iterator<BinaryExpression> it = listBool.iterator();
		boolean hold = true;
		while(it.hasNext() && hold) hold = hold && ((BoolValue) it.next().evaluate(lc)).getValue();
		return hold;
	}

	private void createLocalContext(List<String> lIds, Context lc, List<Value> e) {
		Iterator<String> itIds = lIds.iterator();
		Iterator<Value> itValues = e.iterator();
		while(itIds.hasNext() && itValues.hasNext()){
			lc.getVarMap().put(itIds.next(), itValues.next());				
		}
	}

	private Set<List<Value>> generateIdsAndValuesForBiding(Context c, List<String> lIds) {
		List<Set<Value>> lset = new ArrayList<Set<Value>>();
		for(SetExtension e : listExt) {
			lIds.add(e.getName().getId());
			lset.add((SetValue) e.getSet().evaluate(c));
		}
		Set<List<Value>> s = SetOperations.caculateCartesianProduct(lset);
		return s;
	}
	@Override
	public boolean checkType(Context c) {
//		boolean setWellTyped = true;
//		boolean listMemWellTyped = true;
//		boolean listBoolWellTyped = true;
//		Iterator<Expression> i = setExp.iterator();
//		while(setWellTyped && i.hasNext()) setWellTyped = i.next().checkType(c);
//		if (!setWellTyped) return false;
//		i = listExt.iterator();
//		while(listMemWellTyped && i.hasNext()) listMemWellTyped = i.next().checkType(c);
//		if (!listMemWellTyped) return false;
//		i = listBool.iterator();
//		while(listBoolWellTyped && i.hasNext()) listBoolWellTyped = i.next().checkType(c);
//		if (!listBoolWellTyped) return false;
		return true;
	}

	@Override
	public String toConcreteSyntax() {
		// TODO Auto-generated method stub
		return null;
	}
	

}
