/**
* A utility class that can be used to protect an object whose methods
* issue asynchronous requests from starting a new request before the
* original one comes back.
*/
DeepFreeze = Class.create(
    {        
        /**
        * The object being "frozen"
        */
        target : null,
        /**
        * The methods that should be blocked when the object is in
        * a frozen state
        */
        protectedMethods : [],
        
        /**
        * Indicates whether the object is currently in a frozen
        * state
        *
        * @var Boolean
        */
        frozen: false,
        
        /**
        * Creates a new Deep Freeze instance and attaches it
        * to an object, protecting the specified methods
        *
        * @param object target              the object being protected
        * @param Array  protectedMethods    the methods to protect
        */
        initialize : function(target, protectedMethods) {
            this.target = target;
            this.protectedMethods = protectedMethods;
            this.attach();
        },
        
        /**
        * Freezes the object, thereby preventing protected methods from
        * being invoked
        *
        * This method also fires DeepFreeze.Event.FREEZING before setting 
        * the flag and DeepFreeze.Event.FROZEN after freezing
        */
        freeze : function() {
            this.target.fire(DeepFreeze.Event.FREEZING);    
            this.frozen = true;
            this.target.fire(DeepFreeze.Event.FROZEN);
        },
        
        /**
        * Removes the freeze, thereby allowing protected methods to
        * be called again
        */
        unfreeze : function() {
            this.target.fire(DeepFreeze.Event.UNFREEZING);    
            this.frozen = false;
            this.target.fire(DeepFreeze.Event.UNFROZEN);
        },
        
        /**
        * Determines if the target has been frozen
        *
        * @return Boolean
        */
        isFrozen : function() {
            return this.frozen;
        },
        
        /**
        * Associates this Deep Freeze instance with the target
        */
        attach : function() {
            if (!this.target.fire) {
                new EventHandler(this.target);
            }
            this.target.isFrozen = this.isFrozen.bind(this);
            this.target.freeze = this.freeze.bind(this);
            this.target.unfreeze = this.unfreeze.bind(this);
            this.protectMethods();            
        },
        
        /**
        * Protects methods specified in the protectedMethods
        * argument to the constructor
        */
        protectMethods : function() {
            $A(this.protectedMethods).each(
                this.protectMethod.bind(this)
            );
        },
        
        /**
        * Protects the method with the specified name on the
        * target by replacing it with a lambda that calls the 
        * original method only if the target has not been frozen
        *
        * @param string 
        */
        protectMethod : function(methodName) {
            if (this.target[methodName] && typeof(this.target[methodName]) == 'function') {
                var originalMethod = this.target[methodName];
                this.target[methodName] = function() {                
                    if (!this.isFrozen()) {
                        this.freeze();
                        originalMethod.apply(this, arguments);
                    }
                }
            }
        }        
    }
);

DeepFreeze.Event = {
    FREEZING : 'freezing',
    FROZEN: 'frozen',
    UNFREEZING: 'unfreezing',
    UNFROZEN: 'unfrozen'
}
