package bk;

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

import animation.AnimatedAlgorithm;
import animation.AnimationController;
import animation.CodeLine;
import animation.ControlFlow;
import animation.Memory;
import animation.PseudoCodeNode;
import animation.PseudoCodeProcessor;
import animation.Memory.MemoryType;
import animation.Memory.ReadOnlyMemory;
import codelines.DeclareVariable;
import codelines.FunctionCall;
import codelines.FunctionDefinition;
import codelines.Comment;
import codelines.SetVariable;
import graph.LayeredGraphNode;
import lib.TextLayoutHelper;

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

    public enum State
    {
        CONFLICTS,
        LAYOUT1,
        LAYOUT2,
        LAYOUT3,
        LAYOUT4,
        COMBINE
    }
    
    private State state;
    
    public BKNodePlacement(AnimationController controller, LayeredGraphNode graph, JFrame view) {
        super(controller, graph, view);
        state = State.CONFLICTS;
    }
    
    public State getAlgorithmState()
    {
        return state;
    }
    
    public void setAlgorithmState( State s )
    {
        state = s;
    }

    @Override
    public PseudoCodeNode createPseudocodeTree( JTree tree )
    {
    	String[] vars = { "layout", "graph" };
    	PseudoCodeNode mainFunction = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode("function bkNodePlacement( graph )", vars ), tree, new FunctionDefinition( new String[]{"graph"} ) );
    	root = new PseudoCodeNode( TextLayoutHelper.setupPseudoCodeStage("-- BK Node Placement Algorithm --" ), tree, new CodeLine() {

			@Override
			public ControlFlow runForward(Memory m) {
				m.declare( "param1", graph, MemoryType.GLOBAL );
				if( m.isDefined( "Called", MemoryType.GLOBAL ) )
				{
					actions.push( (Memory mem) -> {
					    m.undeclare( "param1", MemoryType.GLOBAL );
					} );
					return new ControlFlow( ControlFlow.STEP_OVER );
				}
				m.declare( "Called", true, MemoryType.GLOBAL );
				actions.push( (Memory mem) -> {
                    m.undeclare( "param1", MemoryType.GLOBAL );
	                m.undeclare( "Called", MemoryType.GLOBAL );
				} );
				return new ControlFlow( mainFunction );
			}
        	
        } );
        root.setSelected( true );
    	
        PseudoCodeNode conflictDetectionFunction = new ConflictDetection( this ).createPseudocodeTree( tree );
        PseudoCodeNode calcLayout = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "function calcLayout( layout, graph )", vars ), tree, new FunctionDefinition( vars ) );
        PseudoCodeNode combine = new Combine().createPseudocodeTree( tree );
        root.add( mainFunction );
        mainFunction.add( new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "call detectConflicts( graph )", vars ), tree, new FunctionCall( conflictDetectionFunction, new String[]{ "graph" } ) ) );
        mainFunction.add( new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "layout = DOWN_RIGHT", vars ), tree, new DeclareVariable<String>( "layout" ) {
            @Override
            protected String value(ReadOnlyMemory m) {
                return "DOWN_RIGHT";
            }
        }) );
        mainFunction.add( new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "call calcLayout( layout, graph )", vars ), tree, new FunctionCall( calcLayout, new String[]{ "layout", "graph" } ) ) );
        mainFunction.add( new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "layout = DOWN_LEFT", vars ), tree, new SetVariable<String>( "layout" ) {
            @Override
            protected String value(ReadOnlyMemory m) {
                return "DOWN_LEFT";
            }
        }) );
        mainFunction.add( new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "call calcLayout( layout, graph )", vars ), tree, new FunctionCall( calcLayout, new String[]{ "layout", "graph" } ) ) );
        mainFunction.add( new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "layout = UP_RIGHT", vars ), tree, new SetVariable<String>( "layout" ) {
            @Override
            protected String value(ReadOnlyMemory m) {
                return "UP_RIGHT";
            }
        }) );
        mainFunction.add( new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "call calcLayout( layout, graph )", vars ), tree, new FunctionCall( calcLayout, new String[]{ "layout", "graph" } ) ) );
        mainFunction.add( new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "layout = UP_LEFT", vars ), tree, new SetVariable<String>( "layout" ) {
            @Override
            protected String value(ReadOnlyMemory m) {
                return "UP_LEFT";
            }
        }) );
        mainFunction.add( new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "call calcLayout( layout, graph )", vars ), tree, new FunctionCall( calcLayout, new String[]{ "layout", "graph" } ) ) );
        mainFunction.add( new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "call combine( graph )", vars ), tree, new FunctionCall( combine, new String[]{ "graph" } ) ) );
        root.add( new PseudoCodeNode( TextLayoutHelper.setupPseudoCodeStage( "-- mark type 1 conflicts --" ), tree, new Comment() ) );
        root.add( conflictDetectionFunction );
        PseudoCodeNode blockCalc = new BlockCalc().createPseudocodeTree( tree );
        PseudoCodeNode horizontalCompaction = new Compaction().createPseudocodeTree( tree );
        calcLayout.add( new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "call calculateBlockGraph( layout, graph )", vars ), tree, new FunctionCall( blockCalc, vars ) ) );
        calcLayout.add( new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "call horizontalCompaction( layout, graph )", vars ), tree, new FunctionCall( horizontalCompaction, vars ) ) );
        root.add( new PseudoCodeNode( TextLayoutHelper.setupPseudoCodeStage( "-- Compute an extremal layout --" ), tree, new Comment() ) );
        root.add( calcLayout );
        root.add( new PseudoCodeNode( TextLayoutHelper.setupPseudoCodeStage( "-- vertical alignment --" ), tree, new Comment() ) );
        root.add( blockCalc );
        root.add( new PseudoCodeNode( TextLayoutHelper.setupPseudoCodeStage( "-- horizontal compaction --" ), tree, new Comment() ) );
        root.add( horizontalCompaction );
        root.add( new PseudoCodeNode( TextLayoutHelper.setupPseudoCodeStage( "-- balancing --" ), tree, new Comment() ) );
        root.add( combine );
        processor = new PseudoCodeProcessor( root );
        return root;
    }
}
