package codelines;

import animation.CodeLine;
import animation.ControlFlow;
import animation.Memory;
import animation.StackFrame;
import animation.StackFrame.FrameType;

public abstract class WhileLoop extends CodeLine {

	@Override
	public ControlFlow runForward(Memory m) {
		boolean declared = false; // prove if it is the first step in the loop
		if( !m.isSomewhereDefined( "line_" + lineId + "_index", false, 1 ) )
		{ // first loop step
			m.declare( "line_" + lineId + "_index", 0, false );
			declared = true;
		}
		if( !condition( m ) ) // prove if the loop has finished
		{
			m.undeclare( "line_" + lineId + "_index", false );
			actions.push( (Memory mem) -> {
				return new ControlFlow( ControlFlow.STEP_OVER ); // loop was not called so nothing to reverse
			});
			return new ControlFlow( ControlFlow.STEP_OVER ); // don't execute the loop body
		}
		if( declared )
		{
			m.addFrame( new StackFrame( FrameType.LOOP ) );
			actions.push( (Memory mem) -> {
				mem.removeFrame();
				mem.undeclare( "line_" + lineId + "_index", false );
				return new ControlFlow( ControlFlow.STEP_OVER ); // step out of the loop
			} );
		}
		else
		{
			if( !condition( m ) ) // prove if loop was finished
			{
				StackFrame sf = m.removeFrame(); // remove loop stack
				m.undeclare( "line_" + lineId + "_index", false );
				actions.push( (Memory mem) -> {
					mem.declare( "line_" + lineId + "_index", 0, false );
					mem.addFrame( sf ); // restore last loop stack
					return new ControlFlow( ControlFlow.STEP_INTO ); // step into the loop body
				});
				return new ControlFlow( ControlFlow.STEP_OVER ); // step out of the loop
			}
			StackFrame old = m.removeFrame(); // fresh stack frame for loop body
			m.addFrame( new StackFrame( FrameType.LOOP ) );
			actions.push( (Memory mem) -> {
				mem.removeFrame();
				mem.addFrame( old );
				return new ControlFlow( ControlFlow.STEP_INTO );
			});
		}
		return new ControlFlow( ControlFlow.STEP_INTO );
	}

	abstract protected boolean condition( Memory m );
}
