/**
 * 
 */
package de.dhbwstuttgart.bytecode.genericsGenerator;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;

import de.dhbwstuttgart.bytecode.constraint.TPHConstraint;
import de.dhbwstuttgart.bytecode.constraint.TPHConstraint.Relation;
import de.dhbwstuttgart.bytecode.genericsGeneratorTypes.Cycle;

/**
 * @author fayez
 *
 */
public class CyclesFinder {
	private final List<TPHConstraint> allCons;

	public CyclesFinder(List<TPHConstraint> allCons) {
		this.allCons = allCons;
	}

	public List<Cycle> findCycles() {
		List<TPHConstraint> vistedConstraints = new ArrayList<>(allCons.size());
		List<Cycle> cycles = new ArrayList<>();
		for (TPHConstraint constraint : allCons) {
			if(constraint.getRel()==Relation.EQUAL)
				continue;
			if (!vistedConstraints.contains(constraint)) {
				vistedConstraints.add(constraint);
				checkConstraint(constraint, vistedConstraints, cycles);
			}
		}
		return cycles;
	}

	private void checkConstraint(TPHConstraint constraint, List<TPHConstraint> vistedConstraints,
			List<Cycle> cycles) {
		List<String> tphsInRelation = new LinkedList<>();
		List<TPHConstraint> maybeCycle = new ArrayList<>();
		maybeCycle.add(constraint);
		tphsInRelation.add(constraint.getLeft());
		tphsInRelation.add(constraint.getRight());
		Optional<TPHConstraint> nextConstraint = getConstraintInRelation(tphsInRelation, maybeCycle);
		while (nextConstraint.isPresent()) {
			if(containsInLongCycle(nextConstraint.get(), cycles)) {
				break;
			}
			if (isCycle(tphsInRelation)) {
				addAllToVisitedConstraints(vistedConstraints, maybeCycle);
				Cycle cycle = new Cycle(maybeCycle);
				cycles.add(cycle);
				return;
			}
			nextConstraint = getConstraintInRelation(tphsInRelation, maybeCycle);
		}

//		addAllToVisitedConstraints(vistedConstraints, maybeCycle);
	}

	private boolean containsInLongCycle(TPHConstraint c, List<Cycle> cycles) {
		for(Cycle cycle : cycles) {
			if(cycle.containConstraint(c))
				return true;
		}
		return false;
	}

	private void addAllToVisitedConstraints(List<TPHConstraint> vistedConstraints, List<TPHConstraint> maybeCycle) {
		for (TPHConstraint con : maybeCycle) {
			if (!vistedConstraints.contains(con))
				vistedConstraints.add(con);
		}
	}

	private Optional<TPHConstraint> getConstraintInRelation(List<String> tphsInRelation,
			List<TPHConstraint> maybeCycle) {
		TPHConstraint constraint = getConstraintByLeftSide(tphsInRelation.get(tphsInRelation.size()-1),maybeCycle);
		Optional<TPHConstraint> nextConstraint = Optional.ofNullable(constraint);
		if(nextConstraint.isPresent()) {
			maybeCycle.add(constraint);
			tphsInRelation.add(constraint.getRight());
		}
		return nextConstraint;
	}

	private TPHConstraint getConstraintByLeftSide(String left, List<TPHConstraint> maybeCycle) {
		for(TPHConstraint constraint : allCons) {
			if(constraint.getRel()==Relation.EQUAL)
				continue;
			if(maybeCycle.contains(constraint))
				continue;
			if(constraint.getLeft().equals(left))
				return constraint;
		}
		return null;
	}

	private boolean isCycle(List<String> tphsInRelation) {
		return tphsInRelation.get(0).equals(tphsInRelation.get(tphsInRelation.size() - 1));
	}

}
