try
{
    wedebug.addLogger("state", "StateManager");
}
catch(e)
{
    alert(e.message);
}



var StateManager = Class.create();
StateManager.Mouse = {
    CONTEXT_MENU : 1,
    MOVE : 2,
    UP : 4,
    DOWN : 8,
    OUT : 16,
    WHEEL : 32,
    DOUBLE_CLICK : 64,
    ALL : 127
};

StateManager.Button = {
    LEFT : 1,
    RIGHT : 2,
    MIDDLE : 3
};

StateManager.prototype = {
    m_navigationDisabled : true,
    m_mapController : null,
    m_element : null,
    m_stateNamesHashtable : new Hash(),
    m_stateListenersHashtable : new Hash(),
    m_globalListeners : [],
    m_states : [],
    m_nextState : 1,
    m_currentState : null,
    m_domExtensionAttributeName : "_stateextended",

    _panEventFired : false,
    _lastX : null,
    _lastY : null,
    _lastPositionX : 0,
    _lastPositionY : 0,
    _isMouseDown : false,
    _isDragging : false,
    
    initialize : function(t_element, t_mapController)
    {
        wedebug.log("state", "initialize: " + [t_element, t_mapController]);
    
        this.m_mapController = t_mapController;
        this.m_element = t_element;
        this._interceptEvents(t_element);
        t_element.$attr(this.m_domExtensionAttributeName, "0");
    },
    
    //throws exception if state exists or evaluation of state fails.
    addState : function( t_stateConstantName, t_callback )
    {
        //Eval state name
        if( $defined(eval(t_stateConstantName)) )
        {
            throw {message : "Cannot define state with name " + t_stateConstantName + " because it is already defined in scope."};
        }
        else
        {
            var t_stateConstant = null;
            try
            {
                wedebug.log("state", "Evaluating expression '" + t_stateConstantName + "=" + this.m_nextState + "'.");
                eval(t_stateConstantName + "=" + this.m_nextState); 
                t_stateConstant = eval(t_stateConstantName);
                this.m_nextState *=2;           
            }
            catch(e)
            {
                throw {message : "Evaluation of state " + t_stateConstantName + " failed. " + e.message};
            }
            
            this.m_states.push(t_stateConstant);
            this.m_stateNamesHashtable[t_stateConstant] = this.m_stateNamesHashtable[t_stateConstantName] = t_stateConstantName;
            this.m_stateListenersHashtable[t_stateConstant] = [];
            //Update dom attribute on element.
            
            this.m_element.setAttribute(this.m_domExtensionAttributeName, this.m_states.sum());
        }
    },
    
    setState : function( t_state )
    {
        if( this._checkValidState(t_state, true) )
        {
            this.m_navigationDisabled = t_state==0;
            this.m_currentState = t_state;   
        }
    },
    
    getState : function()
    {
        return this.m_currentState;
    },
    
    addStateListener : function( t_states, t_callback )
    {   
        //traverse state array, do bitwise checking. 
        var i, t_state;
        for(i=0; i<this.m_states.length; i++)
        {            
            t_state = this.m_states[i]; 
            if( (t_states & t_state) == t_state )
            {
                this.m_stateListenersHashtable[t_state].push(t_callback);
            }
        }
    },
    
    addGlobalStateListener : function( t_callback )
    {
        this.m_globalListeners.push(t_callback);
    },
    
    _checkValidState : function( t_state, t_throwException )
    {
        if( t_state == 0 ) return true;
    
        var t_existingState = this.m_stateNamesHashtable[t_state];
        if( $defined(t_existingState) )
        {
            return true;
        }
        else if ($defined(t_throwException) && t_throwException == true)
        {
            throw {message : "Invalid state. No state " + t_state + " exists."};
        }
        else return false;
    },
    
    _interceptEvents : function(t_element)
    {
        //Catch events.
        Event.observe(t_element, "contextmenu",
            this._onContextMenu.bind(this)
        );  
        
        Event.observe(t_element, "mousemove", 
            this._onMouseMove.bind(this)            
        );
        
        Event.observe(t_element, "mousedown",
            this._onMouseDown.bind(this)
        );
        
        Event.observe(t_element, "mouseup",
            this._onMouseUp.bind(this)
        );
        
        Event.observe(t_element, "mouseout",
            this._onMouseOut.bind(this)
        );
        
        Event.observe(t_element, "dblclick",
            this._onDblClick.bind(this)
        );
        
        /*C&P ajaxmap.js:239*/
        if (t_element.addEventListener) {
			t_element.addEventListener('DOMMouseScroll', this._onWheel.bind(this), false);
		}
        t_element.onmousewheel = this._onWheel.bind(this);
        
        wedebug.log("state", "Event observers registered.");
    },
        
    _notifyListeners : function(t_event, t_eventType, t_wheelDelta)
    {
        if( this.m_currentState != 0 && !this.m_navigationDisabled )
        {
            var t_listeners = this.m_stateListenersHashtable[this.m_currentState].concat(this.m_globalListeners);
            if( t_listeners.length > 0 )
            {
                try
                { 
                    var t_cumOffset = Position.cumulativeOffset(this.m_element);
                    var t_isLeftClick = Event.isLeftClick(t_event);
                    var t_pointerX = Event.pointerX(t_event) - t_cumOffset[0], t_pointerY = Event.pointerY(t_event) - t_cumOffset[1];
                    var t_latlon = this.m_mapController.getPixelLatLon({x: t_pointerX, y: t_pointerY});
                    
                    var i;
                    //wedebug.log("state", "Notifying event listeners: " + ["state: " + this.m_currentState, "x: " + t_pointerX, "y: " + t_pointerY, "lat: " + t_latlon.lat, "lon: " + t_latlon.lon,"isLeftClick: " + t_isLeftClick,"isMouseDown: " + this._isMouseDown,"moveX: " + this._isMouseDown ? -(this._lastX - t_pointerX) : 0, "moveY: " + this._isMouseDown ? -(this._lastY - t_pointerY) : 0]);
                    for( i=0; i<t_listeners.length; i++ )
                    {
                        try
                        {
                            t_listeners[i]( 
                                {
                                    state : this.m_currentState,
                                    eventType : t_eventType,
                                    x : t_pointerX,
                                    y : t_pointerY,
                                    lat : t_latlon.lat,
                                    lon : t_latlon.lon,
                                    isLeftClick : t_isLeftClick,
                                    isMouseDown : this._isMouseDown,
                                    moveX : this._isMouseDown ? -(this._lastX - t_pointerX) : 0,
                                    moveY : this._isMouseDown ? -(this._lastY - t_pointerY) : 0,
                                    event : t_event,
                                    wheelDelta : t_wheelDelta
                                });                                
                        }
                        catch(e)
                        {
                            wedebug.log("state", "Exception thrown invoking callback function. " + e.message, "error");
                        }
                    }
                }
                finally
                {
                    Event.stop(t_event);
                    return false;
                }
            }
        }
    },
    
    /**
    * Callback-function when div is right clicked (GLMap).
    */
    _contextMenuTimer : null,
    _onContextMenu : function(t_event)
    {     
        if( !this._isDragging )
        {     
            this._notifyListeners(t_event, StateManager.Mouse.CONTEXT_MENU);       
        }
        else
        {
            Event.stop(t_event);
            return false;
        }
    },
    
    _onMouseMove : function(t_event)
    {   
               var x = Event.pointerX(t_event);
            var y = Event.pointerY(t_event);
        //Trigger pan event.
        if( this._isMouseDown )
        {
            var x = Event.pointerX(t_event);
            var y = Event.pointerY(t_event);
                        
            if( this._lastX != x ||
                this._lastY != y )
            {
                this._isDragging = true;
            }
            
            this._lastX = x;
            this._lastY = y;
        }
        else
        {
            this._isDragging = false;
        }
        if((this._lastPositionX != x) && this._lastPositionY != y){ 
            this._notifyListeners(t_event, StateManager.Mouse.MOVE);
            this._lastPositionX = x;
            this._lastPositionY = y;
        }
    },
    
    _onMouseDown : function(t_event)
    {
        this._isDragging = false;
    
        this._lastX = Event.pointerX(t_event);
        this._lastY = Event.pointerY(t_event);
        
        this._isMouseDown = true;
        this._notifyListeners(t_event, StateManager.Mouse.DOWN);
    },
        
    _onMouseOut : function(t_event)
    {
        if( this._isMouseDown )
        {
            this._isMouseDown = false;
        }
        this._notifyListeners(t_event, StateManager.Mouse.OUT);
    },
    
    _onMouseUp : function(t_event)
    {
        if( this._isMouseDown )
        {
            this._isMouseDown = false;
        }
        this._notifyListeners(t_event, StateManager.Mouse.UP);
    },
    
    _onDblClick : function(t_event)
    {
        this._notifyListeners(t_event, StateManager.Mouse.DOUBLE_CLICK);
    },
    
    _onWheel : function(t_event)
    {
        var t_delta = 0;
	        
        if( !this.m_navigationDisabled )
        {
            if (!t_event){
	            t_event = window.event;
	        }
	        if (t_event.wheelDelta) {
		        t_delta = t_event.wheelDelta/120;
		        if (window.opera){
		            t_delta = -t_delta;
		        }
	        } else if (t_event.detail) {
		        t_delta = -t_event.detail/3;
	        }
	        if (t_event.preventDefault){
                t_event.preventDefault();
            }
            t_event.returnValue = false;
        }
        
        this._notifyListeners(t_event, StateManager.Mouse.WHEEL, t_delta);
    }
}

