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

/** @class */
jscape.GenerateClientKeyData = Class.extend(
    /** @lends {jscape.GenerateClientKeyData.prototype} */
{
    /** @constructor */
    initialize: function (type, length, periodMillis, commonName, organizationUnit, organization, locality, state, country, privateKeyFilename, privateKeyFormat, privateKeyPassword) {
        this.keyType = type || "RSA";
        this.keyLength = parseInt(length) || 1024;
        this.periodMillis = parseInt(periodMillis) || 365 * 24 * 60 * 60 * 1000;
        this.commonName = commonName || "";
        this.organizationUnit = organizationUnit || "";
        this.organization = organization || "";
        this.locality = locality || "";
        this.state = state || "";
        this.country = country || "";
        this.privateKeyFilename = privateKeyFilename;
        this.privateKeyFormat = privateKeyFormat;
        this.privateKeyPassword = privateKeyPassword;
    }
});

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

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

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

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

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

        this.importController = new jscape.keystore.ImportClientKeyController();
        this.importTslController = new jscape.keystore.ImportClientKeyTslController();
        this.generateController = new jscape.keystore.GenerateClientKeyController();
        this.exportPublicKeyController = new jscape.keystore.ExportClientPublicKeyController();
        this.exportCertificateController = new jscape.keystore.ExportClientCertificateController();
        this.crlController = new jscape.keystore.VerifyClientCertificateRevocationСontroller();
        this.viewController = new jscape.keystore.ViewClientKeystoreController();
        this.revokeController = new jscape.keystore.RevokeClientCertificateController();
        this.deleteController = new jscape.keystore.DeleteClientKeyController();

        this._initValidators();
    },

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

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

    onRefresh: function() {
        return $.when(this.managementService.getClientPublicKeys(), this.managementService.getClientCertificates());
    },

    onImportFile: function () {
        this.importController.start(this.managementService)
                .done($.proxy(function (entry) {
                          if (entry instanceof jscape.CertificateSummary) {
                              this._certificateImported(entry);
                          } else if (entry instanceof jscape.PublicKeySummary) {
                              this._publicKeyImported(entry);
                          }
                      }, this));
    },

    onImportTsl: function() {
        this.importTslController.start(this.managementService)
                .done($.proxy(function() { this.view.refresh(); }, this));
    },

    onExportCertificate: function(certificate) {
        this.exportCertificateController.start(this.managementService, certificate.name);
    },

    onExportCertificatePublicKey: function(certificate) {
        this.exportPublicKeyController.start(this.managementService, certificate.name, true);
    },

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

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

    onViewKey: function (key) {
        return this.viewController.start(this.managementService, key.name, function (service, alias) {
            return service.getClientKeyView(alias);
        });
    },

    onViewCertificate: function (certificate) {
        return this.viewController.start(this.managementService, certificate.name, function (service, alias) {
            return service.getClientCertificateView(alias);
        });
    },

    onVerifyRevocation: function() {
        this.crlController.start(this.managementService)
                .done($.proxy(function() { this.view.refresh(); }, this));
    },

    onRevokeCertificate: function(certificate) {
        this.revokeController.start(this.managementService, certificate)
                .done($.proxy(this._certificateRevoked, this, certificate));
    },

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

    onDeleteCertificate: function(certificate) {
        this.deleteController.start(this.managementService, certificate.name, true)
                .done($.proxy(this._certificateDeleted, this, certificate));
    },

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

    _certificateImported: function (certificate) {
        this.view.getCertificates().push(certificate);
        this.view.addItem(certificate);
        this.view.selectItem(certificate);
    },

    _keyGenerated: function(certificate) {
        this.view.getCertificates().push(certificate);
        this.view.addItem(certificate);
        this.view.selectItem(certificate);
    },

    _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.getCertificates();
                if (items.length) {
                    this.view.selectItem(items[items.length - 1]);
                }
            }
        }
    },

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

    _certificateDeleted: function(certificate) {
        var items = this.view.getCertificates();
        var index = $.inArray(certificate, items);
        if (index != -1) {
            items.splice(index, 1);
            this.view.deleteItem(certificate);

            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("clientkey_nonempty", window.R.string.KEY_MANAGER_CLIENT_KEYS_ERROR_BAD_ALIAS);
        v.rule("clientkey_notexists", $.proxy(this._keyAliasNotExists, this), window.R.string.KEY_MANAGER_CLIENT_KEYS_ERROR_ALIAS_EXISTS);
    },

    _keyAliasNotExists: function(value, excludes) {
        var keys = [].concat(this.view.getPublicKeys(), this.view.getCertificates());
        var aliases = $.map(keys, function(entry) { return excludes && excludes.inArray(entry.name) != -1 ? null : entry.name; });
        return $.inArray(value, aliases) == -1;
    }
});

/** @class */
jscape.keystore.ClientKeysView = Class.extend(
    /** @lends {jscape.keystore.ClientKeysView.prototype} */
{
    DATE_FORMAT: new jscape.DateFormat("{MM}-{dd}-{yy}"),

    selection: null,

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

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

        this.data = $("table[role=grid][aria-label=clientkeys]", 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: 70},
                    {field: "keySize", title: $("thead th:nth-child(3)", this.data).text(), sortable: true, width: 70},
                    {field: "certificateSerialNumber", title: $("thead th:nth-child(4)", this.data).text(), sortable: true, width: 80},
                    {field: "certificateIssuer", title: $("thead th:nth-child(5)", this.data).text(), sortable: false, width: 140},
                    {field: "certificateSubject", title: $("thead th:nth-child(6)", this.data).text(), sortable: false, width: 140},
                    {field: "certificateBeginDate", title: $("thead th:nth-child(7)", this.data).text(), sortable: true, width: 80, formatter: $.proxy(this._formatDate, this), styler: $.proxy(this._styleDate, this, true)},
                    {field: "certificateEndDate", title: $("thead th:nth-child(8)", this.data).text(), sortable: true, width: 80, formatter: $.proxy(this._formatDate, this), styler: $.proxy(this._styleDate, this, false)},
                    {field: "revoked", title: $("thead th:nth-child(9)", this.data).text(), sortable: true, width: 60, align: "center", formatter: function(value) { return "<input type=\"checkbox\" onclick=\"return false\"" + (value ? " checked=\"checked\" />" : "/>"); }}
            ]],
            onSelect: $.proxy(this._adjustButtonsState, this),
            onUnselect: $.proxy(this._adjustButtonsState, this),
            onDblClickRow: $.proxy(this._onClick$ViewButton, this),
            loader: $.proxy(this._onLoad$Data, this),
            onBeforeLoad: function() { return !initializing; },
            onLoadSuccess: $.proxy(this._adjustButtonsState, this)
        });

        this.importButton = $("a[role=button][name=import]", this.fieldset).linkbutton({onClick: $.proxy(this._onClick$ImportButton, this)});
        this.importMenu = $("div[role=menu][aria-label=import-menu]", this.fieldset).menu({onClick: $.proxy(this._onClick$ImportMenu, 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.generateButton = $("a[role=button][name=generate]", this.fieldset).linkbutton({onClick: $.proxy(this._onClick$GenerateButton, this)});
        this.crlButton = $("a[role=button][name=verifycrl]", this.fieldset).linkbutton({onClick: $.proxy(this._onClick$CrlButton, this)});
        this.viewButton = $("a[role=button][name=view]", this.fieldset).linkbutton({disabled: true, onClick: $.proxy(this._onClick$ViewButton, this)});
        this.revokeButton = $("a[role=button][name=revoke]", this.fieldset).linkbutton({disabled: true, onClick: $.proxy(this._onClick$RevokeButton, 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.ClientKeysView.LISTENER;
        this._adjustButtonsState();
    },

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

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

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

    getCertificates: function() {
        return this.data.datagrid("getData").certificates;
    },

    _formatDate: function(value) {
        return $.isNumeric(value) ? this.DATE_FORMAT.format(new Date(value)) : null;
    },

    _styleDate: function(before, value) {
        if ($.isNumeric(value)) {
            var now = new Date().getTime();
            return before && value > now || !before && value < now ? "color:red" : "color:green";
        }
        return null;
    },

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

    _onClick$ImportButton: function() {
        this.importMenu.menu("show", {alignTo: this.importButton});
        this.importButton.blur();
    },

    _onClick$ImportMenu: function(item) {
        var type = $(item.target).attr("aria-label");
        if ("menu-file" == type) {
            this.listener.onImportFile();
        } else if ("menu-tsl" == type) {
            this.listener.onImportTsl();
        }
    },

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

    _onClick$CrlButton: function() {
        this.listener.onVerifyRevocation();
    },

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

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

    _onClick$ViewButton: function() {
        if (this._isPublicKey(this.selection)) {
            this.listener.onViewKey(this.selection);
        } else if (this._isCertificate(this.selection)) {
            this.listener.onViewCertificate(this.selection);
        }
    },

    _onClick$RevokeButton: function() {
        this.listener.onRevokeCertificate(this.selection);
    },

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

    _adjustButtonsState: function() {
        this.selection = this.data.datagrid("getSelected");
        var enabled = this.selection != null;
        this.exportButton.linkbutton(enabled ? "enable" : "disable");
        this.deleteButton.linkbutton(enabled ? "enable" : "disable");
        this.viewButton.linkbutton(enabled ? "enable" : "disable");
        this.revokeButton.linkbutton(enabled && this._isCertificate(this.selection) ? "enable" : "disable");
    },

    _isPublicKey: function(key) {
        return $.inArray(key, this.getPublicKeys()) != -1;
    },

    _isCertificate: function(key) {
        return $.inArray(key, this.getCertificates()) != -1;
    }
});
jscape.keystore.ClientKeysView.LISTENER = {
    onRefresh: $.noop,
    onImportFile: $.noop,
    onImportTsl: $.noop,
    onExportCertificate: $.noop,
    onExportCertificatePublicKey: $.noop,
    onExportPublicKey: $.noop,
    onGenerate: $.noop,
    onViewKey: $.noop,
    onViewCertificate: $.noop,
    onVerifyRevocation: $.noop,
    onRevokeCertificate: $.noop,
    onDeletePublicKey: $.noop,
    onDeleteCertificate: $.noop
};

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

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

    onSubmit: function (dialog) {
        this.managementService.importClientKey(dialog.getAlias(), dialog.getFile())
                .done($.proxy(function (key) { this.deferred.resolve(key); }, 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.keystore.ImportClientKeyDialog();
    }
});

/** @class */
jscape.keystore.ImportClientKeyDialog = jscape.keystore.ImportPublicKeyDialog.extend(
    /** @lends {jscape.keystore.ImportClientKeyDialog.prototype} */
{
    /** @constructor */
    initialize: function() {
        this._super();

        this.alias.textbox({required: true, validType: ["clientkey_nonempty", "clientkey_notexists"], missingMessage: window.R.string.KEY_MANAGER_CLIENT_KEYS_ERROR_BAD_ALIAS, width: "60%"});

        this.file = $("input[name=file]", this.fieldset).filebox({required: true, multiple:false, missingMessage: window.R.string.KEY_MANAGER_CLIENT_KEYS_ERROR_BAD_FILE, width: "60%"});
    },

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

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

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

    onSubmit: function (dialog) {
        this.managementService.importClientKeyTsl(dialog.getUrl())
                .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.keystore.ImportPublicKeyTslDialog();
    }
});

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

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

    onSubmit: function(dialog) {
        var alias = dialog.getAlias();
        var data = this._createData(dialog);
        var deferred = $.Deferred();
        var exportDialog = new jscape.keystore.ExportPrivateKeyDialog();
        exportDialog.setFilename(alias + ".prv");
        exportDialog.show({onSubmit: deferred.resolve, onCancel: deferred.reject});

        deferred.promise()
                .done($.proxy(function() {
                                 var data = this._createData(dialog, exportDialog.getFilename(), exportDialog.getFormat(), exportDialog.getPassword());
                                  dialog.hide();
                                  this.managementService.generateClientKey(alias, data)
                                          .then($.proxy(function() { return this.managementService.getClientCertificate(alias); }, this))
                                          .done($.proxy(function(certificate) { dialog.destroy(); this.deferred.resolve(certificate); this.deferred = null; }, this))
                                          .fail($.proxy(this.onCancel, this, dialog))
                              }, this))
                .fail($.proxy(this.onCancel, this, dialog))
                .always(function() { exportDialog.destroy(); });
    },

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

    _createData: function(dialog, keyFilename, keyFormat, keyPassword) {
        return new jscape.GenerateClientKeyData(dialog.getAlgorithm(),
                                                dialog.getKeyLength(),
                                                dialog.getPeriod(),
                                                dialog.getCommonName(),
                                                dialog.getOrganizationUnit(),
                                                dialog.getOrganization(),
                                                dialog.getLocality(),
                                                dialog.getState(),
                                                dialog.getCountry(),
                                                keyFilename,
                                                keyFormat,
                                                keyPassword);
    },

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

/** @class */
jscape.keystore.GenerateClientKeyDialog = Class.extend(
    /** @lends {jscape.keystore.GenerateClientKeyDialog.prototype} */
{
    PERIOD_FACTOR: 24 * 60 * 60 * 1000,

    /** @constructor */
    initialize: function() {
        this.dialog = $("#d-clientkey-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=generateclientkeyfields]", this.dialog);

        this.alias = $("input[name=alias]", this.fieldset).textbox({required: true, validType: ["clientkey_nonempty", "clientkey_notexists"], missingMessage: window.R.string.KEY_MANAGER_CLIENT_KEYS_ERROR_BAD_ALIAS, width: "60%"});
        this.keyAlgorithm = $("select[name=keyalgorithm]", this.fieldset).combobox({editable: false, width: 80, panelHeight: "auto", onChange: $.proxy(this._adjustKeyLength, this)});
        this.keyLength = $("select[name=keylength]", this.fieldset).combobox({editable: false, width: 80, panelHeight: "auto"});
        this.period = $("input[name=period]", this.fieldset).numberspinner({min: 1, max: 10000, value: 365, increment: 10, width: 80});
        this.commonName = $("input[name=cn]", this.fieldset).textbox({required: true, validType: "non_empty", missingMessage: window.R.string.KEY_MANAGER_ERROR_BAD_COMMON_NAME, invalidMessage: window.R.string.KEY_MANAGER_ERROR_BAD_COMMON_NAME, width: "50%"});
        this.organizationUnit = $("input[name=ou]", this.fieldset).textbox({width: "50%"});
        this.organization = $("input[name=o]", this.fieldset).textbox({width: "50%"});
        this.locality = $("input[name=l]", this.fieldset).textbox({width: "50%"});
        this.state = $("input[name=s]", this.fieldset).textbox({width: "50%"});
        this.country = $("input[name=c]", this.fieldset).textbox({width: "50%"});
    },

    getAlias: function() {
        return this.alias.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);
    },

    getPeriod: function() {
        return parseInt(this.period.numberspinner("getValue")) * this.PERIOD_FACTOR;
    },

    getCommonName: function() {
        return this.commonName.textbox("getValue");
    },

    getOrganizationUnit: function() {
        return this.organizationUnit.textbox("getValue");
    },

    getOrganization: function() {
        return this.organization.textbox("getValue");
    },

    getLocality: function() {
        return this.locality.textbox("getValue");
    },

    getState: function() {
        return this.state.textbox("getValue");
    },

    getCountry: function() {
        return this.country.textbox("getValue");
    },

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

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

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

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

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

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

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

    onSubmit: function(dialog) {
        this.managementService.verifyCertificateRevocation(dialog.getFile(), "client")
                .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.keystore.VerifyClientCertificateRevocationDialog();
    }
});

/** @class */
jscape.keystore.VerifyClientCertificateRevocationDialog = Class.extend(
    /** @lends {jscape.keystore.VerifyClientCertificateRevocationDialog.prototype} */
{
    /** @constructor */
    initialize: function() {
        this.dialog = $("#d-clientkey-verifycrl").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=verifyclientkeycrlfields]", this.dialog);

        this.file = $("input[type=file][name=file]", this.fieldset)
                .validatebox({required: true, validType: "non_empty", missingMessage: window.R.string.KEY_MANAGER_ERROR_BAD_FILE})
                .change(function() { $(this).validatebox("validate"); });
    },

    getFile: function () {
        var files = this.file[0].files;
        return files && files.length ? files[0] : null;
    },

    show: function (listener) {
        this.listener = listener;
        this.dialog.dialog("center").dialog("open");
        this.fieldset.form("validate");
        this.file.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.RevokeClientCertificateController = Class.extend(
    /** @lends {jscape.keystore.RevokeClientCertificateController.prototype} */
{
    start: function(managementService, certificate) {
        if (certificate.revoked) {
            return managementService.setClientCertificateRevocationStatus(certificate.name, false);
        } else {
            return this._confirmRevoke(certificate.name)
                    .done(function () { return managementService.setClientCertificateRevocationStatus(certificate.name, true); })
        }
    },

    _confirmRevoke: function(alias) {
        return jscape.ConfirmDialog.confirm(window.R.string.KEY_MANAGER_REVOKE_CERTIFICATE_TITLE,
                                            window.R.string.KEY_MANAGER_REVOKE_CERTIFICATE_MESSAGE.supplant({0: alias}));
    }
});

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

    start: function(managementService, alias, isCertificate) {
        this.managementService = managementService;
        this.deferred = $.Deferred();

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

        return this.deferred.promise();
    },

    _confirmDelete: function(alias, isCertificate) {
        if (!!isCertificate) {
            return jscape.ConfirmDialog.confirm(window.R.string.KEY_MANAGER_CLIENT_KEYS_DELETE_CERTIFICATE_TITLE,
                                                window.R.string.KEY_MANAGER_CLIENT_KEYS_DELETE_CERTIFICATE_MESSAGE.supplant({0: alias}));
        } else {
            return jscape.ConfirmDialog.confirm(window.R.string.KEY_MANAGER_CLIENT_KEYS_DELETE_PUBLIC_KEY_TITLE,
                                                window.R.string.KEY_MANAGER_CLIENT_KEYS_DELETE_PUBLIC_KEY_MESSAGE.supplant({0: alias}));
        }
    },

    _deleteConfirmed: function (alias, isCertificate) {
        var promise = !!isCertificate
                      ? this.managementService.deleteClientCertificate(alias)
                      : this.managementService.deleteClientPublicKey(alias);
        promise.done($.proxy(function () { this.deferred.resolve(); this.deferred = null; }, this))
                .fail($.proxy(function () { this.deferred.reject(); this.deferred = null; }, this));
    }
});

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

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

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

        var promise = this.isCertificate
                ? this.managementService.exportClientCertificatePublicKey(this.alias, format, filename)
                : this.managementService.exportClientPublicKey(this.alias, format, filename);
        promise.done($.proxy(function () { this.deferred.resolve(); }, this))
                .fail($.proxy(function () { this.deferred.reject(); }, this))
                .always($.proxy(function () { this.deferred = null; }, this));
    },

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

    _setupDialog: function() {
        var dialog = new jscape.keystore.ExportPublicKeyDialog();
        dialog.setFilename(this.alias + ".pub");
        return dialog;
    }
});

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

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

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

        this.managementService.exportClientCertificate(this.alias, format, filename)
                .done($.proxy(function() { this.deferred.resolve(); }, this))
                .fail($.proxy(function() { this.deferred.reject(); }, this))
                .always($.proxy(function() { this.deferred = null; }, this));
    },

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

    _setupDialog: function() {
        var dialog = new jscape.keystore.ExportCertificateDialog();
        dialog.setFilename(this.alias + ".crt");
        return dialog;
    }
});

/** @class */
jscape.keystore.ViewClientKeystoreController = jscape.keystore.ViewKeySummaryController.extend(
    /** @lends {jscape.keystore.ViewClientKeystoreController.prototype} */
{
    /** @override */
    start: function(managementService, alias, loader) {
        this.loader = loader;
        this._super.call(this, managementService, alias);
    },

    /** @override */
    _loadData: function(alias) {
        return $.isFunction(this.loader)
                ? this.loader.call(this, this.managementService, alias)
                : $.Deferred().resolve([]);
    }
});