/// Attention!  
/// Include of utils.js is required (it can looks like WrbResource.axd?...)!

//===============================================================
// UrlParamsManager class 
//===============================================================

// TODO: 
// Business rules to implement in future:
// - Hash-parameter override Query-parameter 
//  i.e. when exists Hash-parameter and Query-parameter with the same name Hash-parameter will be returned!

// Manage url-parameters and notify when hash changed.
// Allow accumulate parameter changes and the apply all changes
// Additional features: 
// - Singleton. All constructors return the same object instance.
function UrlParamsManager(initialUrl)
{
    // Singleton impl ---
        if(UrlParamsManager._Instance!=null)
            return UrlParamsManager._Instance;
        UrlParamsManager._Instance = this;    

    // Private fileds ---
        var _url = initialUrl ? initialUrl : sw_GetCurrentURL(); // Current url
        var _history = sw_HistoryManager.Instance();
        
    // Private methods ---
        var _ShowErr = function(mess){ alert(mess); return false; }
        var _OnHashChanged = function(){
            _url = sw_GetCurrentURL();
        }

    // Public interface ---
        this.SetParam = function(name, value){
            if(value == this.GetParam(name))
                return;
            else
                this.SetHashParam(name, value);
        }

        this.GetParam = function(name){
            var hVal = this.GetHashParam(name);
            if(hVal) 
                return hVal;
            else
                return this.GetQueryParam(name);
        }

        this.SetQueryParam = function(name, value){
            _url = sw_SetUrlParam(_url, name, value);
        }

        this.GetQueryParam = function(name){
            return sw_GetUrlParam(_url, name);
        }

        this.GetHashParam = function(name){
            return sw_GetHashParam(_url, name);
        }
        this.SetHashParam = function(name, value){
            _url = sw_SetHashParam(_url, name, value);
        }

        this.ClearHash = function(){ 
            _url = sw_SetWholeHash(_url);
        }

        // Apply new parameters
        this.Apply = function(silentMode){
            var currUrl =  sw_GetCurrentURL();
            if(currUrl==_url)
                return;
                
            if(sw_ExtractBaseUrl(currUrl) == sw_ExtractBaseUrl(_url)
               && sw_ExtractQuery(currUrl) == sw_ExtractQuery(_url) )
            {
                if(silentMode){
                    _history.SuppressNextEvent();
                }
                _history.SetHash(sw_ExtractHash(_url))
            }
            else
            {
                sw_Navigate(_url);
            }        
        }
        // Apply new parameters without raising event OnHashChanged 
        this.ApplySilent = function(){ this.Apply(true); }        

        this.IsHashNonempty = function(){ 
            var hash = sw_ExtractHash(_url);
            return (hash!='' && hash!=null); 
        }
        
        this.GetUrl = function(){ return _url; }    
        
        // Event handling
        this.AddChangeHandler = function(handler){ _history.AddChangeHandler(handler); }
        
        // Init
        _history.AddChangeHandler( _OnHashChanged );        
}

// "Static" methods ---
UrlParamsManager.Instance = function(){
    if(UrlParamsManager._Instance==null)
        return new UrlParamsManager();
    else
        return UrlParamsManager._Instance;
}


//===============================================================
// sw_HistoryManager class
//===============================================================

// Manage hash and notify when hash changed.
// Instant changes apply!
// Additional features: 
// - Singleton. All constructors return the same object instance.
function sw_HistoryManager()
{
    // Singleton impl ---
        if(sw_HistoryManager._Instance!=null)
            return sw_HistoryManager._Instance;
        sw_HistoryManager._Instance = this;

    // Private filds ---
        var _Strategy = (document.all ? new sw_HistoryManagerIE() : new sw_HistoryManagerFF() );
        var _SuppressNextEvent = false;
        var _OnChange = new EventHandler();
        var _OnChangeHandler = function(arg){ 
            if(_SuppressNextEvent){
                _SuppressNextEvent = false;
                return false;
            }    
            return _OnChange.RaiseEvent(arg); 
        }
        
    // Public interface ---
        this.SetHash = function(hash){ _Strategy.SetHash(hash); }
        this.GetHash = function(){ return _Strategy.GetHash(); }
        this.AddChangeHandler = function(handler){ _OnChange.AddHandler(handler); }
        //this.AddChangeHandler = function(handler){ _Strategy.AddChangeHandler(handler); }
        this.SuppressNextEvent = function(){ _SuppressNextEvent = true; }
    
    // Init
        _Strategy.AddChangeHandler(_OnChangeHandler);
    
}// sw_HistoryManager

// "Static" methods ---
sw_HistoryManager.Instance = function(){
    if(sw_HistoryManager._Instance == null)
        return new sw_HistoryManager();
    else    
        return sw_HistoryManager._Instance;
}


//===============================================================
// EventHandler class
//===============================================================

function EventHandler()
{
    var _handlers = new Array();
    this.AddHandler = function(handler){
        if(handler!=null && typeof(handler) == 'function')
            _handlers[_handlers.length] = handler;
    }
    
    this.RaiseEvent = function(arg){
        for(var i=0; i<_handlers.length ;i++)
            _handlers[i](arg);
    }
    
}//EventHandler

// "Static" methods ---
EventHandler.AttachEvent = function(obj, evType, fn)
{
    if (obj.addEventListener){
        obj.addEventListener(evType, fn, false);
        return true;
    } else if (obj.attachEvent){
        var r = obj.attachEvent("on"+evType, fn);
        return r;
    } else {
        var oldHanler = obj["on"+evType];
        if(oldHanler){
            obj["on"+evType] = function(e){
                oldHanler(e);
                fn(e);
            }
        }else{
            obj["on"+evType] = fn;
        }
        return true;
    }
}


//===============================================================
// sw_HistoryManagerFF class 
//===============================================================

// Singleton. All constructors return the same object instance.
function sw_HistoryManagerFF()
{
    // Singleton impl ---
        if(sw_HistoryManagerFF.Instance!=null)
            return sw_HistoryManagerFF.Instance;
        sw_HistoryManagerFF.Instance = this;
        
    // Public interface ---
        this.SetHash = function(hash){ _SetHash(hash); }
        this.GetHash = function(){ return _GetHash(); }
        this.AddChangeHandler = function(handler){ _OnChange.AddHandler(handler); }
        
    // Private members ---
        var _SetHash = function (hash){ 
            document.location.hash = ("#"+hash); 
        }
        var _GetHash = function (hash){
            if(hash==null)
                hash = document.location.hash;
            if(hash)
                hash = hash.substr(1);
            return hash;
        }    
        var _LastHash = _GetHash();
        var _TimerHandle = null;
        var _OnChange = new EventHandler();
        var _OnLoad = function () 
        {
            // this doesn't help, Opera is currently (9.23, 9.24) broken:
            if (window.opera && window.history) 
                history.navigationMode = 'compatible';
            _TimerHandle = window.setTimeout(_OnTick, 200);
        }
        
        var _OnTick = function() {
            _TimerHandle = null;
            var hash = _GetHash();
            if(_LastHash != hash){
                _OnChange.RaiseEvent(hash);
                _LastHash = hash;
            }        
            _TimerHandle = window.setTimeout(_OnTick, 200);
        }
        
        var _OnUnload = function() {
            if (_TimerHandle) {
                window.clearTimeout(_TimerHandle);
            }
        }
        
    // Initialization ---
        var _Init = function()
        {
            EventHandler.AttachEvent(window, "load", _OnLoad);
            EventHandler.AttachEvent(window, "unload", _OnUnload);
        }
        _Init();

}//sw_HistoryManagerFF


//===============================================================
// sw_HistoryManagerIE class 
//===============================================================

// Singleton. All constructors return the same object instance.
function sw_HistoryManagerIE()
{
    // Singleton impl ---
        if(sw_HistoryManagerIE._Instance!=null)
            return sw_HistoryManagerIE._Instance;
        sw_HistoryManagerIE._Instance = this;

    // Public interface ---
        this.SetHash = function(hash){ _SetHash(hash); }
        this.GetHash = function(){ return _GetHash(); }
        this.AddChangeHandler = function(handler){ _OnChange.AddHandler(handler); }

    // Private fields ---
        var _this = this;
        var _OnChange = new EventHandler();
        var _HistoryFrame = null;
        var _LastHash = null;
        var _TimerHandle = null;
    
    // Private methods ---
        var _OnLoad = function () 
        {
            _SetHash(_GetHash());
            _TimerHandle = window.setTimeout(_OnTick, 200);
        }
        
        var _OnTick = function() {
            _TimerHandle = null;
            var hash = _GetHash();
            if(_LastHash != hash){
                _OnChange.RaiseEvent(hash);
                _LastHash = hash;
            }        
            _TimerHandle = window.setTimeout(_OnTick, 200);
        }
        
        var _OnUnload = function() {
            if (_TimerHandle) {
                window.clearTimeout(_TimerHandle);
            }
        }
    
        var _GetHistoryFrameWindow = function(){
            if(_HistoryFrame==null){
                _HistoryFrame = document.getElementById("historyFrame");
                if(!_HistoryFrame){
                    _HistoryFrame = document.createElement("iframe");
                    _HistoryFrame.id = "historyFrame";
                    _HistoryFrame.style.display = "none";
                    if(document.body){
                        document.body.appendChild(_HistoryFrame);
                    }else{
                        alert('sw_HistoryManagerIE cannot be created before document.body completely loaded!')
                        return null;
                    }
                }
                if(_HistoryFrame.style.display != "none")
                    _HistoryFrame.style.display = "none";
            }
            return _HistoryFrame.contentWindow;
        }
        var _OnFrameLoaded = function(hash) {
            var hash = decodeURI(hash);
            if(_LastHash!=hash){
                _LastHash = hash;
                window.location.hash = hash;
                _OnChange.RaiseEvent(hash);
            }
        }
        
        var _SetHash = function(hash){
            var encodedHash = encodeURI(hash);
            window.location.hash = encodedHash;
            var doc = _GetHistoryFrameWindow().document;
            doc.open("javascript:'<html></html>'");
            //doc.write("<html><head><scri"+"pt type=\"text/javascript\"> parent.HistoryManagerIE_OnFrameLoaded(\""+encodedHash+"\"); </scri"+"pt></head><body>"+hash+"</body></html>");
            doc.write("<html><head><script type=\"text/javascript\"> parent.HistoryManagerIE_OnFrameLoaded(\""+encodedHash+"\"); </script></head><body>"+hash+"</body></html>");
            doc.close();
        }
        var _GetHash = function(hash){
            if(hash==null){
                hash = window.location.hash;
            }
            if(hash){
                hash = hash.substr(1);
            }
            return hash;
        }        
    
    // Initialization ---
    var _Init = function(){
        window.HistoryManagerIE_OnFrameLoaded = _OnFrameLoaded;
        _LastHash = _GetHash();
        // Initialize IFrame (do not delete code below)
        EventHandler.AttachEvent(window, "load", _OnLoad);
        EventHandler.AttachEvent(window, "unload", _OnUnload);        
        //EventHandler.AttachEvent(window, "load", function(){ _SetHash(_GetHash()); });
    }
    _Init();
    
}//sw_HistoryManagerIE
