jscape.keystore = jscape.keystore || {};

/** @class */
jscape.keystore.PgpKeysModule = jscape.keystore.KeystoreModule.extend(
    /** @lends {jscape.keystore.PgpKeysModule.prototype} */
{
    /** @constructor */
    initialize: function () {
        this.controller = new jscape.keystore.PgpKeysController();
    },

    onStart: function() {
        this._super();

        this.controller.start(this.managementService);
    },

    onResume: function() {
        this.controller.onResume();
    }
});

/** @class */
jscape.keystore.PgpKeysController = Class.extend(
    /** @lends {jscape.keystore.PgpKeysController.prototype} */
{
    /** @constructor */
    initialize: function() {
        this.view = new jscape.keystore.PgpKeysView();

        this.importController = new jscape.keystore.ImportPgpKeyController();
        this.exportPublicKeyController = new jscape.keystore.ExportPgpPublicKeyController();
        this.exportSecretKeyController = new jscape.keystore.ExportPgpSecretKeyController();
        this.generateController = new jscape.keystore.GeneratePgpKeyController();
        this.signController = new jscape.keystore.SignPgpKeyController();
        this.viewController = new jscape.keystore.ViewPgpKeySignaturesController();
        this.deleteController = new jscape.keystore.DeletePgpKeyController();

        this._initValidators();
    },

    start: function(managementService) {
        this.managementService = managementService;
        this.view.show(this);
    },

    onResume: function() {
        this.view.refresh();
    },

    onRefresh: function () {
        return $.when(this.managementService.getPgpPublicKeys(), this.managementService.getPgpSecretKeys());
    },

    onImport: function () {
        this.importController.start(this.managementService)
                .done($.proxy(function (key, isSecretKey) {
                          if (isSecretKey) {
                              this._secretKeyImported(key);
                          } else {
                              this._publicKeyImported(key);
                          }
                      }, this));
    },

    onGenerate: function() {
        this.generateController.start(this.managementService)
                .done($.proxy(this._keyGenerated, this));
    },

    onExportPublicKey: function(key) {
        this.exportPublicKeyController.start(this.managementService, key.name, false);
    },

    onExportSecretKey: function(key) {
        this.exportSecretKeyController.start(this.managementService, key.name);
    },

    onExportSecretPublicKey: function(key) {
        this.exportPublicKeyController.start(this.managementService, key.name, true);
    },

    onDeletePublicKey: function(key) {
        this.deleteController.start(this.managementService, key.name, false)
                .done($.proxy(this._publicKeyDeleted, this, key));
    },

    onDeleteSecretKey: function(key) {
        this.deleteController.start(this.managementService, key.name, true)
                .done($.proxy(this._secretKeyDeleted, this, key));
    },

    onSign: function(key) {
        this.signController.start(this.managementService, key.name, this.view.getSecretKeys());
    },

    onViewPublicKeySignatures: function(key) {
        this.viewController.start(this.managementService, key.name, false);
    },

    onViewSecretKeySignatures: function(key) {
        this.viewController.start(this.managementService, key.name, true);
    },

    _publicKeyImported: function (key) {
        this.view.getPublicKeys().push(key);
        this.view.addItem(key);
        this.view.selectItem(key);
    },

    _secretKeyImported: function (key) {
        this.view.getSecretKeys().push(key);
        this.view.addItem(key);
        this.view.selectItem(key);
    },

    _keyGenerated: function(key) {
        this.view.getSecretKeys().push(key);
        this.view.addItem(key);
        this.view.selectItem(key);
    },

    _publicKeyDeleted: function(key) {
        var items = this.view.getPublicKeys();
        var index = $.inArray(key, items);
        if (index != -1) {
            items.splice(index, 1);
            this.view.deleteItem(key);
        }
        if (items.length) {
            index = Math.max(0, Math.min(index, items.length - 1));
            this.view.selectItem(items[index]);
        } else {
            items = this.view.getSecretKeys();
            if (items.length) {
                this.view.selectItem(items[items.length - 1]);
            }
        }
    },

    _secretKeyDeleted: function(key) {
        var items = this.view.getSecretKeys();
        var index = $.inArray(key, items);
        if (index != -1) {
            items.splice(index, 1);
            this.view.deleteItem(key);
        }
        if (items.length) {
            index = Math.max(0, Math.min(index, items.length - 1));
            this.view.selectItem(items[index]);
        } else {
            items = this.view.getPublicKeys();
            if (items.length) {
                this.view.selectItem(items[items.length - 1]);
            }
        }
    },

    _initValidators: function () {
        var v = new jscape.Validator();
        v.nonEmptyRule("pgp_nonempty_name", window.R.string.KEY_MANAGER_PGP_KEYS_ERROR_BAD_NAME);
        v.rule("pgp_unique_key", $.proxy(this._keyAliasNotExists, this), window.R.string.KEY_MANAGER_PGP_KEYS_ERROR_ALIAS_EXISTS);
    },

    _keyAliasNotExists: function(_, params) {
        var realName = $(params[0]).val();
        var email = $(params[1]).val();
        var alias = email.trim().length != 0 ? "{0} <{1}>".supplant({0: realName, 1: email}) : realName;
        var aliases = $.map([].concat(this.view.getPublicKeys(), this.view.getSecretKeys()), function(key) { return key.name; });
        return $.inArray(alias, aliases) == -1;
    }
});

/** @class */
jscape.keystore.PgpKeysView = Class.extend(
    /** @lends {jscape.keystore.PgpKeysView.prototype} */
{
    selection: null,

    /** @constructor */
    initialize: function() {
        var initializing = true;

        this.fieldset = $("fieldset[name=pgpkeysfields]");

        this.data = $("table[role=grid][aria-label=pgpkeys]", this.fieldset);
        this.data.datagrid({
            fit: true,
            fitColumns: true,
            remoteSort: false,
            singleSelect: true,
            idField: "name",
            sortName: "name",
            columns: [[
                    {field: "name", title: $("thead th:nth-child(1)", this.data).text(), sortable: true, width: 120},
                    {field: "keyAlgorithm", title: $("thead th:nth-child(2)", this.data).text(), sortable: true, width: 120},
                    {field: "encryptionAllowed", title: $("thead th:nth-child(3)", this.data).text(), sortable: true, width: 60, formatter: $.proxy(this._formatBoolean, this), styler: $.proxy(this._styleBoolean, this)},
                    {field: "decryptionAllowed", title: $("thead th:nth-child(4)", this.data).text(), sortable: true, width: 60, formatter: $.proxy(this._formatBoolean, this), styler: $.proxy(this._styleBoolean, this)},
                    {field: "signingAllowed", title: $("thead th:nth-child(5)", this.data).text(), sortable: true, width: 60, formatter: $.proxy(this._formatBoolean, this), styler: $.proxy(this._styleBoolean, this)},
                    {field: "signatureVerificationAllowed", title: $("thead th:nth-child(6)", this.data).text(), sortable: true, width: 60, formatter: $.proxy(this._formatBoolean, this), styler: $.proxy(this._styleBoolean, this)},
                    {field: "fingerprint", title: $("thead th:nth-child(7)", this.data).text(), sortable: false, width: 200}
            ]],
            onSelect: $.proxy(this._adjustRowSelection, this),
            onUnselect: $.proxy(this._adjustRowSelection, this),
            onDblClickRow: $.proxy(this._onClick$SignaturesButton, this),
            loader: $.proxy(this._onLoad$Data, this),
            onBeforeLoad: function() { return !initializing; },
            onLoadSuccess: $.proxy(this._adjustRowSelection, this)
        });

        this.importButton = $("a[role=button][name=import]", this.fieldset).linkbutton({onClick: $.proxy(this._onClick$ImportButton, this)});
        this.exportButton = $("a[role=button][name=export]", this.fieldset).linkbutton({disabled: true, onClick: $.proxy(this._onClick$ExportButton, this)});
        this.exportMenu = $("div[role=menu][aria-label=export-menu]", this.fieldset).menu({onClick: $.proxy(this._onClick$ExportMenu, this)});
        this.signButton = $("a[role=button][name=sign]", this.fieldset).linkbutton({disabled: true, onClick: $.proxy(this._onClick$SignButton, this)});
        this.signaturesButton = $("a[role=button][name=signatures]", this.fieldset).linkbutton({disabled: true, onClick: $.proxy(this._onClick$SignaturesButton, this)});
        this.generateButton = $("a[role=button][name=generate]", this.fieldset).linkbutton({onClick: $.proxy(this._onClick$GenerateButton, this)});
        this.deleteButton = $("a[role=button][name=delete]", this.fieldset).linkbutton({disabled: true, onClick: $.proxy(this._onClick$DeleteButton, this)});

        initializing = false;
    },

    show: function(listener) {
        this.listener = listener || jscape.keystore.PgpKeysView.LISTENER;
        this._adjustRowSelection();
    },

    refresh: function() {
        this.data.datagrid("reload");
    },

    addItem: function(value) {
        this.data.datagrid("appendRow", value);
    },

    deleteItem: function (value) {
        var index = this.data.datagrid("getRowIndex", value);
        if (index >= 0) {
            this.data.datagrid("deleteRow", index);
        }
        this._adjustRowSelection();
    },

    selectItem: function(value) {
        this.data.datagrid("selectRecord", value);
    },

    getSecretKeys: function() {
        return this.data.datagrid("getData").secretKeys;
    },

    getPublicKeys: function() {
        return this.data.datagrid("getData").publicKeys;
    },

    _formatBoolean: function (value) {
        return !!value ? "yes" : "no"
    },

    _styleBoolean: function(value) {
        return !!value ? "color:green" : "color:red";
    },

    _onLoad$Data: function (_, success, error) {
        this.data.datagrid("unselectAll");
        this.listener.onRefresh()
                .then(function (publicKeys, secretKeys) {
                          var rows = [].concat(publicKeys, secretKeys);
                          var data = {total: rows, rows: rows, publicKeys: publicKeys, secretKeys: secretKeys};
                          return $.Deferred().resolve(data).promise();
                      })
                .done(success)
                .fail(error);
    },

    _onClick$GenerateButton: function() {
        this.listener.onGenerate();
    },

    _onClick$ImportButton: function() {
        this.listener.onImport();
    },

    _onClick$ExportButton: function() {
        var value = this.selection;
        if (this._isPublicKey(value)) {
            this.listener.onExportPublicKey(value);
        } else if (this._isSecretKey(value)) {
            this.exportMenu.menu("show", {alignTo: this.exportButton});
            this.exportButton.blur();
        }
    },

    _onClick$ExportMenu: function(item) {
        var type = $(item.target).attr("aria-label");
        if ("secret" == type) {
            this.listener.onExportSecretKey(this.selection);
        } else if ("pubkey" == type) {
            this.listener.onExportSecretPublicKey(this.selection);
        }
    },

    _onClick$SignButton: function() {
        this.listener.onSign(this.selection);
    },

    _onClick$DeleteButton: function() {
        var value = this.selection;
        if (this._isPublicKey(value)) {
            this.listener.onDeletePublicKey(value);
        } else if (this._isSecretKey(value)) {
            this.listener.onDeleteSecretKey(value);
        }
    },

    _onClick$SignaturesButton: function() {
        var value = this.selection;
        if (this._isPublicKey(value)) {
            this.listener.onViewPublicKeySignatures(value);
        } else if (this._isSecretKey(value)) {
            this.listener.onViewSecretKeySignatures(value);
        }
    },

    _adjustRowSelection: function() {
        this.selection = this.data.datagrid("getSelected");
        this._adjustButtonsState();
    },

    _adjustButtonsState: function() {
        var signingAllowed = this._isPublicKey(this.selection) && this.getSecretKeys().length != 0;
        this.exportButton.linkbutton(this.selection != null ? "enable" : "disable");
        this.deleteButton.linkbutton(this.selection != null ? "enable" : "disable");
        this.signButton.linkbutton(signingAllowed ? "enable" : "disable");
        this.signaturesButton.linkbutton(this.selection != null ? "enable" : "disable");
    },

    _isSecretKey: function(value) {
        return $.inArray(value, this.getSecretKeys()) != -1;
    },

    _isPublicKey: function(value) {
        return $.inArray(value, this.getPublicKeys()) != -1;
    }
});
jscape.keystore.PgpKeysView.LISTENER = {
    onRefresh: $.noop,
    onImport: $.noop,
    onGenerate: $.noop,
    onExportPublicKey: $.noop,
    onExportSecretKey: $.noop,
    onExportSecretPublicKey: $.noop,
    onDeletePublicKey: $.noop,
    onDeleteSecretKey: $.noop,
    onSign: $.noop,
    onViewPublicKeySignatures: $.noop,
    onViewSecretKeySignatures: $.noop
};

/** @class */
jscape.keystore.ImportPgpKeyController = Class.extend(
    /** @lends {jscape.keystore.ImportPgpKeyController.prototype} */
{
    deferred: null,

    start: function(managementService) {
        this.managementService = managementService;
        this.deferred = $.Deferred();
        this._setupDialog().show(this);
        return this.deferred.promise();
    },

    onSubmit: function(dialog) {
        if (dialog.isSecretKey()) {
            this.managementService.importPgpSecretKey(dialog.getFileSource(), dialog.getFilePassword())
                    .done($.proxy(function(key) { this.deferred.resolve(key, true); this.deferred = null; }, this))
                    .always(function() { dialog.destroy(); });
        } else if (dialog.isPublicKey()) {
            this.managementService.importPgpPublicKey(dialog.getFileSource(), dialog.getFilePassword())
                    .done($.proxy(function(key) { this.deferred.resolve(key, false); this.deferred = null; }, this))
                    .always(function() { dialog.destroy(); });
        } else {
            dialog.destroy();
            this.deferred.reject();
            this.deferred = null;
        }
    },

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

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

/** @class */
jscape.keystore.ImportPgpKeyDialog = Class.extend(
    /** @lends {jscape.keystore.ImportPgpKeyDialog.prototype} */
{
    /** @constructor */
    initialize: function() {
        this.dialog = $("#d-pgpkey-import").clone().attr("id", "");
        this.dialog.dialog({
            width: 530, 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=importpgpkeyfields]", this.dialog);

        this.type = $("input[name=type]:radio", this.fieldset).change($.proxy(this._adjustFieldState, this));
        this.file = $("input[name=filesource]", this.fieldset).filebox({required: true, multiple:false, missingMessage: window.R.string.KEY_MANAGER_PGP_KEYS_ERROR_BAD_FILE, width: "60%"});
        this.password = $("input[name=filepwd]", this.fieldset).passwordbox({required: true, lastDelay: 0, missingMessage: window.R.string.KEY_MANAGER_PGP_KEYS_ERROR_BAD_FILE_PASSWORD, width: "60%"});
    },

    isPublicKey: function() {
        return this.type.filter("[value=public]").is(":checked");
    },

    isSecretKey: function() {
        return this.type.filter("[value=secret]").is(":checked");
    },

    getFileSource: function() {
        return $("#" + this.file.filebox("options").fileboxId)[0].files[0];
    },

    getFilePassword: function() {
        return this.password.passwordbox("getValue");
    },

    show: function (listener) {
        this.listener = listener;
        this.dialog.dialog("center").dialog("open");
        this.fieldset.form("validate");
        this.type.filter(":first").prop("checked", true).change().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;
    },

    _adjustFieldState: function() {
        var enabled = this.isSecretKey();
        this.password.passwordbox(enabled ? "enable" : "disable")
                .passwordbox(enabled ? "enableValidation" : "disableValidation")
                .passwordbox("validate");
    }
});

/** @class */
jscape.keystore.ExportPgpPublicKeyController = Class.extend(
    /** @lends {jscape.keystore.ExportPgpPublicKeyController.prototype} */
{
    deferred: null,

    start: function(managementService, keyAlias, isSecretKey) {
        this.managementService = managementService;
        this.keyAlias = keyAlias;
        this.secretKey = !!isSecretKey;
        this.deferred = $.Deferred();
        this._setupDialog().show(this);
        return this.deferred.promise();
    },

    onSubmit: function(dialog) {
        var filename = dialog.getFilename();
        dialog.destroy();

        (this.secretKey
            ? this.managementService.exportPgpSecretPublicKey(this.keyAlias, filename)
            : this.managementService.exportPgpPublicKey(this.keyAlias, filename))
                .done($.proxy(function() { this.deferred.resolve(); this.deferred = null; }, this))
                .fail($.proxy(function() { this.deferred.reject(); this.deferred = null; }, this));
    },

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

    _setupDialog: function() {
        var dialog = new jscape.keystore.ExportPgpPublicKeyDialog();
        dialog.setFilename("pgp-key.pub");
        return dialog;

    }
});

/** @class */
jscape.keystore.ExportPgpPublicKeyDialog = Class.extend(
    /** @lends {jscape.keystore.ExportPgpPublicKeyDialog.prototype} */
{
    /** @constructor */
    initialize: function() {
        this.dialog = $("#d-pgpkey-exportpublic").clone().attr("id", "");
        this.dialog.dialog({
            width: 530, 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=exportpgpkeyfields]", this.dialog);

        this.filename = $("input[name=filename]", this.fieldset).textbox({required: true, missingMessage: window.R.string.KEY_MANAGER_EXPORT_KEYS_ERROR_BAD_FILENAME, width: "60%"});
    },

    getFilename: function() {
        return this.filename.textbox("getValue");
    },

    setFilename: function(value) {
        this.filename.textbox("setValue", value);
    },

    show: function (listener) {
        this.listener = listener;
        this.dialog.dialog("center").dialog("open");
        this.fieldset.form("validate");
        this.fieldset.find("input:first").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.keystore.ExportPgpSecretKeyController = Class.extend(
    /** @lends {jscape.keystore.ExportPgpSecretKeyController.prototype} */
{
    deferred: null,

    start: function(managementService, keyAlias) {
        this.managementService = managementService;
        this.keyAlias = keyAlias;
        this.deferred = $.Deferred();
        this._setupDialog().show(this);
        return this.deferred.promise();
    },

    onSubmit: function(dialog) {
        var filename = dialog.getFilename();
        var password = dialog.getPassword();
        dialog.destroy();

        this.managementService.exportPgpSecretKey(this.keyAlias, filename, password)
                .done($.proxy(function() { this.deferred.resolve(); this.deferred = null; }, this))
                .fail($.proxy(function() { this.deferred.reject(); this.deferred = null; }, this));
    },

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

    _setupDialog: function() {
        var dialog = new jscape.keystore.ExportPgpSecretKeyDialog();
        dialog.setFilename("pgp-key.sec");
        return dialog;
    }
});

/** @class */
jscape.keystore.ExportPgpSecretKeyDialog = Class.extend(
    /** @lends {jscape.keystore.ExportPgpSecretKeyDialog.prototype} */
{
    /** @constructor */
    initialize: function() {
        this.dialog = $("#d-pgpkey-exportsecret").clone().attr("id", "");
        this.dialog.dialog({
            width: 530, 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=exportpgpkeyfields]", this.dialog);

        this.filename = $("input[name=filename]", this.fieldset).textbox({required: true, missingMessage: window.R.string.KEY_MANAGER_EXPORT_KEYS_ERROR_BAD_FILENAME, width: "60%"});
        this.password = $("input[name=filepwd]", this.fieldset).passwordbox({required: true, lastDelay: 0, missingMessage: window.R.string.KEY_MANAGER_EXPORT_KEYS_ERROR_BAD_FILE_PASSWORD, width: "50%"});
    },

    getFilename: function() {
        return this.filename.textbox("getValue");
    },

    setFilename: function(value) {
        this.filename.textbox("setValue", value);
    },

    getPassword: function() {
        return this.password.passwordbox("getValue");
    },

    show: function (listener) {
        this.listener = listener;
        this.dialog.dialog("center").dialog("open");
        this.fieldset.form("validate");
        this.filename.textbox("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.keystore.GeneratePgpKeyController = Class.extend(
    /** @lends {jscape.keystore.GeneratePgpKeyController.prototype} */
{
    deferred: null,

    start: function(managementService) {
        this.managementService = managementService;
        this.deferred = $.Deferred();
        this._setupDialog().show(this);
        return this.deferred.promise();
    },

    onSubmit: function(dialog) {
        var realName = dialog.getRealName();
        var email = dialog.getEmail();
        var keyType = dialog.getAlgorithm();
        var keyLength = dialog.getKeyLength();
        dialog.destroy();

        this.managementService.generatePgpKey(realName, email, keyType, keyLength)
                .done($.proxy(function(key) { this.deferred.resolve(key); this.deferred = null; }, this))
                .fail($.proxy(function() { this.deferred.reject(); this.deferred = null; }, this));
    },

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

    _setupDialog: function() {
        var dialog = new jscape.keystore.GeneratePgpKeyDialog();
        dialog.setAlgorithms(jscape.KeyPairAlgorithm.values());
        return dialog;
    }
});

/** @class */
jscape.keystore.GeneratePgpKeyDialog = Class.extend(
    /** @lends {jscape.keystore.GeneratePgpKeyDialog.prototype} */
{
    /** @constructor */
    initialize: function() {
        this.dialog = $("#d-pgpkey-generate").clone().attr("id", "");
        this.dialog.dialog({
            width: 530, 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=generatepgpkeyfields]", this.dialog);

        this.realName = $("input[name=realname]", this.fieldset).textbox({required: true, validType: ["pgp_nonempty_name"], missingMessage: window.R.string.KEY_MANAGER_PGP_KEYS_ERROR_BAD_NAME, width: "60%"});
        this.email = $("input[name=email]", this.fieldset).textbox({validType: ["email"], width: "60%"});
        var uniquePgpKeyRule = "pgp_unique_key['#{0}','#{1}']".supplant({
            0: this.realName.textbox("textbox").attr("id"),
            1: this.email.textbox("textbox").attr("id")
        });
        this.realName.textbox("textbox").validatebox("options").validType.push(uniquePgpKeyRule );
        this.email.textbox("textbox").validatebox("options").validType.push(uniquePgpKeyRule);
        this.realName.textbox("textbox").on("input", $.proxy(function() { this.email.textbox("validate");}, this));
        this.email.textbox("textbox").on("input", $.proxy(function() { this.realName.textbox("validate");}, this));

        this.keyAlgorithm = $("select[name=keytype]", this.fieldset).combobox({required: true, editable: false, width: 80, panelHeight: "auto", onChange: $.proxy(this._adjustKeyLength, this)});
        this.keyLength = $("select[name=keylength]", this.fieldset).combobox({required: true, editable: false, width: 80, panelHeight: "auto"});
    },

    getRealName: function() {
        return this.realName.textbox("getValue");
    },

    getEmail: function() {
        return this.email.textbox("getValue");
    },

    getAlgorithm: function() {
        return this.keyAlgorithm.combobox("getValue");
    },

    setAlgorithms: function(values) {
        var data = $.map(values || [], function(item) { return {text: item.name, value: item.name}; });
        this.keyAlgorithm.combobox("loadData", data);
    },

    getKeyLength: function() {
        return this.keyLength.combobox("getValue");
    },

    setKeyLengths: function(values) {
        this.keyLength.combobox("loadData", values);
    },

    show: function (listener) {
        this.listener = listener;
        this.dialog.dialog("center").dialog("open");
        this.fieldset.form("validate");
        this.realName.textbox("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;
    },

    _adjustKeyLength: function() {
        this.setKeyLengths(jscape.KeyPairAlgorithm.lengthsOf(this.getAlgorithm()));
    }
});

/** @class */
jscape.keystore.ViewPgpKeySignaturesController = Class.extend(
    /** @lends {jscape.keystore.ViewPgpKeySignaturesController.prototype} */
{
    deferred: null,

    start: function(managementService, keyAlias, isSecretKey) {
        this.managementService = managementService;
        this.deferred = $.Deferred();
        (!!isSecretKey
                ? this.managementService.getPgpSecretKeySignatures(keyAlias)
                : this.managementService.getPgpPublicKeySignatures(keyAlias))
                .done($.proxy(function (signatures) { this._setupDialog(signatures).show(this); }, this))
                .fail($.proxy(function () { this.deferred.reject(); this.deferred = null; }, this));
        return this.deferred.promise();
    },

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

    _setupDialog: function(signatures) {
        var dialog = new jscape.keystore.PgpKeySignaturesDialog();
        dialog.setSignatures(signatures);
        return dialog;
    }
});

/** @class */
jscape.keystore.PgpKeySignaturesDialog = Class.extend(
    /** @lends {jscape.keystore.PgpKeySignaturesDialog.prototype} */
{
    /** @constructor */
    initialize: function() {
        this.dateFormat = new jscape.DateFormat("{MM}-{dd}-{yy}");

        this.dialog = $("#d-pgpkey-signatures").clone().attr("id", "");
        this.dialog.dialog({
            width: 530, height: "auto",
            modal: true, resizable: false, closable: true, closed: true,
            buttons: [{text: window.R.string.DIALOG_BUTTON_OK || "Ok", handler: $.proxy(this._onClose, this)}],
            onClose: $.proxy(function () {if (!this.closing) { this._onClose(); }}, this)
        }).show();

        this.fieldset = $("fieldset[name=pgpkeysignaturesfields]", this.dialog);
        this.template = $("fieldset[name=pgpkeysignatureentryfields]", this.dialog);
    },

    setSignatures: function(values) {
        for (var i = 0, size = values.length; i < size; i++) {
            var signature = values[i];
            this.template.find("code[aria-label=number]").text(i + 1);
            this.template.find("code[aria-label=signingkey]").text(signature.signingKey);
            this.template.find("code[aria-label=creationtime]").text(this.dateFormat.format(new Date(signature.creationTime)));
            this.template.find("code[aria-label=type]").text(window.R.string["KEY_MANAGER_PGP_KEYS_SIGNATURE_TYPE_" + signature.type]);
            this.template.find("code[aria-label=version]").text(signature.version);
            if (i < size - 1) {
                this.template = this.template.clone().appendTo(this.fieldset);
            }
        }
    },

    show: function (listener) {
        this.listener = listener;
        this.dialog.dialog("center").dialog("open");
    },

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

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

    _onClose: function () {
        this.hide();
        this.listener.onClose(this);
    },

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

/** @class */
jscape.keystore.SignPgpKeyController = Class.extend(
    /** @lends {jscape.keystore.SignPgpKeyController.prototype} */
{
    deferred: null,

    start: function(managementService, keyAlias, signingKeys) {
        this.managementService = managementService;
        this.keyAlias = keyAlias;
        this.deferred = $.Deferred();
        this._setupDialog(signingKeys).show(this);
        return this.deferred.promise();
    },

    onSubmit: function(dialog) {
        var signingKey = dialog.getSigningKey();
        dialog.destroy();

        this.managementService.signPgpPublicKey(this.keyAlias, signingKey)
                .done($.proxy(function(key) { this.deferred.resolve(key); this.deferred = null; }, this))
                .fail($.proxy(function() { this.deferred.reject(); this.deferred = null; }, this));
    },

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

    _setupDialog: function(signingKeys) {
        var dialog = new jscape.keystore.SignPgpKeyDialog();
        dialog.setSigningKeys(signingKeys);
        return dialog;
    }
});

/** @class */
jscape.keystore.SignPgpKeyDialog = Class.extend(
    /** @lends {jscape.keystore.SignPgpKeyDialog.prototype} */
{
    /** @constructor */
    initialize: function() {
        this.dialog = $("#d-pgpkey-sign").clone().attr("id", "");
        this.dialog.dialog({
            width: 530, 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=signpgpkeyfields]", this.dialog);

        this.signingKey = $("select[name=signingkey]", this.fieldset).combobox({required: true, editable: false, width: 180, panelHeight: "auto"});
    },

    getSigningKey: function () {
        return this.signingKey.combobox("getValue");
    },

    setSigningKeys: function (values) {
        var data = $.map(values || [], function(key) { return {text: key.name, value: key.name}; });
        this.signingKey.combobox("loadData", data);
    },

    show: function (listener) {
        this.listener = listener;
        this.dialog.dialog("center").dialog("open");
        this.fieldset.form("validate");
        this.signingKey.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.keystore.DeletePgpKeyController = Class.extend(
    /** @lends {jscape.keystore.DeletePgpKeyController.prototype} */
{
    deferred: null,

    start: function(managementService, keyAlias, isSecretKey) {
        this.managementService = managementService;
        this.alias = keyAlias;
        this.secretKey = !!isSecretKey;
        this.deferred = $.Deferred();

        this._confirmDelete()
                .done($.proxy(this._deleteConfirmed, this))
                .fail($.proxy(function() { this.deferred.reject(); this.deferred = null; }, this));

        return this.deferred.promise();
    },

    _confirmDelete: function() {
        return jscape.ConfirmDialog.confirm(window.R.string.KEY_MANAGER_PGP_KEYS_CONFIRM_DELETE_TITLE,
                                            window.R.string.KEY_MANAGER_PGP_KEYS_CONFIRM_DELETE_MESSAGE.supplant({0: this.alias}));
    },

    _deleteConfirmed: function() {
        var promise = this.secretKey
                      ? this.managementService.deletePgpSecretKey(this.alias)
                      : this.managementService.deletePgpPublicKey(this.alias);
        $.when.call(jQuery, promise)
                .done($.proxy(function() { this.deferred.resolve(); this.deferred = null; }, this))
                .fail($.proxy(function() { this.deferred.reject(); this.deferred = null; }, this));
    }
});