window.jscape = window.jscape || {};

/**Class inheritance */
(function () {
    var fnTest = /xyz/.test(function () {xyz;}) ? /\b_super\b/ : /.*/;
    var initializing = false;
    this.Class = function () {};
    Class.extend = function (prop) {
        var _super = this.prototype;
        initializing = true;
        var prototype = new this();
        initializing = false;

        for (var name in prop) {
            var overwriting = typeof prop[name] === "function"
                              && typeof _super[name] === "function"
                              && fnTest.test(prop[name]);
            prototype[name] = overwriting
                    ? (function (name, fn) {
                return function () {
                    var ref = this._super;
                    this._super = _super[name];
                    var result = fn.apply(this, arguments);
                    this._super = ref;
                    return result;
                }
            })(name, prop[name])
                    : prop[name];
        }
        function Class() {
            if (!initializing && this.initialize) {
                this.initialize.apply(this, arguments);
            }
        }

        Class.prototype = prototype;
        Class.prototype.constructor = Class;
        Class.extend = arguments.callee;
        return Class;
    };
})();

String.prototype.escapeXml = function() {
    var entities = {"<": "&lt;", ">": "&gt;", "&": "&amp;", '"': "&quot;"};
    return this.replace(/[<>&"]/g, function(entry) { return entities[entry]});
};
String.prototype.supplant = function(substitution) {
    return this.replace(
            /\{([^{}]*)\}/g,
            function (value, variable) {
                var replacement = substitution[variable];
                return typeof replacement === "string" || typeof replacement === "number" || typeof replacement === "boolean"
                        ? replacement
                        : value;
            }
    );
};

/** @class */
jscape.Application = Class.extend(
    /** @lends {jscape.Application.prototype} */
{
    modules: null,
    started: null,
    current: null,

    /** @constructor */
    initialize: function() {
        this.modules = {};
        this.started = [];
        $.parser.auto = false;

        $(window).on("beforeunload", $.proxy(this._onBeforeUnload, this));
        $(window).one("unload", $.proxy(this._destroy, this));
        $(document).on("broadcast", $.proxy(this.onBroadcast, this));
    },

    register: function(id, module) {
        this.modules[id] = module;
    },

    start: function () {
        var args = $.makeArray(arguments);
        $(document).ready($.proxy(function () { this._launch.apply(this, args); args = null; }, this));
    },

    onReady: function() {
    },

    onBroadcast: function(e, message, success) {
        if (message && message.trim().length) {
            $.messager.show({
                msg: message, escapeXml: true, noheader: true, height: "auto", showType: "fade", showSpeed: 200, border: "thin",
                style: {top: document.body.scrollTop + document.documentElement.scrollTop + 20, right: "", bottom: "", "border-color": success == false ? "#FFA8A8" : "#808080"}
            }).window("body").css({"background-color": success == false ? "#FFF3F3" : "#C1F0C1", "padding-bottom": "1em", "text-align": "center"});
        }
    },

    onSelectModule: function (id) {
        if (!this.modules.hasOwnProperty(id)) {
            return;
        }

        var previous = this.current;
        this.current = this.modules[id];

        if (previous != this.current) {
            if (previous) {
                previous.onPause();
            }
            if ($.inArray(id, this.started) == -1) {
                this.started.push(id);
                this.current.onStart();
            }
            this.current.onResume();
        }
    },

    _forceStartModule: function(id) {
        this.onSelectModule(id);
    },

    _forceStopModule: function(id) {
        if (this.modules.hasOwnProperty(id) && $.inArray(id, this.started) != -1) {
            this.modules[id].onPause();
            this.modules[id].onStop();
        }
    },

    _launch: function () {
        var args = $.makeArray(arguments);
        this._parseLayout()
                .then($.proxy(function () {
                          var d = $.Deferred();
                          try {
                              this._createView();
                              $.each(this.modules, function (id, module) {
                                  module.onCreate.apply(module, args);
                              });
                              args = null;
                              d.resolve();
                          } catch (e) {
                              d.reject(e);
                          }
                          return d.promise();
                      }, this))
                .done($.proxy(this.onReady, this))
                .fail(function (e) { throw "Error starting application: " + e; }, this);
    },

    _createView: function() {
    },

    _parseLayout: function() {
        var d = $.Deferred();
        var h = $.parser.onComplete;
        $.parser.onComplete = function () {
            $.parser.onComplete = h;
            d.resolve();
            h = null;
            d = null;
        };
        setTimeout(function () {
            $.parser.parse(document);
        }, 0);
        return d.promise();
    },

    _onBeforeUnload: function(e) {
        window.originalEvent = e;
        var started = [].concat(this.started);
        $.each(this.modules, function (id, module) {
            if ($.inArray(id, started) != -1) {
                module.onPause.call(module);
            }
        });
        window.originalEvent = null;
        return e.returnValue || undefined;
    },

    _destroy: function () {
        $(document).off("broadcast");
        var started = this.started;
        $.each(this.modules, $.proxy(function (id, module) {
            if ($.inArray(id, started) != -1) {
                module.onStop.call(module);
            }
        }, this));
        this.modules = null;
        this.started = [];
    }
});

/** @class */
jscape.DashboardView = Class.extend(
    /** @lends {jscape.DashboardView.prototype} */
{
    /** @constructor */
    initialize: function(modules) {
        this.modules = modules;

        $("body").css("overflow", "hidden");
        this.throbber = $("#throbber").show();
        this.viewport = $("#viewpane").css("visibility", "hidden");
        this.lastLoginDate = $("#last-login");
    },

    show: function(listener) {
        this.listener = listener || jscape.DashboardView.LISTENER;
        this.throbber.remove();
        this.throbber = null;

        this.viewport.css("visibility", "visible");
        $("body").css("overflow", "");
        this._adjustLoginInfo();
    },

    _adjustLoginInfo: function() {
        var time = parseInt(this.lastLoginDate.data("timestamp"));
        var locale = this.lastLoginDate.data("locale");
        if (!isNaN(time)) {
            this.lastLoginDate.text(new Date(time).toLocaleString(locale));
        }
        this.lastLoginDate.show();
    },

    _onSelect$Tab: function(title, index) {
        if (this.listener) {
            var tab = this._findSelected(this.modules);
            if (tab != null && tab.length) {
                var opts = tab.panel("options");
                this.listener.onSelectModule(opts.id || opts.title);
            }
        }
    },

    _findSelected: function(parent) {
        if (parent == null || parent.length == 0) {
            return null;
        }
        for (var i = 0; i < parent.length; ++i) {
            var tab = $(parent[0]).tabs("getSelected");
            if (tab != null && tab.panel("options").id) {
                return tab;
            }
        }
        return this._findSelected(this._submodule(parent));
    },

    _submodule: function(parent) {
        return parent && parent.length
                ? parent.find("div[role=tablist]").filter(function () { return $(this).parents("div.dialog").length == 0; })
                : null;
    }

});
jscape.DashboardView.LISTENER = {
    onSelectModule: $.noop
};

/** @class */
jscape.ApplicationModule = Class.extend(
    /** @lends {jscape.ApplicationModule.prototype} */
{
    onCreate: function() {
    },

    onStart: function() {
    },

    onPause: function() {
    },

    onResume: function() {
    },

    onStop: function() {
    }
});

/** @class */
jscape.InactivityServiceModule = jscape.ApplicationModule.extend(
    /** @lends {jscape.InactivityServiceModule.prototype} */
{
    timerId: null,
    reminderId: null,

    /** @constructor */
    initialize: function(timeout, gracePeriod) {
        this.timeout = parseInt(timeout) || 0;
        this.gracePeriod = parseInt(gracePeriod) || 0;
    },

    onStart: function() {
        if (this.timeout > 0) {
            this._startTimer();
            $(document).ajaxStart($.proxy(this.onResume, this));
            $(document).on("_activity.user", $.proxy(this.onResume, this));
        }
    },

    onResume: function() {
        this._startTimer();
    },

    onStop: function() {
        $(document).off(".user");

        this.timerId && clearTimeout(this.timerId);
        this.timerId = null;

        this.reminderId && clearInterval(this.reminderId);
        this.reminderId = null;

        this.dialog && this.dialog.destroy();
        this.dialog = null;
    },

    onTimeout: function() {
        this.dialog = new jscape.InactivityReminderDialog().show(this);
        this._startReminder(this.dialog);
    },

    onReminderTimeout: function() {
        this.onStop();
        jscape.API.logout();
    },

    onSubmit: function(dialog) {
        clearInterval(this.reminderId);
        dialog.destroy();
        this.onResume();
    },

    _startTimer: function() {
        if (this.timerId) {
            clearTimeout(this.timerId);
        }
        this.timerId = setTimeout($.proxy(function() {
            this.timerId = null;
            this.onTimeout();
        }, this), this.timeout);
    },

    _startReminder: function(dialog) {
        if (this.reminderId) {
            clearInterval(this.reminderId);
        }
        var remainingTime = this.gracePeriod;
        function _remainder(timeout) {
            dialog.setReminder(Math.round(timeout/1000), Math.round(remainingTime/1000));
        }
        _remainder(this.timeout);
        this.reminderId = setInterval($.proxy(function() {
            if (remainingTime > 0) {
                _remainder(this.timeout);
            } else {
                this.onReminderTimeout();
                remainingTime = null;
            }
            remainingTime -= 1000;
        }, this), 1000);
    }
});

/** @class */
jscape.InactivityReminderDialog = Class.extend(
    /** @lends {jscape.InactivityReminderDialog.prototype} */
{
    setReminder: function(timeout, remainTime) {
        if (this.dialog) {
            var button = this.dialog.dialog("options").buttons[0].text;
            var message = window.R.string.APPLICATION_CONFIRM_ACTIVE.supplant({0: timeout, 1: "<b>" + button + "</b>", 2: "<b>" + remainTime + "</b>"});

            var panel = this.dialog.dialog("body");
            panel.html(panel.html().replace(this.reminder, message));
            this.reminder = message;
        }
    },

    show: function (listener) {
        this.reminder = window.R.string.APPLICATION_CONFIRM_ACTIVE;
        this.dialog = $.messager.alert({title: window.R.string.APPLICATION_TITLE, msg: this.reminder, icon: "warning", timeout: 0, fn: $.proxy(this._onSubmit, this)});
        this.listener = listener;
        return this;
    },

    destroy: function() {
        this.dialog = null;
        this.listener = null;
    },

    _onSubmit: function () {
        this.listener.onSubmit(this);
    }
});
