define(['./rule.js', './rights.js', 'jquery', 'cla_select2'], function (Rule, Rights, $) {

    /**
     * Collection of rules
     * @class
     * @constructor
     * @property {Object} rules - an array of rules
     */
    var Rules = function () {
        'use strict';
        this.collection = new Rules.Collection();
        this.view = new Rules.View();
        this.collection.setView(this.view);
        this.view.setCollection(this.collection);
    };

    Rules.Collection = function () {
        'use strict';
        this.view = null;
        /**
         * @private
         * @type {Array}
         */
        this.rules = [];
    };

    Rules.Collection.prototype = (function () {
        'use strict';
        var setView = function (view) {
            this.view = view;
        };

        /**
         * Add Rule objects to rules array
         * @private
         * @memberOf Rules
         * @param {Object} data - JSON data for all rules
         */
        var setData = function (data) {
            var self = this;
            _.each(data, function (rule) {
                if (rule instanceof Rule) {
                    self.rules.push(rule);
                } else {
                    var instance = new Rule(rule);
                    instance.model.setData(rule);
                    self.rules.push(instance);
                }
            });
        };

        /**
         * Get array of Rule objects
         * @private
         * @memberOf Rules
         * @returns {Array}
         */
        var getData = function () {
            return this.rules;
        };

        /**
         * Fetch a group from the Rules collection by id
         * @public
         * @memberOf Rules
         * @param {Number} ruleId    - grruleoup id
         * @returns {Object}
         */
        var getByRuleId = function (ruleId) {
            var rule = _.find(Instance.Rules.collection.rules, function (rule) {
                return parseInt(rule.model.ruleId, 10) === parseInt(ruleId, 10);
            });
            return rule;
        };

        /**
         * Fetch the index from the Rules collection
         * @public
         * @memberOf Rules
         * @param {Number} ruleId    - rule id
         * @returns {Number}
         */
        var getIndexByRuleId = function (ruleId) {
            var index = _.findIndex(Instance.Rules.collection.rules, function (rule) {
                return parseInt(rule.model.ruleId, 10) === parseInt(ruleId, 10);
            });
            return index;
        };

        /**
         * Delete a ruleset from a group
         * @private
         * @memberOf ICFieldRights.Group
         * @returns {Object} ICFieldRights.Group
         */
        var deleteRule = function (event) {
            event.preventDefault();
            if (confirm('Are you sure you want to delete this rule set?')) {
                var groupId = parseInt($(this).data('group-id'), 10);
                var ruleId = parseInt($(this).data('rule-id'), 10);
                var group = Instance.Groups.collection.getByGroupId(groupId);
                var rightIndex = _.findIndex(group.model.rights, function (right) {
                    return parseInt(right.collection.ruleId, 10) === parseInt(ruleId, 10);
                });

                // The two base properties of ICFieldRights.Rights have references to each other which break a deep copy (stack recursion error)
                // These properties (collection and view) are only used inside this instance so with a shallow copy they maintain their circular
                // references and undo still works.
                var rights = $.extend(new Rights(), group.model.rights[rightIndex]);

                Instance.Undo.model.addHistory({
                    action: 'delete-rule',
                    ruleId: ruleId,
                    groupId: groupId,
                    rights: rights,
                    rightIndex: rightIndex
                });
                group.model.rights.splice(rightIndex, 1);
                $(this).tooltip('hide');
                Instance.Groups.view.render();
            }
        };

        return {
            setView: setView,
            /**
             * Initalise object
             * @public
             * @memberOf Rules
             */
            setData: setData,

            /**
             * Get an array of Rule objects
             * @public
             * @memberOf Rules
             * @returns {Array}
             */
            rules: getData,

            /**
             * Get an Rule object for rules array
             * @public
             * @memberOf Rules
             * @returns {Object}
             */
            getByRuleId: getByRuleId,

            /**
             * Get array index of a Rule object
             * @public
             * @memberOf Rules
             * @return {Number}
             */
            getIndexByRuleId: getIndexByRuleId,

            deleteRule: deleteRule

        };
    })();

    Rules.View = function () {
        'use strict';
        this.collection = null;
    };

    Rules.View.prototype = (function () {
        'use strict';
        var setCollection = function (collection) {
            this.collection = collection;
        };

        /**
         * Show add rule modal and bind button to method
         * @private
         * @memberOf ICFieldRights.Group
         * @returns {Object} ICFieldRights.Group
         */
        var addRule = function () {
            var groupId = parseInt($(this).data('group-id'), 10);
            var group = Instance.Groups.collection.getByGroupId(groupId);
            group.model.createConditionsDropdown();
            $('#add-rule').attr('href', '#group-' + groupId);
            $('#add-rule-id').select2().select2({'data': null , width: '300px'});
            $('#add-rule-modal').modal('show');
            $('#add-rule').unbind('click').on('click', function () {
                //if the button is disabled, do nothing
                if($(this).hasClass('disabled') || $(this).is('[disabled]'))
                    return;
                insertRule(group);
                $('#add-rule-modal').modal('hide');
            });
        };

        /**
         * Add ruleset to a group
         * @private
         * @memberOf ICFieldRights.Group
         * @returns {Object} ICFieldRights.Group
         */
        var insertRule = function (group) {
            var ruleId = parseInt($('#add-rule-id').val(), 10);
            var groupId = parseInt(group.model.id, 10);
            var rule = Instance.Rules.collection.getByRuleId(ruleId);
            var instance = new Rights();
            instance.collection.setData({
                group_id: groupId,
                name: rule.model.name,
                rule_id: ruleId
            });
            instance.collection.setRights(instance.collection.createDefaultRights());
            group.model.rights.push(instance);
            Instance.Groups.view.render();
            Instance.Undo.model.addHistory({
                action: 'add-rule',
                ruleId: ruleId,
                groupId: groupId
            });
        };

        /**
         * Show edit rule modal and bind button to method
         * @private
         * @memberOf ICFieldRights.Group
         * @returns {Object} ICFieldRights.Group
         */
        var editRule = function () {
            var el = $(this);
            var groupId = parseInt($(this).data('group-id'), 10);
            var group = Instance.Groups.collection.getByGroupId(groupId);
            group.model.createConditionsDropdown(el.data('rule-id'));
            // $('#edit-rule-id').select2().select2('val', el.data('rule-id'));
            $('#edit-rule-id').select2().select2({'val': el.data('rule-id'), width: '100%'});
            $('#edit-rule-modal').modal('show');
            $('#edit-rule').unbind('click').on('click', function (event) {
                event.preventDefault();
                var rule = Instance.Rules.collection.getByRuleId(el.data('rule-id'));
                updateRule(group, rule);
                $('#edit-rule-modal').modal('hide');
            });
        };

        /**
         * Update a ruleset
         * @private
         * @memberOf ICFieldRights.Group
         * @param {Number} original - original rule id
         */
        var updateRule = function (group, rule) {
            var ruleId = parseInt($('#edit-rule-id').val(), 10);
            var newRule = Instance.Rules.collection.getByRuleId(ruleId);
            var originalRuleIndex = _.findIndex(group.model.rights, function (right) {
                return parseInt(right.collection.ruleId, 10) === parseInt(rule.model.ruleId, 10);
            });
            var fieldRights = group.model.rights[originalRuleIndex];
            var position = parseInt(group.model.rights[originalRuleIndex].collection.position, 10);
            group.model.rights.splice(originalRuleIndex, 1);
            var instance = new Rights();
            instance.collection.setData({
                group_id: group.model.id,
                name: newRule.model.name,
                rule_id: newRule.model.ruleId,
                position: position,
                field_rights: fieldRights.collection.rights
            });
            group.model.rights.splice(originalRuleIndex, 0, instance);
            Instance.Undo.model.addHistory({
                action: 'edit-rule',
                originalRuleId: rule.model.ruleId,
                currentRuleId: ruleId,
                fieldRights: fieldRights,
                groupId: parseInt(group.model.id, 10)
            });
            Instance.Groups.view.render();
            window.location.hash = 'group-' + group.model.id;
        };

        return {
            setCollection: setCollection,

            addRule: addRule,

            editRule: editRule,

            updateRule: updateRule,

            insertRule: insertRule

        };
    })();

    return Rules;
});
