package org.maltparser.parser.algorithm.twoplanar;

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 TwoPlanar extends TransitionSystem {
	protected static final int SHIFT = 1;
	protected static final int SWITCH = 2;
	protected static final int RIGHTARC = 3;
	protected static final int LEFTARC = 4;
	protected static final int REDUCE = 5;
	protected static final int REDUCEBOTH = 6;
	
	

	
	public TwoPlanar() throws MaltChainedException {
		super();
	}
	
	public void apply(GuideUserAction currentAction, ParserConfiguration config) throws MaltChainedException {
		TwoPlanarConfig planarConfig = (TwoPlanarConfig)config;
		Stack<DependencyNode> activeStack = planarConfig.getActiveStack();
		Stack<DependencyNode> inactiveStack = planarConfig.getInactiveStack();
		Stack<DependencyNode> input = planarConfig.getInput();
		currentAction.getAction(actionContainers);
		Edge e = null;
		int actionCode = transActionContainer.getActionCode();
		switch ( actionCode ) {
		case LEFTARC:
			e = planarConfig.getDependencyStructure().addDependencyEdge(input.peek().getIndex(), activeStack.peek().getIndex());
			addEdgeLabels(e);
			break;
		case RIGHTARC:
			e = planarConfig.getDependencyStructure().addDependencyEdge(activeStack.peek().getIndex(), input.peek().getIndex());
			addEdgeLabels(e);
			break;
		case SWITCH:
			planarConfig.switchStacks();
			if ( planarConfig.reduceAfterSwitch() )
			{
				planarConfig.getActiveStack().pop();
			}
			break;
		case REDUCE:
			activeStack.pop();
			break;
		case REDUCEBOTH:
			activeStack.pop();
			inactiveStack.pop();
			break;
		default: //SHIFT
			DependencyNode n = input.pop();
			activeStack.push(n);
			inactiveStack.push(n);
			break;
		}
		planarConfig.setLastAction(actionCode);
	}
	

	public GuideUserAction getDeterministicAction(GuideUserHistory history, ParserConfiguration config) throws MaltChainedException {
		TwoPlanarConfig theConfig = (TwoPlanarConfig)config;
		if (theConfig.getRootHandling() != TwoPlanarConfig.NORMAL && theConfig.getActiveStack().peek().isRoot()) {
			return updateActionContainers(history, TwoPlanar.SHIFT, null);
		}
		return null;
	}
	
	protected void addAvailableTransitionToTable(TransitionTable ttable) throws MaltChainedException {
		ttable.addTransition(SHIFT, "SH", false, null);
		ttable.addTransition(SWITCH, "SW", false, null);
		ttable.addTransition(REDUCE, "RE", false, null);
		ttable.addTransition(REDUCEBOTH, "RB", false, null);
		ttable.addTransition(RIGHTARC, "RA", true, null);
		ttable.addTransition(LEFTARC, "LA", true, null);
	}
	
	protected void initWithDefaultTransitions(GuideUserHistory history) throws MaltChainedException {
		GuideUserAction currentAction = new ComplexDecisionAction((History)history);
		
		transActionContainer.setAction(SHIFT);
		transActionContainer.setAction(REDUCE);
		transActionContainer.setAction(SWITCH); //TODO it seems like a good idea to do this, but I don't know what it actually does
		transActionContainer.setAction(REDUCEBOTH); //TODO same as above
		for (int i = 0; i < arcLabelActionContainers.length; i++) {
			arcLabelActionContainers[i].setAction(-1);
		}
		currentAction.addAction(actionContainers);
	}
	
	public String getName() {
		return "two-planar arc-eager";
	}

	public boolean permissible(GuideUserAction currentAction, ParserConfiguration config) throws MaltChainedException {
		currentAction.getAction(actionContainers);
		int trans = transActionContainer.getActionCode();
		TwoPlanarConfig planarConfig = (TwoPlanarConfig)config;
		DependencyNode activeStackPeek = planarConfig.getActiveStack().peek();
		DependencyNode inactiveStackPeek = planarConfig.getInactiveStack().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) && !isActionContainersLabeled()) {
			return false;
		}
		//if ((trans == LEFTARC || trans == REDUCE) && stackPeek.isRoot()) { 
		//	return false;
		//}
		if (trans == LEFTARC) {
			//avoid making root child of something
			if ( activeStackPeek.isRoot() ) 
				return false;
			//enforce single-head constraint if present
			if ( activeStackPeek.hasHead() && singleHeadConstraint ) 
				return false;
			//avoid two links being created from and to the same node
			if ( activeStackPeek.hasHead() && dg.getTokenNode(activeStackPeek.getIndex()).getHead().getIndex() == inputPeek.getIndex() )
				return false;
			//enforce acyclicity constraint if present
			if ( acyclicityConstraint && activeStackPeek.findComponent().getIndex() == inputPeek.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() == activeStackPeek.getIndex() )
				return false;
			//enforce acyclicity constraint if present
			if ( acyclicityConstraint && activeStackPeek.findComponent().getIndex() == inputPeek.findComponent().getIndex() )
				return false;
		}
		if (trans == REDUCE) {
			//do not reduce the dummy root
			if ( activeStackPeek.isRoot() ) 
				return false;
			//enforce no-covered-roots constraint if present
			if ( !activeStackPeek.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 == REDUCEBOTH) {
			//do not reduce the dummy root
			if ( activeStackPeek.isRoot() || inactiveStackPeek.isRoot() ) 
				return false;
			//enforce no-covered-roots constraint if present
			if ( (!activeStackPeek.hasHead() || inactiveStackPeek.hasHead()) && noCoveredRootsConstraint )
				return false;
			
			//TODO remove this:
			//not using this transition at the moment, so
			return false;
		}
		if ( trans == SWITCH )
		{
			if ( planarConfig.reduceAfterSwitch() )
			{
				if ( inactiveStackPeek.isRoot() ) 
					return false;
				//enforce no-covered-roots constraint if present
				if ( !inactiveStackPeek.hasHead() && noCoveredRootsConstraint )
					return false;
			}
			else
			{
				if ( planarConfig.getLastAction() == SWITCH ) return false;
			}
		}
		return true;
	}
	
	public GuideUserAction defaultAction(GuideUserHistory history, ParserConfiguration configuration) throws MaltChainedException {
		return updateActionContainers(history, TwoPlanar.SHIFT, null);
	}
	
	
}