package org.maltparser.parser.algorithm.planar3T;


import org.maltparser.core.exception.MaltChainedException;
import org.maltparser.core.syntaxgraph.DependencyStructure;
import org.maltparser.core.syntaxgraph.LabelSet;
import org.maltparser.core.syntaxgraph.node.DependencyNode;
import org.maltparser.parser.DependencyParserConfig;
import org.maltparser.parser.Oracle;
import org.maltparser.parser.ParserConfiguration;
import org.maltparser.parser.algorithm.planar.Planar;
import org.maltparser.parser.algorithm.planar.PlanarConfig;
import org.maltparser.parser.history.GuideUserHistory;
import org.maltparser.parser.history.action.GuideUserAction;
import org.maltparser.core.symbol.SymbolTable;
import org.maltparser.core.symbol.SymbolTableHandler;
import org.maltparser.core.symbol.trie.Trie;
import org.maltparser.core.symbol.trie.TrieSymbolTable;
import java.util.Set;
import java.util.Stack;
import java.util.SortedSet;
import org.maltparser.core.syntaxgraph.edge.Edge;
import org.maltparser.core.syntaxgraph.node.TokenNode;
import java.util.Iterator;

public class Planar3TArcEagerOracle extends Oracle {

	public Planar3TArcEagerOracle(DependencyParserConfig manager, GuideUserHistory history) throws MaltChainedException {
		super(manager, history);
		setGuideName("Planar3T");
	}
	
	public GuideUserAction predict(DependencyStructure gold, ParserConfiguration config) throws MaltChainedException {
		Planar3TConfig planarConfig = (Planar3TConfig)config;
		DependencyStructure dg = planarConfig.getDependencyGraph();
		DependencyNode stackPeek = planarConfig.getStack().peek();
		int stackPeekIndex = stackPeek.getIndex();
		int inputPeekIndex = planarConfig.getInput().peek().getIndex();
		
	   if (checkIfNodesAreRelated(gold, stackPeekIndex , inputPeekIndex)
				&& !checkIfNodesAreRelated ( dg , stackPeekIndex , inputPeekIndex ) ) {
			return updateActionContainers(Planar3T.ARC, getEdgeGraph( gold , stackPeekIndex ,inputPeekIndex ).getLabelSet());
	   } else if (planarConfig.getRootHandling() == Planar3TConfig.STRICT && !stackPeek.hasHead()) {
			return updateActionContainers(Planar3T.SHIFT, null);
		} else if (areInputBufferRelated(gold,planarConfig.getStack(),inputPeekIndex) &&
				(!checkIfNodesAreRelated ( gold , 0 , inputPeekIndex ) || planarConfig.getRootHandling() == Planar3TConfig.NORMAL)) {
			return updateActionContainers(Planar3T.REDUCE, null);
		} else {
			return updateActionContainers(Planar3T.SHIFT, null);
		}
	  
			
	}
	
//	private boolean checkIfArcExists ( DependencyStructure dg , int index1 , int index2 ) throws MaltChainedException
//	{
//		return dg.getTokenNode(index2).hasHead() && dg.getTokenNode(index2).getHead().getIndex() == index1;
//	}
	
	private boolean checkIfNodesAreRelated( DependencyStructure dg , int index1 , int index2 ) throws MaltChainedException
	{
		TokenNode tk=dg.getTokenNode(index1);
		Set<DependencyNode> heads=null;
		if(tk!=null){
		   heads=tk.getHeads();
		
		   for (DependencyNode head : heads) {
			if(head.getIndex()==index2)
			{
				return true;
			}
		   }
		}   
		
		tk=dg.getTokenNode(index2);
		if(tk!=null){
		   heads=tk.getHeads();
		   for (DependencyNode head : heads) {
			if(head.getIndex()==index1)
			{
				return true;
			}
		  }
		}   
		return false;
	}
	
	private boolean areInputBufferRelated( DependencyStructure dg , Stack<DependencyNode> stack , int indexBuffer ) throws MaltChainedException
	{
		Iterator<DependencyNode> it=stack.iterator();
		while(it.hasNext())
		{
			DependencyNode nodo=it.next();
			if(checkIfNodesAreRelated(dg,nodo.getIndex(),indexBuffer) && stack.peek().getIndex()!=nodo.getIndex())
			{
				return true;
			}
		}
		return false;
		
	}
	
	private Edge getEdgeGraph( DependencyStructure dg , int index1 , int index2 ) throws MaltChainedException
	{
	  SortedSet<Edge> arcos=dg.getEdges();
	  Iterator<Edge> it=arcos.iterator();
	  while(it.hasNext())
	  {
		Edge arco=it.next();
		if(arco.getSource().getIndex()==index1 && arco.getTarget().getIndex()==index2)
		{
			return arco;
		}
		if(arco.getSource().getIndex()==index2 && arco.getTarget().getIndex()==index1)
		{
			return arco;
		}
	  }
	  return null;
	}
	
	public void finalizeSentence(DependencyStructure dependencyGraph) throws MaltChainedException {}
	
	public void terminate() throws MaltChainedException {}
}
