package codelines;

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

public abstract class ForLoop extends CodeLine {

	String loopVar;
	
	public ForLoop( String varName )
	{
		this.loopVar = varName;
	}
	
	@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", MemoryType.LOCAL ) )
		{ // first loop step
			m.declare( "line_" + lineId + "_index", minimum( m.createReadOnlyMemory() ), MemoryType.LOCAL );
			declared = true;
		}
		if( m.<Integer>read( "line_" + lineId + "_index", MemoryType.LOCAL ) > maximum( m.createReadOnlyMemory() ) ) // prove if the loop has finished
		{
			m.undeclare( "line_" + lineId + "_index", MemoryType.LOCAL );
			actions.push( (Memory mem) -> {});
			return new ControlFlow( ControlFlow.STEP_OVER ); // don't execute the loop body
		}
		if( declared )
		{
			m.addFrame( new StackFrame( FrameType.LOOP ) );
            m.declare( loopVar, minimum( m.createReadOnlyMemory() ), MemoryType.LOCAL ); // set loop variable
			actions.push( (Memory mem) -> {
				mem.removeFrame();
				mem.undeclare( "line_" + lineId + "_index", MemoryType.LOCAL );
			} );
		}
		else
		{
			int oldIndex = m.<Integer>read( "line_" + lineId + "_index", MemoryType.LOCAL );
			m.write( "line_" + lineId + "_index", oldIndex + 1, MemoryType.LOCAL ); // count index down
			if( m.<Integer>read( "line_" + lineId + "_index", MemoryType.LOCAL ) > maximum( m.createReadOnlyMemory() ) ) // prove if loop was finished
			{
				StackFrame sf = m.removeFrame(); // remove loop stack
				m.undeclare( "line_" + lineId + "_index", MemoryType.LOCAL );
                //int old = m.read( loopVar, false );
                //m.undeclare( loopVar, false );
				actions.add( (Memory mem) -> {
	                //m.declare( loopVar, old, false );
					mem.write( "line_" + lineId + "_index", oldIndex, MemoryType.LOCAL );
					mem.addFrame( sf ); // restore last loop stack
				});
				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 ) );
			m.declare( loopVar, (int)m.read( "line_" + lineId + "_index", MemoryType.LOCAL ), MemoryType.LOCAL ); // update loop variable
			actions.push( (Memory mem) -> {
				mem.removeFrame();
				mem.addFrame( old );
				mem.write( "line_" + lineId + "_index", oldIndex, MemoryType.LOCAL );
			});
		}
		return new ControlFlow( ControlFlow.STEP_INTO );
	}
	
	abstract protected int minimum( ReadOnlyMemory m );
	
	abstract protected int maximum( ReadOnlyMemory m );
}
