/** @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(managementService) {
        this.managementService = managementService;
        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, 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"});
        }
    },

    onMenuItemSelected: function (item) {
        if (item && !item.handled) {
            this._onClick$MenuItem(item.id);
        }
    },

    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)) {
            var index = $.inArray(id, this.started);
            if (index != -1) {
                var module = this.modules[id];
                module.onPause();
                module.onStop();
                this.started.splice(index, 1);
                if (module == this.current) {
                    this.current = null;
                }
            }
        }
    },

    _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; });
    },

    _createView: function () {
        this.menu = new jscape.Menu();
        this.menu.bind($.proxy(this.onMenuItemSelected, this), "application");
    },

    _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();
    },

    _onClick$MenuItem: function(id) {
        if ("menu-logout" == id) {
            this.managementService.logout();
        } else if ("menu-license" == id) {
            var c = new jscape.LicenseController();
            c.start(this.managementService)
                    .done(function () { $(document).triggerHandler("broadcast", window.R.string.APPLICATION_SUCCESS_LICENSE); })
                    .done($.proxy(function () {
                              var current = this.current;
                              $.each(this.modules, $.proxy(function (id) { this._forceStopModule(id); }, this));
                              $.each(this.modules, $.proxy(function (id, module) {
                                  if (current == module) {
                                      this._forceStartModule(id);
                                      return false;
                                  }
                              }, this));
                          }, this));
        }
    },

    _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.ApplicationModule = Class.extend(
    /** @lends {jscape.ApplicationModule.prototype} */
{
    managementService: null,

    /** @constructor */
    initialize: function() {
    },

    onCreate: function(managementService) {
        this.managementService = managementService;
    },

    onStart: function() {
    },

    onStop: function() {
    },

    onPause: function() {
    },

    onResume: function() {
    },

    _showConfirmApply: function() {
        if (window.originalEvent && window.originalEvent.type === "beforeunload") {
            window.originalEvent.returnValue = window.R.string.APPLICATION_CONFIRM_LEAVE;
            return $.Deferred().reject().promise();
        }

        var deferred = $.Deferred();
        var promise = deferred.promise();
        var dlg = $.messager.confirm({
            title: window.R.string.APPLICATION_TITLE,
            msg: window.R.string.APPLICATION_CONFIRM_APPLY,
            buttons: [
                {
                    text: window.R.string.DIALOG_BUTTON_APPLY,
                    onClick: function () {
                        dlg.dialog("close");
                        deferred.resolve(true);
                        dlg = deferred = null;
                    }
                },
                {
                    text: window.R.string.DIALOG_BUTTON_DISCARD,
                    onClick: function () {
                        dlg.dialog("close");
                        deferred.reject();
                        dlg = deferred = null;
                    }
                }
            ]
        });
        return promise;
    },

    _confirmAndApply: function (controllers) {
        if (arguments.length == 1 && !$.isArray(controllers)) {
            controllers = [controllers];
        }
        if (controllers.length == 0) {
            return;
        }
        return this._showConfirmApply()
                .done(function () {
                          for (var i = 0; i < controllers.length; i++) {
                              if (typeof controllers[i].onApply == "function") {
                                  controllers[i].onApply();
                              }
                          }
                      })
                .fail(function () {
                          for (var i = 0; i < controllers.length; i++) {
                              if (typeof controllers[i].onDiscard == "function") {
                                  controllers[i].onDiscard();
                              }
                          }
                      });
    }
});

/** @class */
jscape.DashboardView = Class.extend(
    /** @lends {jscape.DashboardView.prototype} */
{
    /** @constructor */
    initialize: function(modules) {
        $("body").css("overflow", "hidden");
        this.throbber = $("#throbber").show();
        this.viewport = $("#viewpane").layout({fit: true, border: false, width: "100%", height: "100%"});
        this.viewport.css("visibility", "hidden");
        this.lastLoginDate = $("#last-login");
        this.modules = modules;
    },

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

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

    _adjustLoginInfo: function() {
        var time = parseInt(this.lastLoginDate.data("timestamp"));
        if (!isNaN(time)) {
            this.lastLoginDate.text(new Date(time).toLocaleString());
        }
        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.LicenseController = Class.extend(
    /** @lends {jscape.LicenseController.prototype} */
{
    start: function(managementService) {
        this.managementService = managementService;
        this.deferred = $.Deferred();
        this._setupDialog().show(this);
        return this.deferred.promise();
    },

    onSubmit: function(dialog) {
        this.managementService.importLicense(dialog.getFile())
                .done($.proxy(function() { this.deferred.resolve(); }, this))
                .fail($.proxy(function() { this.deferred.reject(); }, this))
                .always($.proxy(function() { dialog.destroy(); this.deferred = null; }, this));
    },

    onCancel: function(dialog) {
        dialog.destroy();
        this.deferred.reject();
        this.deferred = null;
    },

    _setupDialog: function() {
        return new jscape.LicenseDialog();
    }
});

/** @class */
jscape.LicenseDialog = Class.extend(
    /** @lends {jscape.LicenseDialog.prototype} */
{
    /** @constructor */
    initialize: function() {
        this.dialog = $("#d-license-import").clone().attr("id", "");
        this.dialog.dialog({
            width: 600, height: "auto",
            modal: true, resizable: false, closable: true, closed: true,
            buttons: [
                {text: window.R.string.DIALOG_BUTTON_OK || "Ok", handler: $.proxy(this._onSubmit, this)},
                {text: window.R.string.DIALOG_BUTTON_CANCEL || "Cancel", handler: $.proxy(this._onCancel, this)}
            ],
            onClose: $.proxy(function () {if (!this.closing) { this._onCancel(); }}, this)
        }).show();

        this.fieldset = $("fieldset[name=importlicensefields]", this.dialog);

        this.file = $("input[name=file]", this.fieldset).filebox({required: true, width: "60%"});
    },

    getFile: function () {
        return this.file.filebox("files")[0];
    },

    show: function (listener) {
        this.listener = listener;
        this.dialog.dialog("center").dialog("open");
        this.fieldset.form("validate");
        this.file.filebox("textbox").focus();
    },

    hide: function () {
        if (!this._closed()) {
            this.closing = true;
            this.dialog.dialog("close");
            this.fieldset.form("disableValidation");
            this.closing = false;
        }
    },

    destroy: function() {
        this.dialog.dialog("destroy");
    },

    _onSubmit: function () {
        if (this.fieldset.form("validate")) {
            this.hide();
            this.listener.onSubmit(this);
        }
    },

    _onCancel: function () {
        this.hide();
        this.listener.onCancel(this);
    },

    _closed: function () {
        return !!this.dialog.dialog("options").closed;
    }
});

/** @class */
jscape.Menu = Class.extend(
    /** @lends {jscape.Menu.prototype} */
{
    /** @constructor */
    initialize: function () {
        $("[role=menubar]")
                .children("[role=menu]")
                .children("[role=menuitem]")
                .each($.proxy(function (_, entry) {
                          entry = $(entry);
                          var label = entry.find("#" + entry.attr("aria-labelledby"));
                          if (label.length) {
                              if (/true/.test(entry.attr("aria-haspopup"))) {
                                  var popup = entry.find("[role=menu]");
                                  label.menubutton({plain: true, menu: popup});
                                  $(label.menubutton("options").menu).menu({
                                      onClick: function (item) {
                                          if (item.id || item.name || item.href) {
                                              $("body").triggerHandler("menu.application", {
                                                  id: item.id || item.name,
                                                  url: item.href || "",
                                                  handled: $(item).is("a") && item.href
                                              });
                                          }
                                      }
                                  });
                              } else {
                                  label.linkbutton({
                                      plain: true, onClick: function () {
                                          var t = $(this);
                                          $("body").triggerHandler("menu.application", {
                                              id: t.linkbutton("options").id,
                                              url: t.prop("href") || "",
                                              handled: t.is("a") && t.prop("href")
                                          });
                                      }
                                  });
                              }
                          }
                      }, this));
    },

    bind: function(fn, namespace) {
        if (typeof fn == "function") {
            $("body").on("menu.application" + (namespace ? "." + namespace : ""), function(e, item) { return fn(item); });
        }
    },

    unbind: function(namespace) {
        $("body").off("menu.application" + (namespace ? "." + namespace : ""));
    }
});