package bk;

import java.util.ArrayList;

import javax.swing.JTree;

import animation.AlgorithmStage;
import animation.BackwardAction;
import animation.PseudoCodeNode;
import graph.LayeredGraphEdge;
import graph.LayeredGraphNode;

public class ConflictDetection implements AlgorithmStage {

    private LayeredGraphNode graph;
    private ArrayList< BackwardAction > actions;
    
    private int i;
    private int l1;
    private PseudoCodeNode markNode;
    
    ConflictDetection( LayeredGraphNode graph )
    {
        this.graph = graph;
        actions = new ArrayList<>();
        i = 1;
        l1 = 0;
    }
    
    @Override
    public StageStatus forwardStep() {
    	int oldI = i;
    	int oldL1 = l1;
    	((PseudoCodeNode)markNode.getParent()).setSelected( true );
    	markNode.setSelected( true );
    	if( i + 1 >= graph.getContainedLayers().size() - 1 )
    	{
            ((PseudoCodeNode)markNode.getParent()).setSelected( false );
            markNode.setSelected( false );
    		return StageStatus.FINISHED;
    	}
    	LayeredGraphNode curr = graph.getContainedLayers().get( i + 1 ).get( l1 );
    	curr.setSelected( null );
    	ArrayList< LayeredGraphEdge > edges = curr.getIncomingEdges();
    	LayeredGraphEdge dummyEdge = null;
    	for( LayeredGraphEdge e : edges )
    	{
    		if( e.isDummyEdge() )
    		{
    			dummyEdge = e;
    			break;
    		}
    	}
    	ArrayList< LayeredGraphEdge > conflicts = new ArrayList<>();
        if( dummyEdge != null )
        {
    		for( LayeredGraphEdge e : edges )
    		{
    			if( e.isDummyEdge() )
    			{
    				ArrayList< LayeredGraphEdge > conf = e.calcConflictedEdges();
    				for( LayeredGraphEdge ce : conf )
    				{
    					if( !ce.isDummyEdge() )
    						conflicts.add( ce );
    				}
    			}
    		}
        }
        for( LayeredGraphEdge c : conflicts )
        	c.setConflicted( true, null );
    	StageStatus status =  calcNextStatus();
    	actions.add( 0, ()->{
    		i = oldI;
    		l1 = oldL1;
            if( i + 1 < graph.getContainedLayers().size() - 1 && l1 > 0 )
            {
                LayeredGraphNode layde = graph.getContainedLayers().get( i + 1 ).get( l1 - 1 );
                layde.setSelected( null );
            }
            for( LayeredGraphEdge c : conflicts )
            	c.setConflicted( false, null );
    	});
    	return status;
    }
    
    private StageStatus calcNextStatus()
    {
        l1++;
        if( l1 >= graph.getContainedLayers().get( i + 1 ).size() )
        {
            i++;
            if( i >= graph.getContainedLayers().size() - 2 )
            {
                i--;
                l1--;
                ((PseudoCodeNode)markNode.getParent()).setSelected( false );
                markNode.setSelected( false );
                return StageStatus.FINISHED;
            }
            l1 = 0;
        }
        return StageStatus.UNFINISHED;
    }

    @Override
    public StageStatus backwardStep() {
        ((PseudoCodeNode)markNode.getParent()).setSelected( true );
        markNode.setSelected( true );
        if( actions.size() == 0 )
        {
            ((PseudoCodeNode)markNode.getParent()).setSelected( false );
            markNode.setSelected( false );
        	return StageStatus.FINISHED;
        }
        actions.get( 0 ).reverse();
        actions.remove( 0 );
        return StageStatus.UNFINISHED;
    }

    @Override
    public PseudoCodeNode createPseudocodeTree( JTree tree ) {
        PseudoCodeNode root = new PseudoCodeNode( "Preprocessing (mark type 1 conflicts)", tree );
        PseudoCodeNode loopNode = new PseudoCodeNode( "Loop through all nodes...", tree );
        markNode = new PseudoCodeNode( "If non-inner segment crosses an inner segment whose target is this node, mark the non-inner segment as conflicted", tree );
        loopNode.add( markNode );
        root.add( loopNode );
        return root;
    }

    @Override
    public StageStatus forwardStepOver() {
        return forwardStep();
    }

    @Override
    public StageStatus forwardStepOut() {
        StageStatus status = StageStatus.UNFINISHED;
        while( status != StageStatus.FINISHED )
            status = forwardStep();
        return status;
    }

    @Override
    public StageStatus backwardStepOver() {
        return backwardStep();
    }

    @Override
    public StageStatus backwardStepOut() {
        StageStatus status = StageStatus.UNFINISHED;
        while( status != StageStatus.FINISHED )
            status = backwardStep();
        return status;
    }
}
