package bk;

import java.lang.reflect.InvocationTargetException;

import javax.swing.JTree;

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

/**
 * The stage where the for extremal layouts are computed.
 * @author kolja
 *
 */
public class ExtremalLayoutCalc implements AlgorithmStage {

	public enum LayoutType{
		TOP_BOTTOM_LEFT,
		TOP_BOTTOM_RIGHT,
		BOTTOM_TOP_LEFT,
		BOTTOM_TOP_RIGHT,
		COMBINED
	}
	
	private enum LayoutState
	{
		BLOCK_CALCULATION,
		COMPACTION
	}
	
	private PseudoCodeNode pseudoCode;
	private BlockCalc bc;
	private Compaction cp;
	private LayoutState status;
	private PseudoCodeNode bcNode;
	private PseudoCodeNode cpNode;
	private LayoutType type;
	private boolean inside;
	
	
	public ExtremalLayoutCalc( LayoutType typ, LayeredGraphNode graph )
	{
	    type = typ;
		status = LayoutState.BLOCK_CALCULATION;
		bc = new BlockCalc( graph, typ );
		cp = new Compaction( graph, typ );
		inside = false;
	}
	
	@Override
	public StageStatus forwardStep() {
		return forward( "forwardStep" );
	}

	@Override
	public StageStatus backwardStep() {
		return backward( "backwardStep" );
	}

    @Override
    public PseudoCodeNode createPseudocodeTree( JTree tree ) {
        if( type == LayoutType.TOP_BOTTOM_LEFT )
            pseudoCode = new PseudoCodeNode( "Extremal layout: leftmost upper", tree );
        if( type == LayoutType.TOP_BOTTOM_RIGHT )
            pseudoCode = new PseudoCodeNode( "Extremal layout: rightmost upper'", tree );
        if( type == LayoutType.BOTTOM_TOP_LEFT )
            pseudoCode = new PseudoCodeNode( "Extremal layout: leftmost lower'", tree );;
        if( type == LayoutType.BOTTOM_TOP_RIGHT )
            pseudoCode = new PseudoCodeNode( "Extremal layout: rightmost lower'", tree );
        bcNode = bc.createPseudocodeTree( tree );
        cpNode = cp.createPseudocodeTree( tree );
        pseudoCode.add( bcNode );
        pseudoCode.add( cpNode );
        return pseudoCode;
    }

    @Override
    public StageStatus forwardStepOver() {
        if( !inside )
        {
            LayoutState oldState = status;
            StageStatus stage = StageStatus.UNFINISHED;
            while( status == oldState && stage != StageStatus.FINISHED )
                stage = forwardStep();
            return stage;
        }
        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 )
        {
            LayoutState oldState = status;
            StageStatus stage = StageStatus.UNFINISHED;
            while( status == oldState && stage != StageStatus.FINISHED )
                stage = backwardStep();
            return stage;
        }
        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 {
            if( status == LayoutState.BLOCK_CALCULATION )
            {
                bcNode.setSelected( true );
                if( (StageStatus)(BlockCalc.class.getMethod( fName ).invoke( bc ) ) == StageStatus.FINISHED )
                {
                    inside = false;
                    bcNode.setSelected( false );
                    cpNode.setSelected( true );
                    status = LayoutState.COMPACTION;
                }
                else
                    inside = true;
                return StageStatus.UNFINISHED;
            }
            if( status == LayoutState.COMPACTION )
            {
                if( (StageStatus)(Compaction.class.getMethod( fName ).invoke( cp ) ) == StageStatus.FINISHED )
                {
                    inside = false;
                    cpNode.setSelected( 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 {
            if( status == LayoutState.BLOCK_CALCULATION )
            {
                bcNode.setSelected( true );
                if( (StageStatus)(BlockCalc.class.getMethod( fName ).invoke( bc ) ) == StageStatus.FINISHED )
                {
                    inside = false;
                    bcNode.setSelected( false );
                    return StageStatus.FINISHED;
                }
                else
                    inside = true;
            }
            if( status == LayoutState.COMPACTION )
            {
                cpNode.setSelected( true );
                if( (StageStatus)(Compaction.class.getMethod( fName ).invoke( cp ) ) == StageStatus.FINISHED )
                {
                    inside = false;
                    cpNode.setSelected( false );
                    status = LayoutState.BLOCK_CALCULATION;
                }
                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;
    }
}
