define(['./rule.js', './rules.js', 'jquery'], function (Rule, Rules, $) {

    /**
     * Field class
     * @class Field
     * @constructor
     * @param {Object} field
     * @propery {Object} el - jQuery object
     */
    var Field = function (field) {
        /**
         * Field.Model
         * @type {Object}
         * @memberOf Field
         */
        this.model = new Field.Model(field);

        /**
         * Field.View
         * @type {Object}
         * @memberOf Field
         */
        this.view = new Field.View();

        this.model.setView(this.view);
        this.view.setModel(this.model);
    };

    /**
     * Field model class
     * @class Field.Model
     * @property {String} label - field label
     * @property {String} symName - field symbolic name
     * @property {Array} rules - array of rule objects
     * @property {String} type - field type
     */
    Field.Model = function (model) {
        this.view = null;

        /**
         * Field label
         * @type {String}
         * @memberOf Field.Model
         */
        this.label = null;

        /**
         * Field symbolic name
         * @type {String}
         * @memberOf Field.Model
         */
        this.symName = null;

        /**
         * ICDynamicFieldChanges.Rules.Collection
         * @type {Object}
         * @memberOf Field.Model
         */
        this.rules = {};

        /**
         * Field type
         * @type {String}
         * @memberOf Field.Model
         */
        this.type = null;

        this.init(model);
    };

    Field.Model.prototype = (function () {
        var setView = function (view) {
            this.view = view;
        };

        /**
         * Set object data
         * @private
         * @memberOf Field.Model
         * @param {Object} data
         */
        var setData = function (data) {
            setLabel.call(this, data.label);
            setSymName.call(this, data.symName);
            setRules.call(this, data.rules, data.symName);
            setType.call(this, data.type);
        };

        /**
         * Set field label
         * @private
         * @memberOf Field.Model
         * @param {String} label - field label
         */
        var setLabel = function (label) {
            this.label = label;
        };

        /**
         * Get field label
         * @private
         * @memberOf Field.Model
         * @returns {String}
         */
        var getLabel = function () {
            if (_.isString(this.label)) {
                return this.label;
            } else {
                return '';
            }
        };

        /**
         * Set field symbolic name
         * @private
         * @memberOf Field.Model
         * @param {String} symName - field symbolic name
         */
        var setSymName = function (symName) {
            this.symName = symName;
        };

        /**
         * Get field symbolic name
         * @private
         * @memberOf Field.Model
         * @returns {String}
         */
        var getSymName = function () {
            if (_.isString(this.symName)) {
                return this.symName;
            } else {
                return '';
            }
        };

        /**
         * Set field rules
         * @private
         * @memberOf Field.Model
         * @param {Object} rules - JSON object
         */
        var setRules = function (rules, fieldSymName) {
            this.rules = new Rules(rules, fieldSymName);
        };

        /**
         * Get field rules
         * @private
         * @memberOf Field.Model
         * @returns {Array}
         */
        var getRules = function () {
            if (_.isArray(this.rules)) {
                return this.rules;
            } else {
                return [];
            }
        };

        /**
         * Set field type
         * @private
         * @memberOf Field.Model
         * @param {String} type - field type
         */
        var setType = function (type) {
            this.type = type;
        };

        /**
         * Get field type
         * @private
         * @memberOf Field.Model
         * @returns {String}
         */
        var getType = function () {
            if (_.isString(this.type)) {
                return this.type;
            } else {
                return '';
            }
        };

        /**
         * Get order of rules from the draggable table.
         * @private
         * @memberOf Field.Model
         * @param {String} row - jQuery selector
         */
        var getOrder = function (row) {
            var order = [];
            _.each(row.parent().find('tr'), function (tr) {
                if ($(tr).data('rule-id')) {
                    order.push($(tr).data('rule-id'));
                }
            });
            return order;
        };

        /**
         * Add a field to the collection. Called by modal
         * @private
         * @memberOf Field.Model
         * @param {Object} event - jQuery event object
         */
        var addField = function (event) {
            var field = event.data.field;
            var ruleId = parseInt($('#trigger-select').val(), 10);
            var name = $('#trigger-select option:selected').text();
            var newValueInput, newValue, newValueText;

            switch (field.model.type) {
                case lmsg('forms.fbfield.sh_string') :
                case lmsg('forms.fbfield.med_string') :
                case lmsg('forms.fbfield.long_string') :
                    newValueInput = $('#_field_' + field.model.symName);
                    newValueText = newValueInput.val();
                    newValue = newValueInput.val();
                    break;
                case lmsg('forms.fbfield.sh_text') :
                case lmsg('forms.fbfield.med_text') :
                case lmsg('forms.fbfield.long_text') :
                    newValueInput = $('#_field_' + field.model.symName);
                    newValueText = newValueInput.val();
                    newValue = newValueInput.val();
                    break;
                case lmsg('forms.fbfield.checkbox') :
                    newValueInput = $('#_field_' + field.model.symName);
                    newValueText = newValueInput.prop('checked') ? 'Yes' : 'No';
                    newValue = newValueInput.val();
                    break;
                case lmsg('forms.fbfield.radio') :
                    newValueInput = $('input[name="field_radio' + field.model.symName + '"]:checked');
                    newValueText = newValueInput.parent().text();
                    newValue = newValueInput.val();
                    break;
                case lmsg('forms.fbfield.select') :
                    newValueInput = $('#_field_' + field.model.symName);
                    newValueText = $('#_field_' + field.model.symName + ' option:selected').text();
                    newValue = newValueInput.val();
                    break;
                case lmsg('forms.fbfield.mult_select') :
                    newValueInput = $('#_field_' + field.model.symName);
                    var values = [];
                    _.each($('#_field_' + field.model.symName + ' option:selected'), function (selected) {
                        values.push($(selected).text());
                    });
                    newValue = newValueInput.val();
                    newValueText = values.join(', ');
                    break;
                case lmsg('forms.fbfield.date_dropdowns') :
                    var dayInput = $('#_field_' + field.model.symName + '_day :selected');
                    var monthInput = newValueInput = $('#_field_' + field.model.symName + '_month :selected');
                    var yearInput = newValueInput = $('#_field_' + field.model.symName + '_year :selected');
                    newValueText = dayInput.text() + ' ' + monthInput.text() + ' ' + yearInput.text();
                    newValue = dayInput.val() + '-' + monthInput.val() + '-' + yearInput.val();
                    break;
                case lmsg('forms.fbfield.label') :
                    newValueText = '@TODO';
                    newValue = newValueInput.val();
                    break;
                case lmsg('forms.fbfield.users') :
                    newValueInput = $('#_field_' + field.model.symName);
                    newValueText = $('#_field_' + field.model.symName + ' option:selected').text();
                    newValue = newValueInput.val();
                    break;
                case lmsg('forms.fbfield.mult_users') :
                    newValueInput = $('#_field_' + field.model.symName);
                    var values = [];
                    _.each($('#_field_' + field.model.symName + ' option:selected'), function (selected) {
                        values.push($(selected).text());
                    });
                    newValue = newValueInput.val();
                    newValueText = values.join(', ');
                    break;
                case 'HTML' :
                    // @TODO broken
                    break;
                case lmsg('forms.fbfield.group') :
                    // @TODO broken
                    break;
                case lmsg('forms.fbfield.role') :
                    // @TODO broken
                    break;
                case lmsg('forms.fbfield.document') :
                    // @TODO broken
                    break;
                case lmsg('forms.fbfield.file') :
                    // @TODO broken
                    break;
                case lmsg('forms.fbfield.date_str') :
                    // @TODO broken
                    break;
            }

            var instance = new Rule({
                id: ruleId,
                label: field.model.label,
                name: name,
                newValue: newValue,
                newValueText: newValueText
            });

            field.model.rules.collection.models.push(instance);
            Instance.Undo.model.addHistory({
                action: 'add-rule',
                id: ruleId,
                label: field.model.label,
                fieldSymName: field.model.symName,
                name: name,
                newValue: newValue,
                newValueText: newValueText
            });
            $('#add-rule').modal('hide');
            Instance.Fields.view.render();
        };

        return {
            setView: setView,

            /**
             * Set object data
             * @public
             * @memberOf Field.Model
             * @param {Object} data
             */
            init: setData,

            /**
             * Field label
             * @public
             * @memberOf Field.Model
             * @returns {String}
             */
            label: getLabel,

            /**
             * Field symbolic name
             * @public
             * @memberOf Field.Model
             * @returns {String}
             */
            symName: getSymName,

            /**
             * Field rules
             * @public
             * @memberOf Field.Model
             * @returns {Array}
             */
            rules: getRules,

            /**
             * Field type
             * @public
             * @memberOf Field.Model
             * @returns {String}
             */
            type: getType,

            /**
             * Add field
             * @public
             * @memberOf Field.Model
             * @param {Object} event - jQuery event object
             */
            addField: addField
        };
    })();

    /**
     * Field view object
     * @class Field.View
     * @constructor
     * @param {Object} controller - Field object
     */

    Field.View = function () {
        this.model = null;

        /**
         * DOM element
         * @private
         * @type {Object}
         * @memberOf Field.View
         */
        this.el = null;

    };

    Field.View.prototype = (function () {
        var setModel = function (model) {
            this.model = model;
        };

        /**
         * Render Field object
         * @private
         * @memberOf Field.View
         */
        var render = function () {
            $('#fields-list').append(_.template($('#field-tmpl').html())(this.model));
            setElement.call(this);
            this.model.rules.view.setElement(this.el.find('table tbody').eq(0));
            this.model.rules.view.render();
            bindElements.call(this);
            makeSortable.call(this);
        };

        /**
         * Set view DOM element
         * @private
         * @memberOf Field.View
         */
        var setElement = function () {
            this.el = $('#fields-list').find('div[data-sym-name=' + this.model.symName + ']').eq(0);
        };

        /**
         * Get view DOM element
         * @private
         * @memberOf Field.View
         * @returns {Object}
         */
        var getElement = function () {
            return this.el;
        };

        /**
         * Bind DOM elements withing view to events
         * @private
         * @memberOf Field.View
         */
        var bindElements = function () {
            this.el.find('a[name="new_rule_link"]').on('click', addRuleModal);
        };

        /**
         * Helper for draggable rules rows
         * @private
         * @memberOf Field.View
         * @param {Object} event - Event
         * @param {Object} el - jQuery object to fix
         * @return {Object} el
         */
        var fixHelper = function (event, el) {
            el.children().each(function () {
                $(this).width($(this).width());
            });
            return el;
        };

        /**
         * Make group rights table sortable
         * @private
         * @memberOf Field.View
         */
        var makeSortable = function () {
            var self = this;
            $(self.el).find('table').sortable({
                items: 'tbody > tr',
                helper: fixHelper,
                handle: '.js-drag-handle',
                start: function (event, ui) {
                    self.originalOrder = getOrder(ui.item);
                },
                stop: function (event, ui) {
                    self.currentOrder = getOrder(ui.item);
                    Instance.Undo.addHistory({
                        action: 'move',
                        symName: self.symName,
                        original: self.originalOrder,
                        current: self.currentOrder
                    });
                }
            }).disableSelection();
            return self;
        };

        /**
         * @private
         * @memberOf Field.View
         * @returns Field.View
         */
        var addToSelect = function () {
            $('#add-rule-select').append(_.template($('#field-rule-tmpl').html())(this.model));
            return this;
        };

        /**
         * @private
         * @memberOf Field.View
         */
        var addRuleModal = function () {
            var self;
            if (this instanceof Field.View) {
                self = this.controller;
            } else {
                self = Instance.Fields.collection.getByFieldSymName($(this).data('sym-name'));
            }
            //console.log(self);
            self.model.mode = 'Add';
            $('#modal-container').html(_.template($('#add-field-tmpl').html())(self.model));
            $('#add-rule').modal('show');
            var remaining = _.reject(Instance.Triggers.collection.models, function (trigger) {
                var match = false;
                var x = _.find(self.model.rules.collection.models, function (rule) {
                    return rule.id === trigger.model.id;
                });
                if (!_.isUndefined(x)) {
                    match = true;
                }
                return match;
            });
            _.each(remaining, function (test) {
                $('#trigger-select').append(_.template($('#trigger-select-tmpl').html())(test.model));
            });
            xajax_rule_add(self.model.symName, window.projectId);
            $('#modal-container').find('a[name="save-rule"]').on('click', {
                field: self
            }, self.model.addField);
        };

        return {
            setModel: setModel,
            /**
             * @public
             * @memberOf Field.View
             */
            render: render,

            /**
             * @public
             * @memberOf Field.View
             */
            addToSelect: addToSelect,

            addRuleModal: addRuleModal
        };
    })();

    return Field;
});
