package codelines;

import java.util.List;

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

public class BackwardForEachLoop <T> extends CodeLine {

	String listVar;
	String loopVar;
	
	public BackwardForEachLoop( String listName, String varName )
	{
		this.listVar = listName;
		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", m.<List<T>>read( listVar, MemoryType.LOCAL ).size() - 1, MemoryType.LOCAL );
			declared = true;
		}
		if( m.<Integer>read( "line_" + lineId + "_index", MemoryType.LOCAL ) < 0 ) // 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.declare( loopVar, m.<List<T>>read( listVar, MemoryType.LOCAL ).get( m.read( "line_" + lineId + "_index", MemoryType.LOCAL ) ), MemoryType.LOCAL ); // set loop variable
			m.addFrame( new StackFrame( FrameType.LOOP ) );
			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 ) < 0 ) // prove if loop was finished
			{
				StackFrame sf = m.removeFrame(); // remove loop stack
                T old = m.read( loopVar, MemoryType.LOCAL );
                m.undeclare( loopVar, MemoryType.LOCAL );
				m.undeclare( "line_" + lineId + "_index", MemoryType.LOCAL );
				actions.push( (Memory mem) -> {
	                m.declare( loopVar, old, MemoryType.LOCAL );
					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.write( loopVar, m.<List<T>>read( listVar, MemoryType.LOCAL ).get( 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 );
	}
}
