define([
  // Application.
  "app",
],

function (app) {

    var LocksManager = { Models: {}, Collections: {}, };

    LocksManager.init = function (ctx, module, app) {
        app.models.locksManager = new module.Model();
        app.models.locksManager.start();
    }; 

    LocksManager.Models.Lock = Backbone.Model.extend({
        defaults: {
            id: null,
            name: null,
            agentId: null,
            owner: null,
            status: null,
            timestamp: null,
            data: null, 
            timeout: null,
        },
    });

    LocksManager.Collections.Locks = Backbone.Collection.extend({
        model: LocksManager.Models.Lock,
        fetch: function (opt) {
            var options = {
                method: "set",
                async: true,
                callback: null, 
            };

            options = _.extend(options, (opt) ? opt : {}); 

            var that = this,
                QP = new Core.Database.QueryParameters();

            Core.Json.CallProcedure(app.DatabaseNames.IH + ".dbo.GetLocks", QP, {
                onSuccess: function (response) {
                    if (response && response.Table) {
                        records = response.Table;

                        var mdls = [];
                        for (var i = 0, len = records.length; i < len; i++) {
                            var data = records[i];
                            var obj = _.clone(LocksManager.Models.Lock.prototype.defaults);

                            _.extend(obj, {
                                id: data.Id,
                                name: data.Name,
                                agentId: data.AgentId,
                                owner: data.Owner,
                                status: data.Status,
                                timestamp: data.Timestamp,
                                data: data.Data, 
                                timeout: data.Timeout,
                            });

                            mdls.push(obj);
                        }

                        that[options.method](mdls);

                        if (options.callback != null && _.isFunction(options.callback))
                            options.callback.call(this, that); 
                    }
                },
                Async: options.async,
                Cache: true, 
            }, app.ConnectionStrings.app);

            return this;
        }
    });

    LocksManager.Models.LockCategory = Backbone.Model.extend({
        defaults: {
            name: null,
            locks: null,
        },
        initialize: function () {
            this.set({ locks: new LocksManager.Collections.Locks() });
        }
    });

    LocksManager.Collections.LocksCategories = Backbone.Collection.extend({
        model: LocksManager.Models.LockCategory,
        fetch: function () {
        }
    });

    LocksManager.Model = Backbone.Model.extend({
        defaults: {
            locks: null,
            categories: null,
            //locksCategories
        },

        toid: null,
        delay: 2000,
        refreshing: false,

        initialize: function ()
        {
            _.bindAll(this);
            
            this.set({ locks: new LocksManager.Collections.Locks(), categories: new LocksManager.Collections.LocksCategories()}); 

            this.setCategories(); 
            this.bindEvents(); 
        },

        setCategories: function () {
            var categories = this.get("categories");
            categories.add([{ name: "TAGSCONFIGCHANGE" }]);
            categories.add([{ name: "DBSTARTMON" }]);
            categories.add([{ name: "DBSTOPMON" }]);
            categories.add([{ name: "DBFIXCONFLICTS" }]);
            categories.add([{ name: "AGENTDISCONNECTED" }]);
            categories.add([{ name: "SETPLCADDRESS" }]);
            categories.add([{ name: "SETOPCADDRESS" }]);
            categories.add([{ name: "ENABLE" }]);
            categories.add([{ name: "DISABLE" }]);
            categories.add([{ name: "UNREGISTER" }]);
            categories.add([{ name: "CONFIGUREDASH" }]);
            categories.add([{ name: "GETDASHCONFIGURATION" }]);
        }, 

        bindEvents: function () {
            this.listenTo(this.get("locks"), 'add', this.lockAdded);
            this.listenTo(this.get("locks"), 'remove', this.lockRemoved);
            this.listenTo(this.get("locks"), 'change', this.locksChanged);
            this.listenTo(this.get("locks"), 'reset', this.locksReset);
        }, 

        fetch: function (opt)
        {
            try
            {
                clearTimeout(this.toid);
                
                this.get('locks').fetch(opt);
            }
            catch (Error) { console.error(Error); }
            finally
            {
                if (this.refreshing)
                    this.toid = setTimeout(this.fetch, this.delay);
            }
        },

        start: function ()
        {
            if (!this.refreshing)
            {
                this.refreshing = true;

                this.fetch({ method: "reset" });
            }

            return this;
        },

        stop: function ()
        {
            clearTimeout(this.toid);

            this.refreshing = false;

            return this;
        },

        setModelToCategory: function (models, method) {
            if (!_.isArray(models)) models = [models];
            
            var categories = {}; 
            for (var i = 0, len = models.length; i < len; i++){
                var model = models[i]; 
                var category = model.get ? model.get("name").toString().toUpperCase() : model["Name"].toString().toUpperCase();
                
                if (!_.has(categories, category)) categories[category] = [];
                categories[category].push(model); 
            }
            
            for (var cat in categories) {
                if (_.indexOf(["add", "remove"], method) != -1) {
                    for (var i = 0, len = categories[cat].length; i < len; i++)
                        this.modelToCategory(categories[cat][i], cat, method);
                } else {
                    this.modelToCategory(categories[cat], cat, method); 
                }
            }
        }, 

        modelToCategory: function(models, category, method){
            var categories = this.get("categories");
            var category = categories.findWhere({ name: category }); 

            if (category != null) category.get("locks")[method](models); 
        }, 

        locksChanged: function (e) {
            var m = e; 
            var categories = this.get("categories");
            categories.forEach(function (model) {
                model.get("locks").set(m, { add: false, remove: false, merge: true });
            }); 
        },
        
        locksReset: function (e) {
            var models = e.models;
            this.setModelToCategory(models, "set"); 
        }, 

        lockAdded: function (e) {
            var model = e; 
            this.setModelToCategory(model, "add");
        },

        lockRemoved: function (e) {
            var model = e;
            this.setModelToCategory(model, "remove");
        },

        listenToCategory: function (caller, action, cat, fn) {
            action = _.isArray(action) ? action : [action];
            cat = _.isArray(cat) ? cat : [cat];

            if (caller != null) {
                for (var c = 0, len2 = cat.length; c < len2; c++) {
                    var catName = cat[c];
                    var category = this.get("categories").findWhere({ name: catName.toString().toUpperCase() });
                    if (category != null) {
                        for (var a = 0, len1 = action.length; a < len1; a++) {
                            var act = action[a];
                            caller["listenTo"](category.get("locks"), act, (_.isString(fn)) ? caller[fn] : fn);
                        }
                    }
                }
            }
        },

        getCategoryLocks: function (cat, agentId, returnCollection) {
            returnCollection = _.isBoolean(returnCollection) ? returnCollection : false; 
            cat = _.isArray(cat) ? cat : [cat]; 
            var categories = this.get("categories").filter(function (c) { return (_.indexOf(cat, c.get("name")) != -1); });

            var locksCollections = [];
            var singleLocks = []; 

            for (var i = 0, len = cat.length; i < len; i++) {
                var category = _.find(categories, function (m) { return m.get("name") == cat[i]; });
                if (category != null) {
                    if (agentId != null) {
                        var lock = category.get("locks").findWhere({ agentId: parseInt(agentId) });
                        if (lock != null) singleLocks.push(lock);
                    } else locksCollections.push(category.get("locks"));
                }
            }

            if (agentId != null) {
                if (returnCollection) {
                    var locksCollection = new LocksManager.Collections.Locks(singleLocks); 
                    return locksCollection;
                }
                else return (singleLocks.length > 0) ? singleLocks[0] : null;
            } else {
                if (returnCollection) {
                    var arr = []; 
                    for (var i = 0, len = locksCollections.length; i < len; i++)
                        arr = arr.concat(locksCollections[i].toJSON()); 

                    var locksCollection = new LocksManager.Collections.Locks(arr);
                    return locksCollection;
                }
                else return (locksCollections.length > 0) ? locksCollections[0] : null;
            }
        },

    });

    // Required, return the module for AMD compliance.
    return LocksManager;

});
