package bk;

import java.lang.reflect.InvocationTargetException;

import javax.swing.JFrame;
import javax.swing.JTree;

import animation.AlgorithmStage;
import animation.AnimatedAlgorithm;
import animation.AnimationController;
import animation.PseudoCodeNode;
import graph.LayeredGraphNode;

/**
 * The main stage of the BK node placement algorithm.
 * @author kolja
 *
 */
public class BKNodePlacement extends AnimatedAlgorithm {

	/*
	 * Private data structures to store the process of the algorithm
	 */
	
	private enum State
	{
		CONFLICTS,
		LAYOUT1,
		LAYOUT2,
		LAYOUT3,
		LAYOUT4,
		COMBINE
	}
	
	private ConflictDetection conftion;
	private State state;
	private ExtremalLayoutCalc layouts[];
	private Combine combine;
    private PseudoCodeNode conflictsNode; 
    private PseudoCodeNode layout1Node;
    private PseudoCodeNode layout2Node;
    private PseudoCodeNode layout3Node;
    private PseudoCodeNode layout4Node;
    private PseudoCodeNode combineNode;
    private boolean inside;
	
	public BKNodePlacement(AnimationController controller, LayeredGraphNode graph, JFrame view) {
		super(controller, graph, view);
		state = State.CONFLICTS;
		conftion = new ConflictDetection( graph );
		layouts = new ExtremalLayoutCalc[ 4 ];
		layouts[ 0 ] = new ExtremalLayoutCalc( ExtremalLayoutCalc.LayoutType.TOP_BOTTOM_LEFT, graph );
		layouts[ 1 ] = new ExtremalLayoutCalc( ExtremalLayoutCalc.LayoutType.TOP_BOTTOM_RIGHT, graph );
		layouts[ 2 ] = new ExtremalLayoutCalc( ExtremalLayoutCalc.LayoutType.BOTTOM_TOP_LEFT, graph );
		layouts[ 3 ] = new ExtremalLayoutCalc( ExtremalLayoutCalc.LayoutType.BOTTOM_TOP_RIGHT, graph );
		combine = new Combine( graph );
		inside = false;
	}

	@Override
	public StageStatus forwardStep() {
		return forward( "forwardStep" );
	}

	@Override
	public StageStatus backwardStep() {
	    return backward( "backwardStep" );
	}
	
	@Override
	public PseudoCodeNode createPseudocodeTree( JTree tree )
	{
	    PseudoCodeNode root = new PseudoCodeNode( "BK Node Placement Algorithm", tree );
	    root.setSelected( true );
        conflictsNode = conftion.createPseudocodeTree( tree );
        layout1Node = layouts[ 0 ].createPseudocodeTree( tree );
        layout2Node = layouts[ 1 ].createPseudocodeTree( tree );
        layout3Node = layouts[ 2 ].createPseudocodeTree( tree );
        layout4Node = layouts[ 3 ].createPseudocodeTree( tree );
        combineNode = combine.createPseudocodeTree( tree );
        root.add( conflictsNode );
        root.add( layout1Node );
        root.add( layout2Node );
        root.add( layout3Node );
        root.add( layout4Node );
        root.add( combineNode );
        return root;
	}

    @Override
    public StageStatus forwardStepOver() {
        if( !inside )
        {
            State oldState = state;
            StageStatus status = StageStatus.UNFINISHED;
            while( state == oldState && status != StageStatus.FINISHED )
                status = forwardStep();
            return status;
        }
        else
            return forward( "forwardStepOver" );
    }

    @Override
    public StageStatus forwardStepOut() {
        if( !inside )
        {
            StageStatus status = StageStatus.UNFINISHED;
            while( status != StageStatus.FINISHED )
                status = forwardStep();
            return status;
        }
        else
            return forward( "forwardStepOut" );
    }

    @Override
    public StageStatus backwardStepOver() {
        if( !inside )
        {
            State oldState = state;
            StageStatus status = StageStatus.UNFINISHED;
            while( state == oldState && status != StageStatus.FINISHED )
                status = backwardStep();
            return status;
        }
        else
            return backward( "backwardStepOver" );
    }

    @Override
    public StageStatus backwardStepOut() {
        if( !inside )
        {
            StageStatus status = StageStatus.UNFINISHED;
            while( status != StageStatus.FINISHED )
                status = backwardStep();
            return status;
        }
        else
            return backward( "backwardStepOut" );
    }
    
    private StageStatus forward( String fName )
    {
        try {
            switch( state )
            {
            case CONFLICTS:
                conflictsNode.setSelected( true );
                    if( (StageStatus)(ConflictDetection.class.getMethod( fName ).invoke( conftion ) ) == StageStatus.FINISHED )
                    {
                        inside = false;
                        conflictsNode.setSelected( false );
                        layout1Node.setSelected( true );
                        state = State.LAYOUT1;
                    }
                    else
                        inside = true;
                break;
            case LAYOUT1:
                if( (StageStatus)(ExtremalLayoutCalc.class.getMethod( fName ).invoke( layouts[ 0 ] ) ) == StageStatus.FINISHED )
                {
                    inside = false;
                    layout1Node.setSelected( false );
                    layout2Node.setSelected( true );
                    state = State.LAYOUT2;
                }
                else
                    inside = true;
                break;
            case LAYOUT2:
                if( (StageStatus)(ExtremalLayoutCalc.class.getMethod( fName ).invoke( layouts[ 1 ] ) ) == StageStatus.FINISHED )
                {
                    inside = false;
                    layout2Node.setSelected( false );
                    layout3Node.setSelected( true );
                    state = State.LAYOUT3;
                }
                else
                    inside = true;
                break;
            case LAYOUT3:
                if( (StageStatus)(ExtremalLayoutCalc.class.getMethod( fName ).invoke( layouts[ 2 ] ) ) == StageStatus.FINISHED )
                {
                    inside = false;
                    layout3Node.setSelected( false );
                    layout4Node.setSelected( true );
                    state = State.LAYOUT4;
                }
                else
                    inside = true;
                break;
            case LAYOUT4:
                if( (StageStatus)(ExtremalLayoutCalc.class.getMethod( fName ).invoke( layouts[ 3 ] ) ) == StageStatus.FINISHED )
                {
                    inside = false;
                    layout4Node.setSelected( false );
                    combineNode.setSelected( true );
                    state = State.COMBINE;
                }
                else
                    inside = true;
                break;
            case COMBINE:
                if( (StageStatus)(Combine.class.getMethod( fName ).invoke( combine ) ) == StageStatus.FINISHED )
                {
                    inside = false;
                    return StageStatus.FINISHED;
                }
                else
                    inside = true;
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }
        return StageStatus.UNFINISHED;
    }

    private StageStatus backward( String fName ) {
        try {
            switch( state )
            {
            case CONFLICTS:
                if( (StageStatus)(ConflictDetection.class.getMethod( fName ).invoke( conftion ) ) == StageStatus.FINISHED )
                {
                    inside = false;
                    return StageStatus.FINISHED;
                }
                else
                    inside = true;
            case LAYOUT1:
                if( (StageStatus)(ExtremalLayoutCalc.class.getMethod( fName ).invoke( layouts[ 0 ] ) ) == StageStatus.FINISHED )
                {
                    inside = false;
                    layout1Node.setSelected( false );
                    conflictsNode.setSelected( true );
                    state = State.CONFLICTS;
                }
                else
                    inside = true;
                break;
            case LAYOUT2:
                if( (StageStatus)(ExtremalLayoutCalc.class.getMethod( fName ).invoke( layouts[ 1 ] ) ) == AlgorithmStage.StageStatus.FINISHED )
                {
                    inside = false;
                    layout2Node.setSelected( false );
                    layout1Node.setSelected( true );
                    state = State.LAYOUT1;
                }
                else
                    inside = true;
                break;
            case LAYOUT3:
                if( (StageStatus)(ExtremalLayoutCalc.class.getMethod( fName ).invoke( layouts[ 2 ] ) ) == AlgorithmStage.StageStatus.FINISHED )
                {
                    inside = false;
                    layout3Node.setSelected( false );
                    layout2Node.setSelected( true );
                    state = State.LAYOUT2;
                }
                else
                    inside = true;
                break;
            case LAYOUT4:
                if( (StageStatus)(ExtremalLayoutCalc.class.getMethod( fName ).invoke( layouts[ 3 ] ) ) == AlgorithmStage.StageStatus.FINISHED )
                {
                    inside = false;
                    layout4Node.setSelected( false );
                    layout3Node.setSelected( true );
                    state = State.LAYOUT3;
                }
                else
                    inside = true;
                break;
            case COMBINE:
                if( (StageStatus)(Combine.class.getMethod( fName ).invoke( combine ) ) == AlgorithmStage.StageStatus.FINISHED )
                {
                    inside = false;
                    combineNode.setSelected( false );
                    layout4Node.setSelected( true );
                    state = State.LAYOUT4;
                }
                else
                    inside = true;
                break;
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }
        return StageStatus.UNFINISHED;
    }
    
}
