package bk;

import java.util.ArrayList;
import java.util.List;

import javax.swing.JTree;

import animation.AlgorithmStage;
import animation.AnimatedAlgorithm;
import animation.CodeLine;
import animation.ControlFlow;
import animation.Memory;
import animation.PseudoCodeNode;
import bk.ExtremalLayoutCalc.LayoutType;
import codelines.DeclareVariable;
import codelines.ForEachLoop;
import codelines.ForLoop;
import codelines.FunctionCall;
import codelines.FunctionDefinition;
import codelines.IfLoop;
import codelines.Kommentar;
import codelines.WhileLoop;
import graph.LayeredGraphEdge;
import graph.LayeredGraphNode;
import lib.TextLayoutHelper;

/**
 * the preprocessing stage of the bk node placement algorithm
 * 
 * @author kolja and eren
 *
 */
public class ConflictDetection implements AlgorithmStage {

    private AnimatedAlgorithm alg;

    public ConflictDetection( AnimatedAlgorithm alg ) {
        this.alg = alg;
    }

    @Override
    public PseudoCodeNode createPseudocodeTree(JTree tree) {
        String vars[] = { "i", "L", "k0", "l", "l1", "k1", "k", "v", "graph" };
        String params[] = { "graph" };
        PseudoCodeNode root = new PseudoCodeNode(TextLayoutHelper.setupPseudoCode("function mark_conflicts( graph )", vars), tree, new FunctionDefinition( params ), alg );
        PseudoCodeNode text = new PseudoCodeNode(TextLayoutHelper.setupPseudoCodeStage( "-- mark conflicts in subgrapfhs --" ), tree, new Kommentar(), alg );
        root.add( text );
        PseudoCodeNode foreach = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "foreach v in graph.getContainedNodes() do", vars ), tree, new ForEachLoop<LayeredGraphNode>( "v" ) {
			@Override
			protected List<LayeredGraphNode> list(Memory m) {
				return m.<LayeredGraphNode>read( "graph", false ).getContainedNodes();
			}
        }, alg );
        root.add( foreach );
        PseudoCodeNode ifNode = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "if v has subgraph then", vars ), tree, new IfLoop() {
			@Override
			protected boolean condition(Memory m) {
				return m.<LayeredGraphNode>read( "v", false ).getContainedLayers().size() > 0;
			}
        }, alg );
        foreach.add( ifNode );
        PseudoCodeNode call = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "call mark_conflicts( v );", vars ), tree, new FunctionCall( root, new String[]{ "v" } ), alg );
        ifNode.add( call );
        text = new PseudoCodeNode(TextLayoutHelper.setupPseudoCodeStage( "-- mark conflicts in graph --" ), tree, new Kommentar(), alg );
        root.add( text );
        PseudoCodeNode init = new PseudoCodeNode(TextLayoutHelper.setupPseudoCode( "L = graph.getContainedLayers();", vars), tree, new CodeLine() {
			@Override
			public ControlFlow runForward(Memory m) {
				m.declare( "L", m.<LayeredGraphNode>read( "graph", false ).getContainedLayers(), false );
				actions.push( (Memory mem) -> {
					mem.undeclare( "L", false );
					return new ControlFlow( ControlFlow.STEP_OVER );
				} );
				return new ControlFlow( ControlFlow.STEP_OVER );
			}
        }, alg );
        root.add( init );
        PseudoCodeNode outerLoop = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "for i=1 to |L|-2 do", vars ), tree, new ForLoop( "i" ) {
			@Override
			protected int minimum( Memory m ) {
				return 1;
			}
			@Override
			protected int maximum( Memory m ) {
				return m.<ArrayList<ArrayList<LayeredGraphNode>>>read( "L", false ).size() - 2;
			}
        }, alg );
        root.add( outerLoop );
        PseudoCodeNode line = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "k0 = 0; l = 0;", vars ), tree, new CodeLine() {
			@Override
			public ControlFlow runForward(Memory m) {
				m.declare( "k0", 0, false );
				m.declare( "l", 0, false );
				m.declare( "end_", m.<LayeredGraphNode>read( "graph", false ).getContainedLayers().get( m.<Integer>read( "i", false ) + 1 ).size() - 1, false );
				m.declare( "0", 0, false );
				actions.push( (Memory mem) -> {
					mem.undeclare( "k0", false );
					mem.undeclare( "l", false );
					mem.undeclare( "end_", false );
					mem.undeclare( "0", false );
					return new ControlFlow( ControlFlow.STEP_OVER );
				} );
				return new ControlFlow( ControlFlow.STEP_OVER );
			}
        }, alg );
        outerLoop.add( line );
        PseudoCodeNode innerLoop = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "for l1=0 to |L[i+1]|-1 do", vars ), tree, new ForLoop( "l1" ) {
			@Override
			protected int minimum(Memory m) {
				return 0;
			}
			@Override
			protected int maximum(Memory m) {
				return m.<ArrayList<ArrayList<LayeredGraphNode>>>read( "L", false ).get( m.<Integer>read( "i", false ) + 1 ).size() - 1;
			}
        }, alg );
        outerLoop.add( innerLoop );
        ifNode = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "if l1==|L[i+1]|-1 or L[i+1][l1] incident to inner segment between L[i+1] and L[i] then", vars ), tree, new IfLoop() {
			@Override
			protected boolean condition(Memory m) {
	            return m.<LayeredGraphNode>read( "graph", false ).getContainedLayers().get( m.<Integer>read( "i", false ) + 1).size() == m.<Integer>read( "l1", false ) || 
	            		incidentToInnerSegmentBetweenLiPlusOneAndLi( m );
			}
        }, alg );
        innerLoop.add( ifNode );
        line = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "k1 = |L[i]|-1;", vars ), tree, new DeclareVariable<Integer>( "k1" ) {
			@Override
			protected Integer value(Memory m) {
				return (int)m.<ArrayList<ArrayList<LayeredGraphNode>>>read( "L", false ).get( m.read( "i", false ) ).size() - 1;
			}
        }, alg );
        ifNode.add( line );
        PseudoCodeNode innerIfNode = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "if L[i+1][l1] incident to inner segment between L[i+1] and L[i] then", vars ), tree, new IfLoop() {
			@Override
			protected boolean condition(Memory m) {
				return incidentToInnerSegmentBetweenLiPlusOneAndLi( m );
			}
		}, alg );
        ifNode.add( innerIfNode );
        
        line = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "k1 = pos(pred(L[i+1][l1])[0]);", vars ), tree, new DeclareVariable<Integer>( "k1" ) {
			@Override
			protected Integer value(Memory m) {
				return (int)m.<LayeredGraphNode>read( "graph", false ).getContainedLayers().get( m.read( "i", false ) ).indexOf(
						m.<LayeredGraphNode>read( "graph", false ).getContainedLayers().get( m.<Integer>read( "i", false ) + 1 ).get( m.read( "l1", false ) ).getSortedIncomingEdges().get( 0 ).getSources().get( 0 ) );
			}
        }, alg );
        innerIfNode.add( line );
        PseudoCodeNode whileLoop = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "while l <= l1 do", vars ), tree, new WhileLoop() {
			@Override
			protected boolean condition( Memory m ) {
				return m.<Integer>read( "l", false ) <= m.<Integer>read( "l1", false );
			}
        }, alg );
        ifNode.add( whileLoop );
        foreach = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "foreach v in pred(L[i+1][l]) do", vars ), tree, new ForEachLoop<LayeredGraphEdge>( "v" ) {
			@Override
			protected List<LayeredGraphEdge> list(Memory m) {
				return m.<LayeredGraphNode>read( "graph", false ).getContainedLayers().get( m.<Integer>read( "i", false ) + 1 ).get( m.read( "l", false ) ).getIncomingEdges();
			}
        }, alg );
        whileLoop.add( foreach );
        innerIfNode = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "if k < k0 or k > k1 then", vars ), tree, new IfLoop() {
			@Override
			protected boolean condition(Memory m) {
				return m.<Integer>read( "k", false ) < m.<Integer>read( "k0", false ) || m.<Integer>read( "k", false ) > m.<Integer>read( "k1", false );
			}
        }, alg );
        foreach.add( innerIfNode );
        line = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "mark segment (v,L[i+1][l]);", vars ), tree, new CodeLine() {
			@Override
			public ControlFlow runForward(Memory m) {
				LayeredGraphEdge e = m.read( "v", false );
				boolean old = e.isConflicted( LayoutType.TOP_BOTTOM_LEFT );
				e.setConflicted( true, null );
				actions.add( (Memory mem) -> {
					e.setConflicted( old, null );
					return new ControlFlow( ControlFlow.STEP_OVER );
				});
				return new ControlFlow( ControlFlow.STEP_OVER );
			}
        }, alg );
        innerIfNode.add( line );
        line = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "l = l+1;", vars ), tree, new DeclareVariable<Integer>( "l" ) {
			@Override
			protected Integer value(Memory m) {
				return (int)m.<Integer>read( "l", false ) + 1;
			}
        }, alg );
        whileLoop.add( line );
        line = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "k0 = k1;", vars ), tree, new DeclareVariable<Integer>( "k0" ) {
			@Override
			protected Integer value(Memory m) {
				return (int)m.<Integer>read( "l1", false );
			}
        }, alg );
        ifNode.add( line );
        return root;
    }
    
    private boolean incidentToInnerSegmentBetweenLiPlusOneAndLi( Memory m ) {
        LayeredGraphNode curr = m.<LayeredGraphNode>read( "graph", false ).getContainedLayers().get( m.<Integer>read( "i", false ) + 1 ).get( m.read( "l1", false ) );
        for (LayeredGraphEdge e : curr.getIncomingEdges()) {
            if (e.isDummyEdge()) {
                return true;
            }
        }
        return false;
    }

    @Override
    public String getDebugString() {/*
        String info = "| i  | l  | l1 | k0 | k1 | k  |\n";
        info +=       "|----|----|----|----|----|----|\n";
        info += "|" + TextLayoutHelper.strToLen( "" + i, 4 ) + 
                "|" + TextLayoutHelper.strToLen( "" + l, 4 ) + 
                "|" + TextLayoutHelper.strToLen( "" + l1, 4 ) + 
                "|" + TextLayoutHelper.strToLen( "" + k0, 4 ) + 
                "|" + TextLayoutHelper.strToLen( "" + k1, 4 ) + 
                "|" + TextLayoutHelper.strToLen( "" + k, 4 ) + "|\n";
        if( insideSubgraph && vIndex < graph.getContainedNodes().size() )
        {
            info += "Subgraph of " + graph.getContainedNodes().get( vIndex ).getName() + ":\n";
            String tmp = subgraphAlgs.get( vIndex ).getDebugString();
            info += tmp;
            return info;
        }
        return info;*/
    	return "";
    }

}
