package edu.iastate.cs.egroum.aug;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.Optional;

import org.eclipse.jdt.core.dom.ASTNode;

import static edu.iastate.cs.egroum.aug.EGroumDataEdge.Type.*;

public class EGroumControlNode extends EGroumNode {

	public EGroumControlNode(EGroumNode control, String branch, ASTNode astNode, int nodeType) {
		super(astNode, nodeType);
		this.control = control;
		new EGroumControlEdge(control, this, branch);
	}

	public EGroumControlNode(EGroumControlNode node) {
		super(node.astNode, node.astNodeType);
		if (node.control != null)
			this.control = node.control;
	}

	@Override
	public String getLabel() {
		return ASTNode.nodeClassForType(astNodeType).getSimpleName();
	}

	@Override
	public boolean isDefinition() {
		return false;
	}

	@Override
	public Optional<String> getAPI() {
		return Optional.empty();
	}

	@Override
	public String toString() {
		return getLabel();
	}
	
	@Override
	public boolean isSame(EGroumNode node) {
		if (node instanceof EGroumControlNode)
			return astNodeType == node.astNodeType;
		return false;
	}
	
	@Override
	public void buildControlClosure(HashSet<EGroumNode> doneNodes) {
		for (EGroumEdge e : new HashSet<EGroumEdge>(this.inEdges)) {
			if (e instanceof EGroumControlEdge) {
				if (!doneNodes.contains(e.source))
					e.source.buildControlClosure(doneNodes);
				for (EGroumEdge e1 : e.source.inEdges) {
					if (!this.hasInEdge(e1)) {
						if (e1 instanceof EGroumControlEdge)
							new EGroumControlEdge(e1.source, this, ((EGroumControlEdge) e1).label);
						else
							new EGroumDataEdge(e1.source, this, ((EGroumDataEdge) e1).type, ((EGroumDataEdge) e1).label);
					}
				}
			}
		}
		doneNodes.add(this);
	}

	protected void buildConditionClosure() {
		HashSet<EGroumNode> conditionNodes = new HashSet<>();
		for (EGroumEdge edge : inEdges) {
			if (!(edge instanceof EGroumDataEdge) || ((EGroumDataEdge) edge).type != CONDITION)
				continue;
			LinkedList<EGroumNode> nodes = new LinkedList<>();
			nodes.add(edge.source);
			while (!nodes.isEmpty()) {
				EGroumNode node = nodes.removeFirst();
				conditionNodes.add(node);
				for (EGroumEdge e : node.inEdges) {
					if (e instanceof EGroumDataEdge && !conditionNodes.contains(e.source)) {
						EGroumDataEdge de = (EGroumDataEdge) e;
						if (de.type == PARAMETER || de.type == QUALIFIER || de.type == RECEIVER)
							nodes.add(de.source);
					}
				}
			}
		}
		for (EGroumNode node : conditionNodes)
			if (node instanceof EGroumActionNode && !this.hasInNode(node))
				new EGroumDataEdge(node, this, CONDITION, getConditionLabel());
	}

	public boolean controlsAnotherNode(EGroumNode node) {
		EGroumEdge e = this.outEdges.get(getOutEdgeIndex(node));
		for (EGroumEdge oe : this.outEdges) {
			if (oe.getLabel().equals(e.getLabel()) && !oe.target.getLabel().equals(node.getLabel()))
				return true;
		}
		
		return false;
	}
}
