package de.dhbwstuttgart.typeinference.unify;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import de.dhbwstuttgart.syntaxtree.factory.UnifyTypeFactory;
import de.dhbwstuttgart.typeinference.constraints.ConstraintSet;
import de.dhbwstuttgart.typeinference.result.ResultSet;
import de.dhbwstuttgart.typeinference.unify.interfaces.IFiniteClosure;
import de.dhbwstuttgart.typeinference.unify.model.PairOperator;
import de.dhbwstuttgart.typeinference.unify.model.UnifyPair;

public class UnifyResultModel {
	
	ConstraintSet<de.dhbwstuttgart.typeinference.constraints.Pair> cons;
	
	IFiniteClosure fc;
	
	public UnifyResultModel(ConstraintSet<de.dhbwstuttgart.typeinference.constraints.Pair> cons,
					 IFiniteClosure fc) {
		this.cons = cons;
		this.fc = fc;
	}
	
	private List<UnifyResultListener> listeners = new ArrayList<>();
	
	public void addUnifyResultListener(UnifyResultListener listenerToAdd) {
		listeners.add(listenerToAdd);
	}

	public void removeUnifyResultListener(UnifyResultListener listenerToRemove) {
		listeners.remove(listenerToRemove);
	}
	
	public void notify(Set<Set<UnifyPair>> eqPrimePrimeSet) {
		Set<Set<UnifyPair>> eqPrimePrimeSetRet = eqPrimePrimeSet.stream().map(x -> {
			Optional<Set<UnifyPair>> res = new RuleSet().subst(x.stream().map(y -> { 
				if (y.getPairOp() == PairOperator.SMALLERDOTWC) y.setPairOp(PairOperator.EQUALSDOT);
				return y; //alle Paare a <.? b erden durch a =. b ersetzt
			}).collect(Collectors.toCollection(HashSet::new)));
			if (res.isPresent()) {//wenn subst ein Erg liefert wurde was veraendert
				return new TypeUnifyTask().applyTypeUnificationRules(res.get(), fc);
			}
			else return x; //wenn nichts veraendert wurde wird x zurueckgegeben
		}).collect(Collectors.toCollection(HashSet::new));
		List<ResultSet> newResult = eqPrimePrimeSetRet.stream().map(unifyPairs ->
           new ResultSet(UnifyTypeFactory.convert(unifyPairs, de.dhbwstuttgart.typeinference.constraints.Pair.generateTPHMap(cons))))
           .collect(Collectors.toList());
		UnifyResultEvent evt = new UnifyResultEvent(newResult);
		
		for (UnifyResultListener listener : listeners) {
			listener.onNewTypeResultFound(evt);
		}
	}
}
