package bk;

import java.awt.Color;
import java.util.ArrayList;
import java.util.Collections;

import javax.swing.JTree;

import animation.AlgorithmStage;
import animation.BackwardAction;
import animation.PseudoCodeNode;
import bk.ExtremalLayoutCalc.LayoutType;
import graph.LayeredGraphNode;

/**
 * The stage of the combination of the four extremal layouts.
 * @author kolja
 *
 */
public class Combine implements AlgorithmStage {

	LayeredGraphNode graph;
	
	private enum State
	{
		ALIGN,
		SET_COORDINATES
	}
	
	private State state;
	private int btlOffset;
	private int btrOffset;
	private int tblOffset;
	private int tbrOffset;
	private int vIndex;
	private ArrayList< BackwardAction > actions;
    private PseudoCodeNode alignNode;
    private PseudoCodeNode setNode;
    private PseudoCodeNode loopNode;
    private boolean inside;
	
	public Combine( LayeredGraphNode graph )
	{
		this.graph = graph;
		state = State.ALIGN;
		vIndex = 0;
		actions = new ArrayList<>();
		inside = false;
	}
	
	@Override
	public StageStatus forwardStep() {
		if( state == State.ALIGN )
		{
		    inside = false;
		    alignNode.setSelected( true );
			int tblw = (int)graph.getWidth( LayoutType.TOP_BOTTOM_LEFT );
			int tbrw = (int)graph.getWidth( LayoutType.TOP_BOTTOM_RIGHT );
			int btlw = (int)graph.getWidth( LayoutType.BOTTOM_TOP_LEFT );
			int btrw = (int)graph.getWidth( LayoutType.BOTTOM_TOP_RIGHT );
			LayoutType minLayout = LayoutType.TOP_BOTTOM_LEFT;
			int minWidth = tblw;
			if( tbrw < minWidth )
			{
				minWidth = tbrw;
				minLayout = LayoutType.TOP_BOTTOM_RIGHT;
			}
			if( btlw < minWidth )
			{
				minWidth = btlw;
				minLayout = LayoutType.BOTTOM_TOP_LEFT;
			}
			if( btrw < minWidth )
			{
				minWidth = btrw;
				minLayout = LayoutType.BOTTOM_TOP_RIGHT;
			}
			int minX = calcMinX( minLayout );
			btlOffset = minX - calcMinX( LayoutType.BOTTOM_TOP_LEFT );
			tblOffset = minX - calcMinX( LayoutType.TOP_BOTTOM_LEFT );
			btrOffset = minWidth - btrw;
			tbrOffset = minWidth - tbrw;
			graph.setColor( Color.BLACK, null );
			//MainView.frame.setSize( MainView.frame.getWidth() + 1, MainView.frame.getHeight() );
			//MainView.frame.setSize( MainView.frame.getWidth() - 1, MainView.frame.getHeight() );
			actions.add( 0, () -> {
                inside = false;
	            setNode.setSelected( false );
                alignNode.setSelected( true );
				state = State.ALIGN;
				graph.setColor( null, null );
				//MainView.frame.setSize( MainView.frame.getWidth() + 1, MainView.frame.getHeight() );
				//MainView.frame.setSize( MainView.frame.getWidth() - 1, MainView.frame.getHeight() );
			});
			state = State.SET_COORDINATES;
            alignNode.setSelected( false );
            setNode.setSelected( true );
            loopNode.setSelected( true );
		}
		else
		{
			if( vIndex >= graph.getContainedNodes().size() )
			{
	            inside = false; 
			    setNode.setSelected( false );
			    loopNode.setSelected( false );
				return StageStatus.FINISHED;
			}
			else
	            inside = true;
			LayeredGraphNode current = graph.getContainedNodes().get( vIndex );
			current.setSelected( null );
			ArrayList< Integer > positions = new ArrayList<>();
			positions.add( (int)current.getX( LayoutType.TOP_BOTTOM_LEFT ) + tblOffset );
			positions.add( (int)current.getX( LayoutType.TOP_BOTTOM_RIGHT ) + tbrOffset );
			positions.add( (int)current.getX( LayoutType.BOTTOM_TOP_LEFT ) + btlOffset );
			positions.add( (int)current.getX( LayoutType.BOTTOM_TOP_RIGHT ) + btrOffset );
			Collections.sort( positions );
			int oldX = (int)current.getX( LayoutType.COMBINED );
			current.setX( (positions.get( 1 ) + positions.get( 2 )) / 2, true, LayoutType.COMBINED );
			actions.add( 0, () -> {
                inside = true; 
                setNode.setSelected( true );
                loopNode.setSelected( true );
				vIndex--;
				current.setX( oldX, true, LayoutType.COMBINED );
				current.setSelected( null );
			});
			vIndex++;
		}		
		return StageStatus.UNFINISHED;
	}
	
	private int calcMinX(  LayoutType layout )
	{
		int minX = 0;
		if( graph.getContainedNodes().size() > 0 )
			minX = (int)graph.getContainedNodes().get( 0 ).getX( layout );
		for( LayeredGraphNode n : graph.getContainedNodes() )
			minX = Math.min( minX, (int)n.getX( layout ) );
		return minX;
	}

	@Override
	public StageStatus backwardStep() {
		if( actions.size() == 0 )
		{
            inside = false;
			return StageStatus.FINISHED;
		}
		actions.get( 0 ).reverse();
		actions.remove( 0 );
		return StageStatus.UNFINISHED;
	}

    @Override
    public PseudoCodeNode createPseudocodeTree( JTree tree ) {
        PseudoCodeNode root = new PseudoCodeNode( "Balancing", tree );
        alignNode = new PseudoCodeNode( "Align Layouts", tree );
        setNode = new PseudoCodeNode( "Align to assignment of smallest width", tree );
        loopNode = new PseudoCodeNode( "Set coordinates to average median of aligned candidates", tree );
        setNode.add( loopNode );
        root.add( alignNode );
        root.add( setNode );
        return root;
    }

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

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

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

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