package edu.fiu.jtlex.data;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;

/**
 * Class used to partition an input TimeML temporal graph into main subgraph and
 * subordinating subgraphs. TimeML graphs are split by trimming all instances of
 * ILinks that are of type SLinkType and then reassembling the graphs using
 * edges.
 * 
 * @author Emmanuel Garcia
 */

public interface IGraphPartitioner {
	static Set<ILink> singleLinks= new HashSet<>();
	/*
	 * For every node which does not exist in the visitedNodes set: 
	 * * Add them to the subgraph 
	 * * Add them to the visitedNodes set 
	 * * Follow the incoming/outgoing links to the neighboring nodes: 
	 * * Check if the incoming/outgoing links are already on the current graph, add them if not. 
	 * * Call the method recursively on the node's neighbors along with the visited  nodes set and the graph.
	 */
	private static IGraph DFS(INode node, Set<INode> visitedNodes, IGraph subgraph) {
		visitedNodes.add(node);
		subgraph.addNode(node);

		for (ILink link : node.getOutLinks()) {
			if (!subgraph.containsLink(link))
				subgraph.addLink(link);
			if (!visitedNodes.contains(link.getRelatedToEvent()))
				DFS(link.getRelatedToEvent(), visitedNodes, subgraph);
		}
		for (ILink link : node.getInLinks()) {
			if (!subgraph.containsLink(link))
				subgraph.addLink(link);
			if (!visitedNodes.contains(link.getEventInstance()))
				DFS(link.getEventInstance(), visitedNodes, subgraph);
		}

		return subgraph;
	}
	/**
	 * This method takes a TimeML graph and removes all the Subordination type links, then reassembles
	 * the graph with DFS and finally returns an array of the partitioned subgraphs.
	 * @param timeMLGraph
	 * @return ArrayList<IGraph>
	 */
	public static ArrayList<IGraph> partitionGraph(IGraph timeMLGraph) {
		Set<INode> visitedNodes = new HashSet<>();
		ArrayList<IGraph> subGraphArray = new ArrayList<>();
		singleLinks.clear();

		for (ILink link : timeMLGraph.getLinks()) {
			if(link.getRelatedToEvent().getIdStr().equals("eii80")) {
				System.out.println("big problemo");
			}

			if (link.getRelType() instanceof ILink.SLinkType) { 
				/*If the link is an SLINK, we need to check if it is alone for the purpose of partitioning
			      First: inspect all the links OUTGOING from the SOURCE node
			      If there is any other link which is not this one, mark singleSlink as FALSE.
			      Second: inspect all the links INCOMING to the source node.
			      If there is any other link which is not this one, mark singleSlink as FALSE.
				*/
				boolean singleSlink = true;
				for (ILink node_link : link.getEventInstance().getOutLinks()) {
					if(node_link.equals(link)) {
						continue;
					}
					if(node_link.getRelatedToEvent().equals(link.getRelatedToEvent())) {
						singleSlink = false;
						break;
					}
				}
				for (ILink node_link : link.getEventInstance().getInLinks()) {
					if (node_link.equals(link)) {
						continue;
					}
					if(node_link.getEventInstance().equals(link.getRelatedToEvent())) {
						singleSlink = false;
						break;
					}
				}
				if(singleSlink) {
					singleLinks.add(link);
					singleSlink = false;
				}
				timeMLGraph.removeLink(link);
			}
		}
		// Re-assemble the main subgraph/subordinating graphs using DFS
		for (INode node : timeMLGraph.getNodes()) {
			IGraph subgraph = new Graph();
			if (!visitedNodes.contains(node)) {
				subgraph = DFS(node, visitedNodes, subgraph);
				subGraphArray.add(subgraph);
			}
		}
		return subGraphArray;
	}
	
	/**
	 * classifyGraphs Finds the graph with the most nodes and classifies it as a MainGraph and set the others
	 * to a subordination graph
	 * 
	 * @param graphs
	 * @return ArrayList<IGraph>
	 */
	public static ArrayList<IGraph> classifyGraphs(ArrayList<IGraph> graphs) {
		Graph mainGraph = null;
		for (int i = 0; i < graphs.size()-1; i++) {
			if (graphs.get(i).getNodesCount() > graphs.get(i+1).getNodesCount()) {
				mainGraph = (Graph) graphs.get(i);
			}
		}
		
		mainGraph.setGraphType(GraphType.MainGraph);
		
		for (IGraph graph : graphs) {
			if (graph.getGraphType() != GraphType.MainGraph) {
				graph.setGraphType(GraphType.SubbordinationGraph);
			}
		}
		
		return graphs;
		
	}

	/**
	 * This method returns the set of partition links (solo subordination timelinks)
	 * that were obtained from partitioning a TimeML graph. This method can only be used AFTER
	 * the IGraphPartitioner.java has been used to partition a graph.
	 * @return Set<ILink> 
	 */
	public static Set<ILink> partitionLinks(){
		if (singleLinks.isEmpty()) {
			System.out.println("Please partition a TimeML Graph first.");
			return null;
		} else {
			return singleLinks;
		}
	}
}
