package inference.rewrite;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import models.formulas.EquationFormula;
import models.formulas.Formula;
import models.formulas.Then;
import models.terms.DependencyTerm;
import models.terms.EvaluatableTerm;
import models.terms.PrimedTerm;
import models.terms.Resource;

public class RewriteInferenceSystem {

	List<Formula> constraintFormulas = new ArrayList<>();
	List<Formula> invariantFormulas = new ArrayList<>();
	EquationFormula inputFormula;
	List<Formula> conditionalFormulas = new ArrayList<>();
	List<Formula> otherFormulas = new ArrayList<>();
	Formula conclusion;
	
	public RewriteInferenceSystem(List<Formula> assumptions, Formula conclusion) {
		for (Formula assumption : assumptions) {
			if (inputFormulaCheck(assumption)) {
				inputFormula = (EquationFormula) assumption;
			} else if(invariantFormulaCheck(assumption)) {
				invariantFormulas.add(assumption);
			} else if (assumption instanceof EquationFormula) {
				constraintFormulas.add(assumption);
			} else {
				otherFormulas.add(assumption);
			}
		}
		
		if (conclusion instanceof Then then) {
			conditionalFormulas.add(then.getCondition());
			this.conclusion = then.getResult();
		} else {
			this.conclusion = conclusion;
		}
		
	}
	
	public boolean inference() {
		Map<Resource, List<Resource>> resourceTree = new HashMap<>();
		Resource root = parseDependencyTerm(inputFormula.getLeftSideHand(), resourceTree, new ArrayList<>());
		return false;
	}
	
	private Resource parseDependencyTerm(EvaluatableTerm term, Map<Resource, List<Resource>> resourceTree, List<Resource> tops) {
		if (term instanceof Resource resource) {
			if (tops.isEmpty()) {
				resourceTree.put(resource, new ArrayList<>());
				tops.add(resource);
			} else {
				for (Resource top : tops) {
					resourceTree.get(top).add(resource);
					resourceTree.put(resource, new ArrayList<>());
				}
				tops.clear();
				tops.add(resource);
			}
			return resource;
		}
		Resource root;
		DependencyTerm depTerm = (DependencyTerm) term;
		EvaluatableTerm dependingTerm = depTerm.getDependingTerm();
		List<EvaluatableTerm> dependedTerms = depTerm.getDependedTerms();
		List<EvaluatableTerm> argumentTerms = depTerm.getArgumentTerms();
		root = parseDependencyTerm(dependingTerm, resourceTree, tops);
		List<Resource> nextTops = new ArrayList<>();
		for (int i = 0; i < dependedTerms.size(); i++) {
			List<Resource> currentTops = new ArrayList<>(tops);
			parseDependencyTerm(dependedTerms.get(i), resourceTree, currentTops);
			parseDependencyTerm(argumentTerms.get(i), resourceTree, currentTops);
			nextTops.addAll(currentTops);
		}
		tops.clear();
		tops.addAll(nextTops);
		return root;
	}
	
	
	private boolean inputFormulaCheck(Formula formula) {
		if (! (formula instanceof EquationFormula equation)) {
			return false;
		}
		List<Resource> resources  = new ArrayList<>(equation.getLeftSideHand().getSubTerms(Resource.class).values());
		resources.addAll(equation.getRightSideHand().getSubTerms(Resource.class).values());
		for (Resource resource : resources) {
			if (resource.getOrder() == 0) {
				return true;
			}
		}
		return false;
	}
	
	private boolean invariantFormulaCheck(Formula formula) {
		if (! (formula instanceof EquationFormula equation)) {
			return false;
		}
		EvaluatableTerm leftSideHand = equation.getLeftSideHand();
		EvaluatableTerm rightSideHand = equation.getRightSideHand();
		
		if (leftSideHand instanceof PrimedTerm) {
			return ((PrimedTerm) leftSideHand).getPrimedTerm().equals(rightSideHand);
		} else if (rightSideHand instanceof PrimedTerm) {
			return ((PrimedTerm) rightSideHand).getPrimedTerm().equals(leftSideHand);
		}
		
		return false;
	}
	
	
}
