package animation;

import java.util.Stack;

import animation.StackFrame.FrameType;

public class Memory {

    private StackFrame global;
    private Stack< StackFrame > stack;
    
    public Memory()
    {
        stack = new Stack<StackFrame>();
        global = new StackFrame( FrameType.FUNCTION );
    }
    
    public void addFrame( StackFrame frame )
    {
        stack.push( frame );
    }
    
    public StackFrame removeFrame()
    {
        return stack.pop();
    }
    
    public <T> void declare( String name, T value, boolean global )
    {
        if( global )
            this.global.declare( name, value );
        else
        {
        	if( stack.size() == 0 )
        		return;
            stack.peek().declare( name, value );
        }
    }
    
    public <T> void write( String name, T value, boolean global )
    {
        if( global )
            this.global.set( name, value );
        else
        {
        	int index = stack.size() - 1;
        	while( index >= 0 ) {
        	   StackFrame stackF = stack.get( index-- );
        	   if( stackF.isDefined( name ) )
        	   {
        		   stackF.set( name, value );
        		   return;
        	   }
        	   if( stackF.getType() == FrameType.FUNCTION )
        		   break;
        	}
        }
    }
    
    public <T> T read( String name, boolean global )
    {
        if( global )
            return this.global.get( name );
        else
        {
        	int index = stack.size() - 1;
        	while( index >= 0 ) {
        	   StackFrame stackF = stack.get( index-- );
        	   if( stackF.isDefined( name ) )
        		   return stackF.get( name );
        	   if( stackF.getType() == FrameType.FUNCTION )
        		   break;
        	}
            return null;
        }
    }
    
    public boolean isSomewhereDefined( String name, boolean global, int maxDepth )
    {
    	if( global )
    		return this.global.isDefined( name );
    	int index = stack.size() - 1;
    	while( index >= 0 && maxDepth >= 0 ) {
    	   StackFrame stackF = stack.get( index-- );
    	   if( stackF.isDefined( name ) )
    		   return true;
    	   if( stackF.getType() == FrameType.FUNCTION )
    		   break;
    	   maxDepth--;
    	}
    	return false;
    }
    
    public boolean isDefined( String name, boolean global )
    {
    	if( global )
    		return this.global.isDefined( name );
    	if( stack.size() == 0 )
    		return false;
    	return stack.peek().isDefined( name );
    }
    
    public void undeclare( String name, boolean global )
    {
        if( global )
            this.global.undeclare( name );
        else
        {
        	if( stack.size() == 0 )
        		return;
            stack.peek().undeclare( name );
        }
    }
}
