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 AbstractForLoop<T> extends CodeLine {

    String loopVar;

    public AbstractForLoop( String varName )
    {
        this.loopVar = varName;
    }
    
    @Override
    public ControlFlow runForward(Memory m) {
        if( !m.isDefined( loopVar, MemoryType.LOCAL ) )
        { // first loop step
            m.addFrame( new StackFrame( FrameType.LOOP ) );
            m.declare( loopVar, begin( m.createReadOnlyMemory() ), MemoryType.LOCAL ); // set loop variable
            if( !condition( m.createReadOnlyMemory() ) ) // prove if the loop has finished
            {
                m.removeFrame();
                actions.push( (Memory mem) -> {} );
                return new ControlFlow( ControlFlow.STEP_OVER ); // don't execute the loop body
            }
            actions.push( (Memory mem) -> {
                mem.removeFrame();
            } );
            return new ControlFlow( ControlFlow.STEP_INTO );
        }
        T next = step( m.createReadOnlyMemory() );
        T old = m.read( loopVar, MemoryType.LOCAL );
        m.write( loopVar, next, MemoryType.LOCAL );
        if( !condition( m.createReadOnlyMemory() ) ) // prove if loop was finished
        {
            StackFrame sf = m.removeFrame(); // remove loop stack
            actions.add( (Memory mem) -> {
                mem.addFrame( sf ); // restore last loop stack
                mem.write( loopVar, old, MemoryType.LOCAL );
            });
            return new ControlFlow( ControlFlow.STEP_OVER ); // step out of the loop
        }
        StackFrame oldF = m.removeFrame(); // fresh stack frame for loop body
        m.addFrame( new StackFrame( FrameType.LOOP ) );
        m.declare( loopVar, next, MemoryType.LOCAL );
        actions.push( (Memory mem) -> {
            mem.removeFrame();
            mem.addFrame( oldF );
            mem.write( loopVar, old, MemoryType.LOCAL );
        });
        return new ControlFlow( ControlFlow.STEP_INTO );
    }
    
    abstract protected T begin( ReadOnlyMemory m );
    
    abstract protected T step( ReadOnlyMemory m );
    
    abstract protected boolean condition( final ReadOnlyMemory m );
}
