package edu.fiu.jtlex.data;
/**
 * A TimeML graph might not be solvable when formulated as a TCSP because of inconsistencies
 * in the graph. Among these inconsistencies we find self loops, 2 nodes inconsistencies etc.
 * InconsistencyDetector provide static methods to verify the consistency of a graph and also,
 * in the case of incosnistencies, generate the subgraphs of inconsistencies.
 * 
 * @author Luis Robaina, Emmanuel Garcia
 */
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import java.util.List;

public class InconsistencyDetector {
	
	/**
	 * A static method to verify the consistency of a graph by trying to solve the TCSP 
	 * presented by the graphs links and nodes. If there is a solution to the TCSP then the 
	 * graph is consistent, otherwise is inconsistent.
	 * @param TimeMLGraph A timeML graph object with a non empty set of nodes.
	 * @return true if the graph is consistent, false if the graph is inconsistent.
	 */
	public static boolean isConsistent(IGraph TimeMLGraph) {
		GraphTCSP tcsp = new GraphTCSP(TimeMLGraph);
		if (tcsp.solve() != null) {
			return true;
		}
		return false;
	}
	
	/**
	 * Given a TimeML graph with inconcistencies, generate a set of subgraphs
	 * that contain all the links and nodes involved in the inconcistencies that are 
	 * present on the TimeML graph. 
	 * @param TimeMLGraph A TimeML graph.
	 * @return A Set of IGraph objects representing the inconsistent subgraphs or null if no inconcistencies exists.
	 */
	public static Set<IGraph> generateInconsistentSubgraphs(IGraph TimeMLGraph) {

		// verify at least one inconsistency exists.
		if(isConsistent(TimeMLGraph)) {
			return null; // no inconsistencies in this graph.
		}
		
		ArrayList<ILink> consistent_links = new ArrayList<>();
		ArrayList<ILink> inconsistent_links = new ArrayList<>();

		Set<ILink> links = TimeMLGraph.getLinks();
		Set<INode> nodes = TimeMLGraph.getNodes();

		IGraph subGraph = new Graph();
		for (INode node : nodes) {
			subGraph.addNode(node);
		}
		// Find at least one link per inconsistent cycle.
		for (ILink link : links) {
			// System.out.println("Added: " + link.getEventInstance().getIdStr() +
			// link.getRelType() + link.getRelatedToEvent().getIdStr());
			subGraph.addLink(link);
			consistent_links.add(link);

			if (!isConsistent(subGraph)) {
				consistent_links.remove(link);
				inconsistent_links.add(link);
				subGraph.removeLink(link);
			}
		}
		// Find all other links in each inconsistent subgraph.
		Set<IGraph> inconsistentSubgraphs = new HashSet<>();
		
		for (ILink i_link : inconsistent_links) {

			IGraph inconsistentSubGraph = new Graph();
			inconsistentSubGraph.addLink(i_link);
			inconsistentSubGraph.addNode(i_link.getEventInstance());
			inconsistentSubGraph.addNode(i_link.getRelatedToEvent());

			// Fix this inconsistent link in the graph.
			subGraph.addLink(i_link);

			consistent_links.add(i_link);
			// Test if the graph becomes consistent by removing one link at a time.
			for (ILink c_link : consistent_links) {
				if (c_link == i_link) {
					continue;
				}
				subGraph.removeLink(c_link);
				if (isConsistent(subGraph)) {
					// This c_link is an inconsistent link.
					inconsistentSubGraph.addNode(c_link.getEventInstance());
					inconsistentSubGraph.addNode(c_link.getRelatedToEvent());
					inconsistentSubGraph.addLink(c_link);
				}
				subGraph.addLink(c_link);
			}
			subGraph.removeLink(i_link);
			inconsistentSubgraphs.add(inconsistentSubGraph);
			consistent_links.remove(i_link);
		}

		return inconsistentSubgraphs;
	}
}
