function log(txt) {
    if ("console" in window && "log" in console) {
        console.log(txt);
    }
}

Ajax.Application = {}

Ajax.Application.Base = Class.create({
    sendRequest : function(url, callback, requestData){
        new Ajax.Request(url, {
            parameters   : requestData || {},
            onComplete   : this.receiveRequest.bind(this, url, callback),
            onFailure    : this.ajaxFailure.bind(this, url),
            onException  : this.ajaxException.bind(this, url)
        });
    },

    receiveRequest : function(url, callback, response){ callback(url, response); },

    ajaxFailure : function(){
        log("Ajax Request failed args = %o ", arguments);
    },

    ajaxException : function(url, exception){
        log("ajax exception occured args = %s", exception);
    }
});

Ajax.Application.Event = Class.create(Ajax.Application.Base, {
    buildListenerChain : function(){
       if(!this.listenerChain)
            this.listenerChain = {};
    },

    addEventListener : function(type, listener){
       if(!listener instanceof Function)
            throw { message : "Listener isn't a function" };

       this.buildListenerChain();                        

       if(!this.listenerChain[type])
            this.listenerChain[type] = [listener];
       else
            this.listenerChain[type].push(listener);
    },

    hasEventListener : function(type){
       return (typeof this.listenerChain[type] != "undefined");
    },

    removeEventListener : function(type, listener){
       if(!this.hasEventListener(type))
            return false;

       for(var i = 0; i < this.listenerChain[type].length; i++)
            if(this.listenerChain[type][i] == listener)
                 this.listenerChain.splice(i, 1);
    },

    dispatchEvent : function(type, args){
        this.buildListenerChain();

       if(!this.hasEventListener(type))
            return false;

       this.listenerChain[type].any(function(f){ return (f(args) == false ? true : false); });
    }
});

Ajax.Service = {};
Ajax.Service.Base = Ajax.Application.Event;

Ajax.Service.History = Class.create(Ajax.Service.Base, {
    initialize : function(container){
        this.createHashObserver();
        this.buildInterface(container);
        this.createListener();
        this.attachListener();
    },

    createHashObserver : function() {
        var service = this;
        window.setInterval(function() { service.checkCurrentPage.bind(service)(); }, 1000);
        return true;
    },

    checkCurrentPage : function() {
        var historyEvent = this.getHistoryObject();
        if ( historyEvent != null ) {
            if ( window.location.hash != this.historyKey ) {
                this.handleReloadByTimer(window.location.hash);
            }
        }
        return true;
    },

    sendRequest : function(url, callback, parameters){
        this.dispatchEvent("request", {
            url: url,
            callback : callback,
            parameters: parameters
        });

        Ajax.Application.Event.prototype.sendRequest.apply(this, [url, callback, parameters]);
    },

    receiveRequest : function(cb, eAja){
        this.dispatchEvent("response", eAja);
        Ajax.Application.Event.prototype.receiveRequest.apply(this, [cb, eAja]);
    },

    buildInterface : function(obj) {
        this.objectId = obj;
        this.historyArr = [];
        this.historyIndex = undefined;

        if ( Prototype.Platform.Windows && Prototype.Browser.IE ) {
            this.container = $(obj);
            this.historyFrame = this.container.down("iframe");
            this.form = this.container.down("form");            
        }
    },
    
    attachListener : function() {
        if ( Prototype.Platform.Windows && Prototype.Browser.IE ) {
            Event.observe(this.historyFrame, "load", this.reloadHandle);
        }
    },

    createListener : function() {
        this.reloadHandle = this.handleReload.bindAsEventListener(this);
    },

    getHistoryObject : function() {
        return this.historyArr[this.getHistoryIndex()];        
    },

    handleReloadByTimer: function(key) {
        var idx = -1;
        var obj = null;

        if ( this.historyArr.find(function(historyEvent, index) {
            idx = index;
            obj = historyEvent;

            return (historyEvent && historyEvent.arg && (historyEvent.arg[0] == key));
        }) ) {
            this.reloadHistoryEvent(obj, idx);
        }
    },

    handleReload : function(e) {
        var index = parseInt(this.getHistoryIndex());
        var obj = this.historyArr[index];

        if (!obj)
            return true;

        this.reloadHistoryEvent(obj, index);
    },

    reloadHistoryEvent: function(object, index) {
        this.historyIndex = index + 1;

        this.dispatchEvent("reload", [object, index]);
//        this.historyKey = object.arg[0];
        this.dispatchEvent(object.type, object.arg);
    },

    getHistoryIndex : function() {
        if ( Prototype.Platform.Windows && Prototype.Browser.IE ) {
            return this.getIndex(this.historyFrame.contentWindow.location.toString());
        } else {
            return this.historyIndex - 1;
        }
    },

    getIndex : function(str) {
        return str.replace(/.*index=/gi, "");
    },

    getQuery : function(str) {
        return str.replace(/[^?]+?/gi, "");
    },

    registerRequest : function(type, requestParameters) {
        if ( Prototype.Browser.IE && Prototype.Platform.Windows ) {
            if (this.historyIndex && this.historyIndex < this.historyArr.length)
                this.historyArr.length = this.historyIndex;

            this.form.index.value = this.historyArr.length;
        }

        this.historyArr.push({ type : type, arg : requestParameters});
        this.historyIndex = (typeof this.historyIndex != "undefined") ? this.historyIndex + 1 : 0; 
        this.historyKey = requestParameters[0].replace(/index.jsp$/, '');
        document.location.hash = this.historyKey;
        this.dispatchEvent(type, requestParameters);
        if ( Prototype.Browser.IE && Prototype.Platform.Windows ) {
  	     this.form.submit();            
        }
    }
});

Ajax.Service.Listener = Class.create({
    createListener : function() { this.receiveUpdateHandle = this.receiveUpdate.bind(this); },
    receiveUpdate : function(updateData){ this.update(updateData); },
    update : function(response){
        throw { message : "Overwrite this method in subclass" }
    }
});

var DocumentLoad = Class.create(Ajax.Service.Listener, {
    initialize : function(application){
        this.application = application;
        this.createListener();
    },

    update : function(updateData){
        if ( updateData[0] != this.application.historyService.historyKey ) {
            if ( Prototype.Browser.IE ) {
                document.location.hash = updateData[0];
            } 
            
            this.application.historyService.historyKey = updateData[0];                        
            this.application.navigationEntrySelected(updateData[0].replace(/^#/, ''), updateData[1], false);
        }
    }
});

var ModelLoad = Class.create(Ajax.Service.Listener, {
    initialize : function(application){
        this.application = application;
        this.createListener();
    },

    update : function(updateData){
        if ( updateData[0] != this.application.historyService.historyKey ) {
            if ( Prototype.Browser.IE ) {
                document.location.hash = updateData[0];
            }

            this.application.historyService.historyKey = updateData[0];                        
            this.application.modelNavigationUriSelected(updateData[0].replace(/^#/, ''));
        }
    }
})