jscape.domain = jscape.domain || {};

/** @class */
jscape.Contact = Class.extend(
    /** @lends {jscape.Contact.prototype} */
{
    /** @constructor */
    initialize: function(a, b, c, d, e, f) {
        this.id = a || undefined;
        this.name = b || "" ;
        this.email = c || "";
        this.company = d || null;
        this.owner = e || null;
        this.tags = f || [];
    }
});
jscape.Contact.fromJSON = function(data) {
    return data ? new jscape.Contact(data.id, data.name, data.email, data.company, data.owner, data.tags) : null;
};

/** @class */
jscape.domain.ContactsModule = jscape.domain.DomainModule.extend(
    /** @lends {jscape.domain.ContactsModule.prototype} */
{
    onCreate: function () {
        this._super.apply(this, arguments);

        this.controller = new jscape.domain.ContactsController();
        this.controller.onCreate(this.managementService, this.domainName);
    },

    onStart: function () {
        this.controller.onStart();
    },

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

/** @class */
jscape.domain.ContactsController = jscape.ui.DatagridController.extend(
    /** @lends {jscape.domain.ContactsController.prototype} */
{
    view: null,

    /** @constructor */
    initialize: function() {
        this._initValidators();
    },

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

        this.addController = new jscape.domain.AddContactController();
        this.editController = new jscape.domain.EditContactController();
        this.deleteController = new jscape.domain.DeleteContactController();
    },

    onStart: function() {
        if (this.view == null) {
            this.view = new jscape.domain.ContactsView();
        }
        this.view.show(this);
    },

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

    onRefresh: function (params) {
        params = jscape.Page.requestParameters(params);
        return this.managementService.listContacts(this.domainName, params.startIndex, params.count, params.query)
                .then(jscape.Page.datagridFormatter());
    },

    onAddContact: function() {
        this.addController.start(this.managementService, this.domainName)
                .done($.proxy(this._contactAdded, this));
    },

    onEditContact: function(contact) {
        var index = $.inArray(contact, this.view.getContacts());
        if (index != -1) {
            this.editController.start(this.managementService, this.domainName, contact)
                    .done($.proxy(this._contactEdited, this, index));
        }
    },

    onDeleteContact: function(contacts) {
        this.deleteController.start(this.managementService, this.domainName, contacts)
                .done($.proxy(this._contactDeleted, this, contacts));
    },

    _contactAdded: function(contact) {
        this._recordAdded(contact);
    },

    _contactEdited: function(index, contact) {
        this._recordEdited(contact);
    },

    _contactDeleted: function (contacts) {
        this._recordDeleted(contacts);
    },

    _initValidators: function() {
        var v = new jscape.Validator();
        v.nonEmptyRule("contact_name_nonempty", window.R.string.CONTACTS_ERROR_BAD_NAME);
    }
});

/** @class */
jscape.domain.ContactsView = jscape.ui.DatagridView.extend(
    /** @lends {jscape.domain.ContactsView.prototype} */
{
    selection: null,

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

        this.fieldset = $("fieldset[name=contactslistfields]", container);

        this.data = $("table[role=grid][aria-label=contacts]", this.fieldset);
        this.data.datagrid({
            fit: true,
            fitColumns: true,
            ctrlSelect: true,
            pagination: true,
            search: true,
            remoteSearch: true,
            sortName: "name",
            idField: "id",
            columns: [[
                {field: "name", title: $("thead th:nth-child(1)", this.data).text(), width: "20%", sortable: true},
                {field: "company", title: $("thead th:nth-child(2)", this.data).text(), width: "20%", sortable: true, sorter: function(a, b) { return a == b ? 0 : a == null || a < b ? -1 : 1; }},
                {field: "email", title: $("thead th:nth-child(3)", this.data).text(), width: "25%", sortable: false},
                {field: "owner", title: $("thead th:nth-child(4)", this.data).text(), width: "15%", sortable: true, sorter: function(a, b) { return a == b ? 0 : a == null || a < b ? -1 : 1; }},
                {field: "tags", title: $("thead th:nth-child(5)", this.data).text(), width: "20%", formatter: function(value) { return String($.isArray(value) ? value.join(", ") : value).escapeXml(); }}
            ]],
            onSelect: $.proxy(this._adjustRowSelection, this),
            onUnselect: $.proxy(this._adjustRowSelection, this),
            onDblClickRow: $.proxy(this._onClick$EditButton, this),
            loader: $.proxy(this._onLoad$Data, this),
            onBeforeLoad: function() { return !initializing; },
            onLoadSuccess: $.proxy(this._adjustRowSelection, this)
        });

        this.addButton = $("a[role=button][name=add]", this.fieldset).linkbutton({onClick: $.proxy(this._onClick$AddButton, this)});
        this.editButton = $("a[role=button][name=edit]", this.fieldset).linkbutton({disabled: true, onClick: $.proxy(this._onClick$EditButton, 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.domain.ContactsView.LISTENER;
        this.data.datagrid("resize");
        this._adjustRowSelection();
    },

    getContacts: function() {
        return this.getRecords();
    },

    _onLoad$Data: function(params, success, error) {
        this.listener.onRefresh(params).done(success).fail(error);
    },

    _onClick$AddButton: function () {
        this.listener.onAddContact();
    },

    _onClick$EditButton: function () {
        this.listener.onEditContact(this.selection[0]);
    },

    _onClick$DeleteButton: function () {
        this.listener.onDeleteContact(this.selection);
    },

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

    _adjustButtonsState: function() {
        this.editButton.linkbutton(this.selection.length == 1 ? "enable" : "disable");
        this.deleteButton.linkbutton(this.selection.length != 0 ? "enable" : "disable");
    }
});
jscape.domain.ContactsView.LISTENER = {
    onRefresh: $.noop,
    onAddContact: $.noop,
    onEditContact: $.noop,
    onDeleteContact: $.noop
};

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

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

    onSubmit: function(dialog) {
        var contact = this._createContact(dialog);
        dialog.destroy();

        this.managementService.addContact(this.domainName, contact)
                .done($.proxy(function(contact) { this.deferred.resolve(contact); 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;
    },

    _createContact: function(dialog) {
        return new jscape.Contact(null,
                                  dialog.getName(),
                                  dialog.getEmail(),
                                  dialog.getCompany(),
                                  dialog.getOwner(),
                                  dialog.getTags());
    },

    _setupDialog: function () {
        var dialog = new jscape.domain.AddContactDialog();
        this.managementService.getAccountNames(this.domainName).done(function (owners) { dialog.setOwners(owners.sort()); });
        this.managementService.getDomainTags(this.domainName).done(function (tags) { dialog.setAvailableTags(tags); });
        return dialog;
    }
});

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

    start: function(managementService, domainName, contact) {
        this.managementService = managementService;
        this.domainName = domainName;
        this.contactId = contact.id;
        this.deferred = $.Deferred();
        this.managementService.getContact(domainName, this.contactId)
                .done($.proxy(function(contact) { this._setupDialog(contact).show(this); }, this))
                .fail($.proxy(function() { this.deferred.reject(); this.deferred = null; }, this));
        return this.deferred.promise();
    },

    onSubmit: function(dialog) {
        var contact = this._createContact(dialog);
        dialog.destroy();

        this.managementService.setContact(this.domainName, this.contactId, contact)
                .done($.proxy(function() { this.deferred.resolve(contact); 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;
    },

    _createContact: function(dialog) {
        return new jscape.Contact(this.contactId,
                                  dialog.getName(),
                                  dialog.getEmail(),
                                  dialog.getCompany(),
                                  dialog.getOwner(),
                                  dialog.getTags());
    },

    _setupDialog: function (contact) {
        var dialog = new jscape.domain.EditContactDialog();
        dialog.setName(contact.name);
        dialog.setEmail(contact.email);
        dialog.setCompany(contact.company);

        this.managementService.getAccountNames(this.domainName)
                .done(function (owners) {
                          dialog.setOwners(owners.sort());
                          dialog.setOwner(contact.owner);
                      });
        this.managementService.getDomainTags(this.domainName)
                .done(function (tags) {
                          dialog.setAvailableTags(tags);
                          dialog.setTags(contact.tags);
                      });
        return dialog;
    }
});

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

    start: function (managementService, domainName, contacts) {
        this.managementService = managementService;
        this.domainName = domainName;
        this.deferred = $.Deferred();
        this._confirmDelete(contacts)
                .done($.proxy(this._deleteConfirmed, this, contacts))
                .fail($.proxy(function() { this.deferred.reject(); this.deferred = null; }, this));
        return this.deferred.promise();
    },

    _confirmDelete: function(contacts) {
        var text = $.map(contacts || [], function(contact) { return contact.name; }).join(", ");
        return jscape.ConfirmDialog.confirm(
                window.R.string.CONTACTS_CONFIRM_DELETE_TITLE,
                window.R.string.CONTACTS_CONFIRM_DELETE_MESSAGE.supplant({0: text}));
    },

    _deleteConfirmed: function(contacts) {
        $.when.apply(jQuery, $.map(contacts, $.proxy(function(contact) { return this.managementService.deleteContact(this.domainName, contact.id); }, this)))
                .done($.proxy(function () { this.deferred.resolve(contacts); this.deferred = null; }, this))
                .fail($.proxy(function () { this.deferred.reject(); this.deferred = null; }, this));
    }
});

/** @class */
jscape.domain.ContactDialog = Class.extend(
    /** @lends {jscape.domain.AddContactDialog.prototype} */
{
    /** @constructor */
    initialize: function(dialog) {
        this.dialog = dialog.clone().attr("id", new Date().getTime().toString(36));
        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=contactfields]", this.dialog);

        this.name = $("[name=name]", this.fieldset).textbox({required: true, validType: "contact_name_nonempty", missingMessage: window.R.string.CONTACTS_ERROR_BAD_NAME, width: "60%"});
        this.email = $("[name=email]", this.fieldset).textbox({required: true, validType: "multi_value['email',',']", missingMessage: window.R.string.CONTACTS_ERROR_BAD_EMAIL, invalidMessage: window.R.string.CONTACTS_ERROR_BAD_EMAIL, width: "50%"});
        this.company = $("[name=company]", this.fieldset).textbox({width: "50%"});

        this.ownedBy = $("[name=ownedby]:checkbox", this.fieldset).on("change", $.proxy(this._adjustFieldState, this));
        this.owner = $("[name=owner]", this.fieldset).combobox({required: false, editable: true, limitToList: true, disabled: true, width: 120, panelHeight: "auto"});

        this.tags = $("select[name=tags]", this.fieldset).tagbox({limitToList: true, hasDownArrow: true, panelHeight: "auto", width: "60%", onLoadSuccess: function() { $(this).tagbox("clear"); }});
    },

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

    getName: function() {
        return this.name.textbox("getValue");
    },

    setName: function(value) {
        this.name.textbox("setValue", value);
    },

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

    setEmail: function(value) {
        this.email.textbox("setValue", value);
    },

    getCompany: function() {
        return this.company.textbox("getValue");
    },

    setCompany: function(value) {
        this.company.textbox("setValue", value);
    },

    setOwners: function(values) {
        this.owner.combobox("loadData", values);
        this.ownedBy.prop("disabled", values.length == 0).prop("checked", false);
    },

    getOwner: function() {
        return this.ownedBy.is(":checked") ? this.owner.combobox("getValue") : null;
    },

    setOwner: function(value) {
        if (value != null && value.trim().length) {
            this.ownedBy.prop("checked", true);
            this.owner.combobox("setValue", value);
            this._adjustFieldState();
        } else {
            this.ownedBy.prop("checked", false);
        }
    },

    setAvailableTags: function (values) {
        this.tags.tagbox("loadData", values);
    },

    getTags: function () {
        return this.tags.tagbox("getValues");
    },

    setTags: function (values) {
        this.tags.tagbox("setValues", values);
    },

    _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.ownedBy.is(":checked");
        this.owner.combobox(enabled ? "enable" : "disable");
    }
});

/** @class */
jscape.domain.AddContactDialog = jscape.domain.ContactDialog.extend(
    /** @lends {jscape.domain.AddContactDialog.prototype} */
{
    /** @constructor */
    initialize: function() {
        this._super($("#d-contact-add"));
    }
});

/** @class */
jscape.domain.EditContactDialog = jscape.domain.ContactDialog.extend(
    /** @lends {jscape.domain.AddContactDialog.prototype} */
{
    /** @constructor */
    initialize: function() {
        this._super($("#d-contact-edit"));

        this.title = this.dialog.dialog("header").text();
    },

    setName: function(value) {
        this._super(value);
        this.dialog.dialog("setTitle", this.title.supplant({0: value}));
    }
});