define(['./role.js'], function (Role) {

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

    Roles.Collection = function () {
        'use strict';
        this.view = null;
        /**
         * Array of Role objects
         * @private
         * @type {Array}
         */
        this.roles = [];
    };

    Roles.Collection.prototype = (function () {
        'use strict';
        var setView = function (view) {
            this.view = view;
        };
        /**
         * Add models to the collection
         * @private
         * @memberOf Roles
         * @param {Object} data - JSON data for all rules
         */
        var setData = function (data) {
            var self = this;
            _.each(data, function (role) {
                if (role instanceof Role) {
                    self.roles.push(role);
                } else {
                    var instance = new Role();
                    instance.model.setData(role);
                    self.roles.push(instance);
                }
            });
        };

        /**
         * Get array of Role objects
         * @private
         * @memberOf Roles
         * @returns {Array}
         */
        var getData = function () {
            if (_.isArray(this.roles)) {
                return this.roles;
            } else {
                return [];
            }
        };

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

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

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

            /**
             * Get an array of Role objects
             * @public
             * @memberOf Roles
             * @returns {Array}
             */
            roles: getData,

            /**
             * Get an Role object from collection
             * @public
             * @memberOf Roles
             * @returns {Object}
             */
            getByRoleId: getByRoleId,

            /**
             * Get array index of a Role object from collection
             * @public
             * @memberOf Roles
             * @returns {Number}
             */
            getIndexByRoleId: getIndexByRoleId
        };
    })();

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

        this.el = null;
    };

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

        /**
         * Set DOM element
         * @private
         * @memberOf ICFieldRights.Fields.View
         * @param {Object} el - jQuery DOM element from ICFieldRights.Group.View
         */
        var setElement = function (el) {
            this.el = el.find('table');
        };

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

        var render = function (el) {
            var self = this;
            setElement.call(this, el);
            this.el.prepend(_.template($('#roles-tmpl').html())({}));
            _.each(this.collection.roles, function (role) {
                role.view.render(getElement.call(self));
            });
            makeSortable.call(this);
        };

        /**
         * Make group rights table sortable
         * @private
         * @memberOf ICFieldRights.Group
         * @param {Object} self - ICFieldRights.Group instance
         */
        var makeSortable = function () {
            var self = this;
            $(self.el).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);
                    //in these next few lines, we get the group model, find the rows that changed,
                    //and then update it accordingly (the table has already been updated)
                    var groupId = parseInt(ui.item.attr('data-group-id'));
                    var ruleId = parseInt(ui.item.attr('data-rule-id'));
                    var group = Instance.Groups.collection.getByGroupId(groupId);
                    var originalPlace = self.originalOrder.indexOf(ruleId);
                    var currentPlace = self.currentOrder.indexOf(ruleId);
                    var right = group.model.rights[originalPlace];
                    group.model.rights.splice(originalPlace,1);
                    group.model.rights.splice(currentPlace, 0, right);
                    //then we add and undo token
                    Instance.Undo.model.addHistory({
                        action: 'move',
                        groupId: groupId,
                        ruleId: ruleId,
                        original: self.originalOrder,
                        current: self.currentOrder,
                    });
                }
            }).disableSelection();
            return self;
        };

        /**
         * Helper for draggable rules rows
         * @private
         * @memberOf ICFieldRights.Group
         * @param {} 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;
        };

        /**
         * Get order of rules from the draggable table.
         * @private
         * @memberOf ICFieldRights.Group
         * @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;
        };

        return {
            setCollection: setCollection,

            render: render,

            el: getElement
        };
    })();

    return Roles;
});
