package gr.uom.java.ast.decomposition.cfg.mapping;

import gr.uom.java.ast.decomposition.cfg.AbstractVariable;
import gr.uom.java.ast.decomposition.cfg.CFGBranchNode;
import gr.uom.java.ast.decomposition.cfg.PDGAbstractDataDependence;
import gr.uom.java.ast.decomposition.cfg.PDGControlDependence;
import gr.uom.java.ast.decomposition.cfg.PDGDependence;
import gr.uom.java.ast.decomposition.matching.ASTNodeMatcher;

public class PDGEdgeMapping {
	private PDGDependence edgeG1;
	private PDGDependence edgeG2;
	private volatile int hashCode = 0;
	
	public PDGEdgeMapping(PDGDependence edgeG1, PDGDependence edgeG2) {
		this.edgeG1 = edgeG1;
		this.edgeG2 = edgeG2;
	}

	public PDGDependence getEdgeG1() {
		return edgeG1;
	}

	public PDGDependence getEdgeG2() {
		return edgeG2;
	}

	public boolean isCompatible(PDGNodeMapping nodeMapping) {
		if(edgeG1.getClass() == edgeG2.getClass()) {
			if(edgeG1 instanceof PDGControlDependence) {
				PDGControlDependence controlG1 = (PDGControlDependence)edgeG1;
				PDGControlDependence controlG2 = (PDGControlDependence)edgeG2;
				return controlG1.sameLabel(controlG2);
			}
			if(edgeG1 instanceof PDGAbstractDataDependence) {
				AbstractVariable edgeG1Variable = ((PDGAbstractDataDependence)edgeG1).getData();
				AbstractVariable edgeG2Variable = ((PDGAbstractDataDependence)edgeG2).getData();
				CFGBranchNode edgeG1LoopNode = ((PDGAbstractDataDependence)edgeG1).getLoop();
				CFGBranchNode edgeG2LoopNode = ((PDGAbstractDataDependence)edgeG2).getLoop();
				if(equalData(edgeG1Variable, edgeG2Variable) || nodeMapping.matchingVariableDifference(edgeG1Variable, edgeG2Variable)) {
					if(edgeG1LoopNode == null && edgeG2LoopNode == null) {
						return true;
					}
					else if(edgeG1LoopNode != null && edgeG2LoopNode != null) {
						ASTNodeMatcher astNodeMatcher = new ASTNodeMatcher(nodeMapping.getTypeRoot1(), nodeMapping.getTypeRoot2());
						boolean match = astNodeMatcher.match(edgeG1LoopNode.getPDGNode(), edgeG2LoopNode.getPDGNode());
						return match && astNodeMatcher.isParameterizable();
					}
				}
			}
		}
		return false;
	}
	
	private boolean equalData(AbstractVariable edgeG1Variable, AbstractVariable edgeG2Variable) {
		if(edgeG1Variable.getClass() == edgeG2Variable.getClass()) {
			return edgeG1Variable.toString().equals(edgeG2Variable.toString()) &&
					edgeG1Variable.getVariableType().equals(edgeG2Variable.getVariableType()) &&
					edgeG1Variable.isField() == edgeG2Variable.isField() &&
					edgeG1Variable.isParameter() == edgeG2Variable.isParameter();
		}
		return false;
	}

	public boolean equals(Object o) {
		if(this == o)
			return true;
		if(o instanceof PDGEdgeMapping) {
			PDGEdgeMapping mapping = (PDGEdgeMapping)o;
			return this.edgeG1.equals(mapping.edgeG1) &&
					this.edgeG2.equals(mapping.edgeG2);
		}
		return false;
	}

	public int hashCode() {
		if(hashCode == 0) {
			int result = 17;
			result = 37*result + edgeG1.hashCode();
			result = 37*result + edgeG2.hashCode();
			hashCode = result;
		}
		return hashCode;
	}

	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append(edgeG1);
		sb.append(edgeG2);
		return sb.toString();
	}
}
