package org.maltparser.parser.algorithm.moreTransition;



	import java.util.Stack;

	import org.maltparser.core.exception.MaltChainedException;
	import org.maltparser.core.syntaxgraph.DependencyStructure;
	import org.maltparser.core.syntaxgraph.edge.Edge;
	import org.maltparser.core.syntaxgraph.node.DependencyNode;
	import org.maltparser.parser.ParserConfiguration;
	import org.maltparser.parser.TransitionSystem;
	import org.maltparser.parser.history.GuideUserHistory;
	import org.maltparser.parser.history.History;
	import org.maltparser.parser.history.action.ComplexDecisionAction;
	import org.maltparser.parser.history.action.GuideUserAction;
	import org.maltparser.parser.transition.TransitionTable;
	/**
	 * @author Carlos Gomez Rodriguez
	 *
	 */
	public class MoreTransition  extends TransitionSystem {
		protected static final int SHIFT = 1;
		protected static final int REDUCE = 2;
		protected static final int RIGHTARC = 3;
		protected static final int LEFTARC = 4;
		protected static final int LEFTSHORTARC = 5;
		protected static final int RIGHTSHORTARC = 6;
		protected static final int RIGHTARCRED = 7;
		protected static final int SHIFTDUCE = 8;
		protected static final int LEFTARCPOP2 = 9;
		protected static final int LEFTNONPROJARC = 10;
		protected static final int RIGHTNONPROJARC = 11;
		
		public MoreTransition () throws MaltChainedException {
			super();
		}
		
		public void apply(GuideUserAction currentAction, ParserConfiguration config) throws MaltChainedException {
			MoreTransitionConfig planarConfig = (MoreTransitionConfig)config;
			Stack<DependencyNode> stack = planarConfig.getStack();
			Stack<DependencyNode> input = planarConfig.getInput();
			currentAction.getAction(actionContainers);
			Edge e = null;
			switch (transActionContainer.getActionCode()) {
			case LEFTSHORTARC:
				e = planarConfig.getDependencyStructure().addDependencyEdge(input.get(planarConfig.getInput().size()-2).getIndex(), input.peek().getIndex());
				addEdgeLabels(e);
				input.pop();
				break;
//			case LEFTARCPOP2:
//				e = planarConfig.getDependencyStructure().addDependencyEdge(input.get(planarConfig.getInput().size()-3).getIndex(), input.peek().getIndex());
//				addEdgeLabels(e);
//				input.pop();
//				//System.out.println("---LEFTARCPOP2222");
//				break;
			case LEFTARC:
				e = planarConfig.getDependencyStructure().addDependencyEdge(input.peek().getIndex(), stack.peek().getIndex());
				addEdgeLabels(e);
				stack.pop();
				//System.out.println("---LEFTARC");
				break;
//			case LEFTNONPROJARC:
//				e = planarConfig.getDependencyStructure().addDependencyEdge(input.get(planarConfig.getInput().size()-2).getIndex(), stack.peek().getIndex());
//				addEdgeLabels(e);
//				stack.pop();
//				System.out.println("---LEFTNONPROJARC");
//				break;
//			case RIGHTNONPROJARC:
//				e = planarConfig.getDependencyStructure().addDependencyEdge(stack.peek().getIndex(),input.get(planarConfig.getInput().size()-2).getIndex());
//				addEdgeLabels(e);
//				DependencyNode a1=input.pop();
//				DependencyNode a2=input.pop();
//				stack.push(a2);
//				input.push(a1);
//				System.out.println("---RIGHTNONPROJARC");
//				break;
//			case RIGHTSHORTARC:
//				e = planarConfig.getDependencyStructure().addDependencyEdge(input.peek().getIndex(), input.get(planarConfig.getInput().size()-2).getIndex());
//				addEdgeLabels(e);
//				stack.push(input.pop());
//				input.pop();
//				input.push(stack.pop());
//				//System.out.println("---RIGHTARCPOP");
//				break;
//			case RIGHTARCRED:
//				e = planarConfig.getDependencyStructure().addDependencyEdge(stack.peek().getIndex(), input.peek().getIndex());
//				addEdgeLabels(e);
//				stack.pop();
//				stack.push(input.pop());
//				//System.out.println("---------RIGHTARCRED");
//				break;
			case RIGHTARC:
				e = planarConfig.getDependencyStructure().addDependencyEdge(stack.peek().getIndex(), input.peek().getIndex());
				addEdgeLabels(e);
				stack.push(input.pop());
				//System.out.println("---RIGHTARC");
				break;
			case REDUCE:
				stack.pop();
				//System.out.println("---REDUCE");
				break;
//			case SHIFTDUCE:
//				input.pop();
//				//System.out.println("---------SHIFTDUCEEEEEEEEE");
//				break;
			default: //SHIFT
				stack.push(input.pop());
			    //System.out.println("---SHIFT");
				break;
			}
		}
		
		public GuideUserAction getDeterministicAction(GuideUserHistory history, ParserConfiguration config) throws MaltChainedException {
			//PlanarConfig planarConfig = (PlanarConfig)config;
			//if (planarConfig.getRootHandling() != PlanarConfig.NORMAL && planarConfig.getStack().peek().isRoot()) {
			//	return updateActionContainers(history, Planar.SHIFT, null);
			//}
			//TODO: yeah, shift root
			return null;
		}
		
		protected void addAvailableTransitionToTable(TransitionTable ttable) throws MaltChainedException {
			ttable.addTransition(SHIFT, "SH", false, null);
			ttable.addTransition(REDUCE, "RE", false, null);
			ttable.addTransition(RIGHTARC, "RA", true, null);
			ttable.addTransition(LEFTARC, "LA", true, null);
			ttable.addTransition(LEFTSHORTARC, "LSA", true, null);
			ttable.addTransition(RIGHTSHORTARC, "RSA", true, null);
			ttable.addTransition(RIGHTARCRED, "RR", true, null);
			ttable.addTransition(SHIFTDUCE, "SD", false, null);
			ttable.addTransition(LEFTARCPOP2, "L2", true, null);
			
			ttable.addTransition(LEFTNONPROJARC, "LNP", true, null);
			ttable.addTransition(RIGHTNONPROJARC, "RNP", true, null);
		}
		
		protected void initWithDefaultTransitions(GuideUserHistory history) throws MaltChainedException {
			GuideUserAction currentAction = new ComplexDecisionAction((History)history);
			
			transActionContainer.setAction(SHIFT);
			transActionContainer.setAction(REDUCE);
			for (int i = 0; i < arcLabelActionContainers.length; i++) {
				arcLabelActionContainers[i].setAction(-1);
			}
			currentAction.addAction(actionContainers);
		}
		
		public String getName() {
			return "mt arc-eager";
		}

		public boolean permissible(GuideUserAction currentAction, ParserConfiguration config) throws MaltChainedException {
			currentAction.getAction(actionContainers);
			int trans = transActionContainer.getActionCode();
			MoreTransitionConfig planarConfig = (MoreTransitionConfig)config;
			DependencyNode stackPeek = planarConfig.getStack().peek();
			DependencyNode inputPeek = planarConfig.getInput().peek();
			DependencyStructure dg = planarConfig.getDependencyGraph();
			//int rootHandling = planarConfig.getRootHandling();
			boolean singleHeadConstraint = planarConfig.requiresSingleHead();
			boolean noCoveredRootsConstraint = planarConfig.requiresNoCoveredRoots();
			boolean acyclicityConstraint = planarConfig.requiresAcyclicity();
			boolean connectednessConstraintOnReduce = planarConfig.requiresConnectednessCheckOnReduce();
			boolean connectednessConstraintOnShift = planarConfig.requiresConnectednessCheckOnShift();
			
			
			if ((trans == LEFTARC || trans == RIGHTARC || trans == LEFTSHORTARC || trans == LEFTARCPOP2 || trans == LEFTNONPROJARC || trans == RIGHTNONPROJARC || trans == RIGHTSHORTARC || trans == RIGHTARCRED) && !isActionContainersLabeled()) {
				return false;
			}
			//if ((trans == LEFTARC || trans == REDUCE) && stackPeek.isRoot()) { 
			//	return false;
			//}
			if (trans == LEFTARC) {
				//avoid making root child of something
				if ( stackPeek.isRoot() ) 
					return false;
				//enforce single-head constraint if present
				if ( stackPeek.hasHead() && singleHeadConstraint ) 
					return false;
				//avoid two links being created from and to the same node
				if ( stackPeek.hasHead() && dg.getTokenNode(stackPeek.getIndex()).getHead().getIndex() == inputPeek.getIndex() )
					return false;
				//enforce acyclicity constraint if present
				if ( acyclicityConstraint && stackPeek.findComponent().getIndex() == inputPeek.findComponent().getIndex() )
					return false;
			}
			
			if (trans == LEFTNONPROJARC ) {
				if(planarConfig.getInput().size()<2){
					return false;
				}
				DependencyNode inputPeek2 = planarConfig.getInput().get(planarConfig.getInput().size()-2);
				
				//avoid making root child of something
				if ( stackPeek.isRoot() ) 
					return false;
				//enforce single-head constraint if present
				if ( stackPeek.hasHead() && singleHeadConstraint ) 
					return false;
				//avoid two links being created from and to the same node
				if ( stackPeek.hasHead() && dg.getTokenNode(stackPeek.getIndex()).getHead().getIndex() == inputPeek2.getIndex() )
					return false;
				//enforce acyclicity constraint if present
				if ( acyclicityConstraint && stackPeek.findComponent().getIndex() == inputPeek2.findComponent().getIndex() )
					return false;
			}
			
			if (trans == LEFTSHORTARC) {
				if(planarConfig.getInput().size()<2){
					return false;
				}
				DependencyNode inputPeek2 = planarConfig.getInput().get(planarConfig.getInput().size()-2);
				
				//enforce single-head constraint if present
				if ( inputPeek.hasHead() && singleHeadConstraint ) 
					return false;
				//avoid two links being created from and to the same node
				if ( inputPeek.hasHead() && dg.getTokenNode(inputPeek.getIndex()).getHead().getIndex() == inputPeek2.getIndex() )
					return false;
				//enforce acyclicity constraint if present
				if ( acyclicityConstraint && inputPeek.findComponent().getIndex() == inputPeek2.findComponent().getIndex() )
					return false;
				
				
			}
			
			if (trans == LEFTARCPOP2) {
				if(planarConfig.getInput().size()<3){
					return false;
				}
				DependencyNode inputPeek3 = planarConfig.getInput().get(planarConfig.getInput().size()-3);
				
				//enforce single-head constraint if present
				if ( inputPeek.hasHead() && singleHeadConstraint ) 
					return false;
				//avoid two links being created from and to the same node
				if ( inputPeek.hasHead() && dg.getTokenNode(inputPeek.getIndex()).getHead().getIndex() == inputPeek3.getIndex() )
					return false;
				//enforce acyclicity constraint if present
				if ( acyclicityConstraint && inputPeek.findComponent().getIndex() == inputPeek3.findComponent().getIndex() )
					return false;
			}
			
			if (trans == RIGHTSHORTARC) {
				   
				if(planarConfig.getInput().size()<2){
					return false;
				}
				DependencyNode inputPeek2 = planarConfig.getInput().get(planarConfig.getInput().size()-2);
				
//				if(!planarConfig.getDependencyGraph().getTokenNode(inputPeek2.getIndex()).hasDependent()){
//					return false;
//				}
				//enforce single-head constraint if present
				if ( inputPeek2.hasHead() && singleHeadConstraint ) 
					return false;
				//avoid two links being created from and to the same node
				if ( inputPeek2.hasHead() && dg.getTokenNode(inputPeek2.getIndex()).getHead().getIndex() == inputPeek.getIndex() )
					return false;
				//enforce acyclicity constraint if present
				if ( acyclicityConstraint && inputPeek2.findComponent().getIndex() == inputPeek.findComponent().getIndex() )
					return false;
			}
			if (trans == RIGHTARCRED) {
				//No se puede eliminar el root
				if ( stackPeek.isRoot() ) 
					return false;
				if ( !stackPeek.hasHead() )
					return false;
				
				//enforce single-head constraint if present
				if ( inputPeek.hasHead() && singleHeadConstraint )
					return false;
				//avoid two links being created from and to the same node
				if ( inputPeek.hasHead() && dg.getTokenNode(inputPeek.getIndex()).getHead().getIndex() == stackPeek.getIndex() )
					return false;
				//enforce acyclicity constraint if present
				if ( acyclicityConstraint && stackPeek.findComponent().getIndex() == inputPeek.findComponent().getIndex() )
					return false;
			}
			if (trans == RIGHTNONPROJARC) {
				if(planarConfig.getInput().size()<2){
					return false;
				}
				DependencyNode inputPeek2 = planarConfig.getInput().get(planarConfig.getInput().size()-2);
				//enforce single-head constraint if present
				if ( inputPeek2.hasHead() && singleHeadConstraint )
					return false;
				//avoid two links being created from and to the same node
				if ( inputPeek2.hasHead() && dg.getTokenNode(inputPeek2.getIndex()).getHead().getIndex() == stackPeek.getIndex() )
					return false;
				//enforce acyclicity constraint if present
				if ( acyclicityConstraint && stackPeek.findComponent().getIndex() == inputPeek2.findComponent().getIndex() )
					return false;
			}
			if (trans == RIGHTARC) {
				//enforce single-head constraint if present
				if ( inputPeek.hasHead() && singleHeadConstraint )
					return false;
				//avoid two links being created from and to the same node
				if ( inputPeek.hasHead() && dg.getTokenNode(inputPeek.getIndex()).getHead().getIndex() == stackPeek.getIndex() )
					return false;
				//enforce acyclicity constraint if present
				if ( acyclicityConstraint && stackPeek.findComponent().getIndex() == inputPeek.findComponent().getIndex() )
					return false;
			}
			if (trans == REDUCE) {
				//do not reduce the dummy root
				if ( stackPeek.isRoot() ) 
					return false;
				//enforce no-covered-roots constraint if present
				if ( !stackPeek.hasHead() && noCoveredRootsConstraint )
					return false;
				//TODO does this line still make sense? (from Nivre arc-eager)
				//if ( !stackPeek.hasHead() && rootHandling == PlanarConfig.STRICT ) 
				//	return false;
				//enforce connectedness constraint if present
				if ( connectednessConstraintOnReduce )
				{
					boolean path1 = ( stackPeek.findComponent().getIndex() == inputPeek.findComponent().getIndex() );
					boolean path2;
					if ( planarConfig.getStack().size() < 2 ) path2=false;
					else
					{
						DependencyNode stackPrev = planarConfig.getStack().get(planarConfig.getStack().size()-2);
						path2 = stackPrev.findComponent().getIndex() == stackPeek.findComponent().getIndex();
					}
					return path1 || path2;
				}
			}
			if ( trans == SHIFT )
			{
				if ( connectednessConstraintOnShift && planarConfig.getInput().size() == 1 ) //last word
				{
					boolean path = ( planarConfig.getDependencyGraph().getTokenNode(1).findComponent().getIndex() == inputPeek.findComponent().getIndex() ); //require connection to 1st
					return path;
				}
			}
			if ( trans == SHIFTDUCE )
			{
			   
			   
			   if(!planarConfig.getDependencyGraph().getTokenNode(inputPeek.getIndex()).hasHead())
				   return false;
			}
			return true;
		}
		
		public GuideUserAction defaultAction(GuideUserHistory history, ParserConfiguration configuration) throws MaltChainedException {
			return updateActionContainers(history, MoreTransition.SHIFT, null);
		}
	}
