'use strict';
//SCREEN-BOILERPLATE

//this boilerplate builds screens that are gonna be shown in the <div class="content"> tag of our main HTML.
//do not build app components with this boilerplate, only screens (reports)

define([
  // Application variable, always include it to have access to app methods.
  "app",

  //templates-loader: this loads templates async.
  "js/templates-loader",

  "modules/modal",

  "d3", 

],

function (app, T, Modal) {

    var dotBlinker = function (graph, adquiring) {
        var that = this;

        this.graph = graph;
        this.adquiring = adquiring;
        this.active = false;
        this.blinking = false;
        this.stopping = false;
        this.titleObj = null;

        this._addTitle = function () {
            var adq_graphnode_path = that._getSvgObj();

            if (adq_graphnode_path)
            {
                //var titleKey = 'dot_status_' + that.adquiring.Status.toString();

                //var title = app.i18n.global.translate(titleKey).fetch();

                //that.titleObj = adq_graphnode_path
                //    .append('svg:title')
                //    .text(title);

                that.titleObj = adq_graphnode_path
                    .append('svg:title')
                    .text(that.adquiring.StatusName);
            }
        };
        this._blink = function () {
            if (that.stopping == false) {
                that.active = !that.active;

                var adq_graphnode_path = that._getSvgObj();

                if (adq_graphnode_path)
                {
                    adq_graphnode_path
                        .transition()
                        .duration(1000)
                        .style(
                            'fill',
                            function (d) {
                                var color;

                                if (that.active) {
                                    color = '#FF0000';
                                }
                                else {
                                    if (that.adquiring.IsConnected == true)
                                        color = '#' + d.color.replace('#', '');
                                    else
                                        color = d3_helpers.COLOR_DISCONNECTED;
                                }

                                return color;
                            }
                        )
                        .each(
                            'end',
                            function () {
                                setTimeout(that._blink, 1);
                            }
                        );
                }
            }
            else {
                that._stopBlink();

                that._removeTitle();
            }
        };
        this.destroy = function() {
            that.stop();
        };
        this._getSvgObj = function () {
            var adq_graphnode = that.graph.select(
                '[data-nodeid="{{id}}"][data-adqtype="ADQUISITOR"]'.replace('{{id}}', that.adquiring.ID)
            );

            return adq_graphnode.select('path');
        }
        this.start = function () {
            if (that.blinking == false)
            {
                that._addTitle();

                that._blink();
            }
        };
        this._removeTitle = function ()
        {
            var adq_graphnode_path = that._getSvgObj();

            if (adq_graphnode_path)
                adq_graphnode_path.select('title').remove();
        }
        this.stop = function () {
            that.stopping = true;
        };
        this._stopBlink = function () {
            var adq_graphnode_path = that._getSvgObj();

            if (adq_graphnode_path)
            {
                adq_graphnode_path
                    .transition()
                    .duration(1000)
                    .style(
                        'fill',
                        function (d) {
                            return (that.adquiring.IsConnected == true)
                                        ? '#' + d.color.replace('#', '')
                                        : d3_helpers.COLOR_DISCONNECTED;
                        }
                    );
            }
        };
        this.update = function (newAdquiring) {
            that.adquiring = newAdquiring;

            that._removeTitle();
            that._addTitle();
        };
    };
    dotBlinker.i18n = null;

    var d3_helpers = {
        addClass: function (node, classname) {
            if (!node[0][0]) return;
            var classes = classname.split(" ");
            classes = _.invoke(classes, "trim");
            var currentClass = node.attr("class");
            if (_.isString(currentClass)) {
                currentClass = currentClass.trim(); 
                currentClass = currentClass.split(" ");
                currentClass = _.invoke(currentClass, "trim");
                _.each(classes, function (i) { (_.indexOf(currentClass, i) == -1) ? currentClass.push(i) : ""; });
                node.attr("class", currentClass.join(" "));
            } else {
                node.attr("class", "");
                d3_helpers.addClass(node, classname); 
            }
        },
        removeClass: function (node, classname) {
            var classes = classname.split(" ");
            classes = _.invoke(classes, "trim");
            var currentClass = node.attr("class");
            if (_.isString(currentClass)) {
                currentClass = currentClass.trim();
                currentClass = currentClass.split(" ");
                currentClass = _.invoke(currentClass, "trim"); 
                _.each(classes, function (i) { currentClass = _.without(currentClass, i); });
                node.attr("class", currentClass.join(" "));
            }
        },
        hasClass: function (node, classname) {
            var classes = classname.split(" ");
            classes = _.invoke(classes, "trim");
            var currentClass = node.attr("class");
            if (_.isString(currentClass)) {
                currentClass = currentClass.trim();
                currentClass = currentClass.split(" ");
                currentClass = _.invoke(currentClass, "trim");
                return _.every(classes, function (i) { return _.indexOf(currentClass, i) != -1 }); 
            } else return false; 
        },
        COLOR_DISCONNECTED: "#D6D6D6"
    }; 

    //replace all "NetworkOverview" with your view's name.
    var NetworkOverview = { Model: {}, Views: {} }

    NetworkOverview.Model = Backbone.Model.extend({
        defaults: {
            procedure: '',
            graphData: null,
            graphAgentId: null,
            graphAdquisitorId: null,
            showOnlyConnected: false, 
        }
    });

    //the generate id method is called everytime a view is going to be shown by the router and returns and id that
    //is used by the router to store in cache (if it is cacheable) and to know the current shown view.
    //this is useful in case your view is reusable, and displays different data depending on url parameters 
    //(such as a catalog view, or a report that doesnt change in terms of UI but it does change in terms of data)
    //so you can always use the same view on the router but the cache can tell which view is which by using differents ids.
    NetworkOverview.generateID = function (viewParams) {
        try {
            //if the viewparams change the view id, then evaluate the viewparams here
            //and return the appropiate id.
            return "network-overview"; 
        } catch (Error) { }
    }

    NetworkOverview.Views.Main = Backbone.View.extend({
        template: "network-overview"
        , id: "network-overview"
        , title: "Esquema de Red"
        //default not cacheable, change this if you want the view to be cacheable
        // if the view is set as cacheable should also have a refresh method to reset the view without erasing the DOM.
        , isCacheable: false
        , initialize: function () {
            this.options.state = app.view_states.loading;
            this.options.onappend = (_.isFunction(this.options.onappend)) ? this.options.onappend : function () { };

            if (this.options.viewParams) {
            }

            var model = new NetworkOverview.Model({
                procedure: "dbo.procedureName"
            });

            this.options.MYREFERENCES = {
                autoRefresh: {
                    enabled: true
                    , toid: null
                },
                graph: {
                    svg: null,
                    zoom: null,
                    width: null,
                    height: null, 
                }
            };

            this.model = model;

            _.bindAll(this); 

            this.bindEvents();
        },

        blinkingObjs: {
            dots: {},
        },

        events: {
            "click .btn-zoom-in": "zoomIn",
            "click .btn-zoom-out": "zoomOut",
            "click .btn-restore-zoom-pan": "restoreZoomPan",
            "click .btn-go-to-trending-view": "goToTrendingView",
            "click .btn-show-only-connected": "showOnlyConnected", 
        }, 
        
        zoomIn: function () {
            this.doZoom(1); 
        },
        zoomOut: function () {
            this.doZoom(-1);
        },
        restoreZoomPan: function () {
            this.interpolateZoom([0, 0], 1); 
        },
        goToTrendingView: function () {
            var graph = d3.select(".graph-container svg");
            var adq_graphnode = graph.select("[data-nodeid='{{id}}'][data-adqtype='ADQUISITOR']".replace("{{id}}", this.model.get("graphAdquisitorId")));
            var name = adq_graphnode.attr("data-name");
            var internalName = adq_graphnode.attr("data-internalname");

            var dotView = (name == internalName) ? "DOT " + internalName : name; 
            app.router.navigate("!/trending/custom/view=" + dotView, { trigger: true });
        },
        showOnlyConnected: function () {
            var btn = $('.btn-show-only-connected');
            btn.toggleClass('active');

            this.model.set('showOnlyConnected', btn.hasClass('active'));

            if (this.refreshVariableListTOUT != null)
                clearTimeout(this.refreshVariableListTOUT);

            this.$el.find('#variablesListHolder').empty();

            this._autoRefresh();
        }, 
        doZoom: function (direction) {
            var factor = 0.2,
            target_zoom = 1,
            center = [this.options.MYREFERENCES.graph.width / 2, this.options.MYREFERENCES.graph.height / 2],
            extent = this.options.MYREFERENCES.graph.zoom.scaleExtent(),
            translate = this.options.MYREFERENCES.graph.zoom.translate(),
            translate0 = [],
            l = [],
            view = { x: translate[0], y: translate[1], k: this.options.MYREFERENCES.graph.zoom.scale() };

            direction = (_.isNumber(direction)) ? direction : 1;
            target_zoom = this.options.MYREFERENCES.graph.zoom.scale() * (1 + factor * direction);

            if (target_zoom < extent[0] || target_zoom > extent[1]) { return false; }

            translate0 = [(center[0] - view.x) / view.k, (center[1] - view.y) / view.k];
            view.k = target_zoom;
            l = [translate0[0] * view.k + view.x, translate0[1] * view.k + view.y];

            view.x += center[0] - l[0];
            view.y += center[1] - l[1];

            this.interpolateZoom([view.x, view.y], view.k);
        }, 

        interpolateZoom: function (translate, scale) {
            var that = this;
            return d3.transition().duration(350).tween("zoom", function () {
                var iTranslate = d3.interpolate(that.options.MYREFERENCES.graph.zoom.translate(), translate),
                    iScale = d3.interpolate(that.options.MYREFERENCES.graph.zoom.scale(), scale);
                return function (t) {
                    that.options.MYREFERENCES.graph.zoom
                        .scale(iScale(t))
                        .translate(iTranslate(t));
                    that.zoom();
                };
            });
        }, 

        zoom: function (animate) {
            this.options.MYREFERENCES.graph.svg.attr("transform",
                "translate(" + this.options.MYREFERENCES.graph.zoom.translate() + ")" +
                "scale(" + this.options.MYREFERENCES.graph.zoom.scale() + ")"
            );
        }, 

        render: function (container) {
            var that = this;
            var thatContainer = (container != null && container != undefined) ? container : this.options.container;

            _.extend(this.options.MYREFERENCES, {
                subviews: {}
            }); 

            //the screens have a custompath, so it has to be specified in the customPath variable that is
            //then sent to the template loader.
            var customPath = "/app/custom-screens/IHConfiguration/network-overview-d3js/";

            T.render.call(this, this.template, function (tmp) {

                //loading the view and appending it to the views's $el.
                that.$el.html(tmp());

                var vl = that.options.MYREFERENCES.subviews.variablesList = new NetworkOverview.Views.VariablesList({
                    container: that.$("#variablesListHolder")
                });

                vl.render();

                that.$el.find(".graph-container").height($(window).height() - 100); 

                that.append(thatContainer, that.$el);

                that.$el.find('.toolbar-btn').tooltip({
                    placement: 'right',
                    container: 'body', 
                });

                //that.refreshGraphData(); 
                that.bindViewScopedEvents();

            }, customPath, "main_template");
        }

        , append: function (container, el) {
            el = (el != null && el != undefined) ? el : this.$el;

            if (this.options.state == app.view_states.loading
                || this.options.state == app.view_states.shown) {
                //appending view to the main container and set state to shown

                this.options.state = app.view_states.shown;
                container.append(el);

                this.options.onappend(this);
            }

            if (this.options.state == app.view_states.hidden) {
                //append and remain hidden
                container.append(el);
            }

            if (this.options.state == app.view_states.closed) {
                //return without appending.
                return;
            }
        }

        , refreshGraphData: function () {
            try {
                var that = this;
                var qp = new QueryParameters(); 
                qp.Add("ShowOnlyConnected", "BIT", this.model.get('showOnlyConnected'));

                Core.Json.CallProcedure(
                    app.DatabaseNames.IH + ".WEB.GetVariablesValues",
                    qp,
                    {
                        onSuccess: function (data) {
                            data = that.parseData(data); 
                            var diff_action = that.checkDifferences(data);
                            if (!that.model.get("graphData")) diff_action = { action: "redraw" };

                            that.model.set("graphData", data);

                            if (diff_action.action == "redraw") {
                                that.drawGraph(that.parseGraphNodes(that.model.get("graphData")));
                                that.refreshGraph(data);
                            }
                            else if (diff_action.action == "refresh") {
                                that.refreshGraph(data);
                            }
                        },
                        onFailure: function (error) { },
                        Language: app.language,
                    }
                    , app.ConnectionStrings.app
                );
            } catch (Error) {
            }
        }

        , checkDifferences: function (data, oldData) {
            try{
                if (!oldData) oldData = this.model.get("graphData");
                var agentList = data.AgentsList; 
                var prevAgentList = (oldData && oldData.AgentsList) ? oldData.AgentsList : []; 

                if (agentList.length != prevAgentList.length) {
                    return { action: "redraw" };
                } else {
                    var agentList_uniquestr = _.reduce(_.sortBy(agentList, function (obj) { return obj.ID; }), function (mem, obj) { return mem += "," + obj.ID + ":" + _.reduce(obj.AdquiringsList, function (mem, obj) { return mem += "," + obj.ID; }, ""); }, "");
                    var prev_agentList_uniquestr = _.reduce(_.sortBy(prevAgentList, function (obj) { return obj.ID; }), function (mem, obj) { return mem += "," + obj.ID + ":" + _.reduce(obj.AdquiringsList, function (mem, obj) { return mem += "," + obj.ID; }, ""); }, "");
                    if (agentList_uniquestr != prev_agentList_uniquestr) {
                        return { action: "redraw" };
                    } else {
                        return { action: "refresh" }; 
                    }
                }
            } catch (Error) {
                return { action: "redraw" }; 
            }
        }

        , parseData: function (sourceData) {
            var data = { AgentsList: [] };
            try {
                if (sourceData) {
                    if ((sourceData.Table) && (sourceData.Table.length > 0)) {
                        var currentAdquiringData
                            , currentAgentData
                            , variableData = sourceData.Table[0];

                        currentAgentData = { AdquiringsList: [], ID: variableData.AgentID, Name: variableData.AgentName, InternalName: variableData.AgentInternalName, Type: variableData.AgentType, TypeID: variableData.AgentTypeID, VariablesActiveCount: 0, VariablesCount: 0, VariablesList: [] };
                        currentAdquiringData = { AgentID: variableData.AgentID, ID: variableData.AdquiringID, Name: variableData.AdquiringName, InternalName: variableData.AdquiringInternalName, Type: variableData.AdquiringType, VariablesActiveCount: 0, VariablesCount: 0, VariablesList: [], Status: null, StatusName: '' };

                        var alreadyAddedAgent = false;
                        var alreadyAddedAdquiring = false;

                        for (var variableIndex = 0; variableIndex < sourceData.Table.length; variableIndex++) {
                            variableData = sourceData.Table[variableIndex];

                            if (currentAgentData.ID == variableData.AgentID) {
                                if (variableData.AdquiringID == null) {
                                    currentAgentData.VariablesList.push(variableData);
                                } else if (currentAdquiringData.ID == variableData.AdquiringID) {
                                    currentAdquiringData.VariablesList.push(variableData);
                                }
                                else {
                                    if (currentAdquiringData.ID != null && !alreadyAddedAdquiring)
                                        currentAgentData.AdquiringsList.push(currentAdquiringData);

                                    currentAdquiringData = { AgentID: variableData.AgentID, ID: variableData.AdquiringID, Name: variableData.AdquiringName, InternalName: variableData.AdquiringInternalName, Type: variableData.AdquiringType, VariablesActiveCount: 0, VariablesCount: 0, VariablesList: [] };
                                    currentAdquiringData.VariablesList.push(variableData);
                                }
                            }
                            else {

                                if (currentAdquiringData.ID != null && !alreadyAddedAdquiring)
                                    currentAgentData.AdquiringsList.push(currentAdquiringData);

                                if (!alreadyAddedAgent)
                                    data.AgentsList.push(currentAgentData);

                                alreadyAddedAgent = alreadyAddedAdquiring = false; 
                                for (var ag in data.AgentsList) {
                                    if (data.AgentsList[ag].ID == variableData.AgentID) {
                                        currentAgentData = data.AgentsList[ag];
                                        alreadyAddedAgent = true; 
                                        for (var ad in currentAgentData.AdquiringsList) {
                                            if (currentAgentData.AdquiringsList[ad].ID == variableData.AdquiringID) {
                                                currentAdquiringData = currentAgentData.AdquiringsList[ad];
                                                alreadyAddedAdquiring = true; 
                                            }
                                        }
                                    }
                                }

                                if (!alreadyAddedAgent)
                                    currentAgentData = { AdquiringsList: [], ID: variableData.AgentID, Name: variableData.AgentName, InternalName: variableData.AgentInternalName, Type: variableData.AgentType, TypeID: variableData.AgentTypeID, VariablesActiveCount: 0, VariablesCount: 0, VariablesList: [] };
                                if (!alreadyAddedAdquiring)
                                    currentAdquiringData = { AgentID: variableData.AgentID, ID: variableData.AdquiringID, Name: variableData.AdquiringName, InternalName: variableData.AdquiringInternalName, Type: variableData.AdquiringType, VariablesActiveCount: 0, VariablesCount: 0, VariablesList: [], Status: null, StatusName: '' };

                                if (variableData.AdquiringID == null) {
                                    currentAgentData.VariablesList.push(variableData);
                                } else {
                                    currentAdquiringData.VariablesList.push(variableData);
                                }
                            }
                        }

                        if (currentAdquiringData.ID != null && !alreadyAddedAdquiring)
                            currentAgentData.AdquiringsList.push(currentAdquiringData);

                        if (!alreadyAddedAgent)
                            data.AgentsList.push(currentAgentData);
                    }

                    if ((sourceData.Table1) && (sourceData.Table1.length > 0)) {
                        var adquiringData
                            , agentData
                            , agentFound
                            , currentAdquiringData
                            , currentAgentData;

                        for (var agentIndex = 0; agentIndex < sourceData.Table1.length; agentIndex++) {
                            agentData = sourceData.Table1[agentIndex];
                            agentFound = false;

                            for (var currentAgentIndex = 0; currentAgentIndex < data.AgentsList.length; currentAgentIndex++) {
                                if (data.AgentsList[currentAgentIndex].ID == agentData.AgentID) {
                                    agentFound = true;

                                    currentAgentData = data.AgentsList[currentAgentIndex];
                                    currentAgentData.IsConnected = agentData.IsConnected;
                                    currentAgentData.Latency = agentData.Latency;
                                    currentAgentData.VariablesActiveCount = agentData.VariablesActiveCount;
                                    currentAgentData.VariablesCount = agentData.VariablesCount;
                                    currentAgentData.InternalName = agentData.AgentInternalName; 

                                    break;
                                }
                            }

                            if (agentFound == false) {
                                data.AgentsList.push({
                                    AdquiringsList: []
                                    , ID: agentData.AgentID
                                    , Latency: agentData.Latency
                                    , Name: agentData.AgentName
                                    , InternalName: agentData.AgentInternalName
                                    , Type: agentData.AgentTypeName
                                    , TypeID: agentData.AgentTypeID
                                    , VariablesActiveCount: 0
                                    , VariablesCount: 0
                                });
                            }
                        }
                    }

                    if ((sourceData.Table2) && (sourceData.Table2.length > 0)) {
                        var adquiringData
                            , currentAdquiringData
                            , currentAdquiringIndex

                        for (var adquiringIndex = 0; adquiringIndex < sourceData.Table2.length; adquiringIndex++) {
                            adquiringData = sourceData.Table2[adquiringIndex];

                            for (var agentIndex = 0; agentIndex < data.AgentsList.length; agentIndex++) {
                                var agentAdquiringList = data.AgentsList[agentIndex].AdquiringsList;

                                for (var i = 0; i < agentAdquiringList.length; i++) {
                                    var currentAdquiringData = agentAdquiringList[i];

                                    if (currentAdquiringData.ID == adquiringData.AdquiringID) {
                                        currentAdquiringData.IsConnected = adquiringData.IsConnected;
                                        currentAdquiringData.VariablesActiveCount = adquiringData.VariablesActiveCount;
                                        currentAdquiringData.VariablesCount = adquiringData.VariablesCount;
                                        currentAdquiringData.Status = adquiringData.Status;
                                        currentAdquiringData.StatusName = adquiringData.StatusName;
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            catch (Error) { }
            return data;
        }
        , parseGraphNodes: function (data) {

            var graph = {
                nodes: [
                    { "atom": "IHBOX", "size": 50, "type": "square", displayName: false, appender: "image", href: app.foldersRoot + "/app/custom-screens/IHConfiguration/network-overview-d3js/img/data.png" },
                ],
                links: [],
            };

            var atom_props = {
                "DASH AGENT": { atom: "DASH AGENT", size: 45, type: "square", color: "9AC23E" },
                "DOT": { atom: "DOT", size: 10, type: "circle", color: "FF7F0E" },
                "OPC AGENT": { atom: "OPC AGENT", size: 45, type: "square", color: "6C8DCA" },
                "OPC": { atom: "OPC", size: 30, type: "triangle-up", color: "F7912D" },
                "PLC AGENT": { atom: "PLC AGENT", size: 45, type: "square", color: "6C8DCA" },
                "PLC": { atom: "PLC", size: 30, type: "diamond", color: "00FFFF" },
            }; 

            if (data && data.AgentsList) {
                var agentsList = data.AgentsList;
                for (var i = 0; i < agentsList.length; i++) {
                    var agent = agentsList[i];
                    agent.Type = agent.Type.toUpperCase() + " AGENT";
                    var node = (atom_props[agent.Type]) ? _.clone(atom_props[agent.Type]) : { atom: "unknown", size: 30, type: "square", color: "800000" };
                    node["name"] = agent.Name;
                    node["graphid"] = graph.nodes.length;
                    node["id"] = agent.ID;
                    node["adquiringType"] = "AGENT"; 
                    graph.nodes.push(node);
                    graph.links.push({ source: 0, target: node.graphid, bond: 1 });

                    for (var adqInx = 0; adqInx < agent.AdquiringsList.length; adqInx++) {
                        var adquisitor = agent.AdquiringsList[adqInx];
                        var childNode = (atom_props[adquisitor.Type]) ? _.clone(atom_props[adquisitor.Type]) : { atom: "unknown" };
                        childNode["name"] = adquisitor.Name;
                        childNode["graphid"] = graph.nodes.length;
                        childNode["id"] = adquisitor.ID;
                        childNode["parentId"] = agent.ID; 
                        childNode["internalName"] = adquisitor.InternalName; 
                        childNode["adquiringType"] = "ADQUISITOR";
                        graph.nodes.push(childNode);
                        graph.links.push({ source: node.graphid, target: childNode.graphid, bond: 1 });
                    }
                }
            }

            return graph; 

        }

        , graph_components: { width: null, height: null, svg: null, force: null, nodes: null, links: null, node: null, link: null }
        , drawGraph: function (graph) {

            this.$el.find(".graph-container").empty();

            var that = this;
            var width, height; 
            this.options.MYREFERENCES.graph.width = width = this.$el.find(".graph-container").width(); 
            this.options.MYREFERENCES.graph.height = height = $(window).height() - 100;

            var color = d3.scale.category20();

            //console.log("color DASH: " + color("DASH"));
            //console.log("color DOT: " + color("DOT"));
            //console.log("color AGENT: " + color("AGENT"));
            //console.log("color OPC: " + color("OPC"));

            var radius = d3.scale.sqrt()
                .range([0, 6]);

            var svg, zoom; 
            this.options.MYREFERENCES.graph.zoom = zoom = d3.behavior.zoom().scaleExtent([0.3, 2]).on("zoom", this.zoom);

            this.options.MYREFERENCES.graph.svg = svg = d3.select(".graph-container").append("svg")
                .attr("width", width)
                .attr("height", height)
                .append("g")
                .call(zoom)
                .append("g");

            //going back to auto.
            this.$el.find(".graph-container").height("auto");

            svg.append("rect")
            .attr("class", "overlay")
            .attr("width", width)
            .attr("height", height);

            var force = d3.layout.force()
                .size([width, height])
                .charge(-1000)
                .linkDistance(function (d) { return radius(d.source.size) + radius(d.target.size) + 50; });

            // function for handling zoom event
            function zoomHandler() {
                svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
            }
            
            force
            .nodes(graph.nodes)
            .links(graph.links)
            .on("tick", tick)
            .start();

            var link = svg.selectAll(".link")
                .data(graph.links)
                .enter().append("g")
                .attr("class", "link")
                .attr("data-link-nodeid", function (d) { return d.target.id; })
                .attr("data-link-adqtype", function (d) { return d.target.adquiringType; });

            link
            .append("line")
            .style("stroke-width", "2px")
            .style("stroke", "#ccc"); 
            
            //.style("stroke-width", function (d) { return (d.bond * 2 - 1) * 2 + "px"; });

            link.filter(function (d) { return d.bond > 1; }).append("line")
                .attr("class", "separator");

            var node = svg.selectAll(".node")
                .data(graph.nodes)
                .enter().append("g")
                .attr("class", "node")
                .attr("data-nodeid", function (d) { return d.id; })
                .attr("data-adqtype", function (d) { return d.adquiringType; })
                .attr("data-name", function (d) {
                    return d.name;
                })
                .attr("data-internalname", function (d) {
                    return d.internalName;
                })
                .call(force.drag)
                .on("mousedown", function (e) { $.Event(d3.event).stopPropagation(); })
                .on("click", function (e) {
                    that.nodeClick(e, this); 
                });

            //node.each(function (d) {
            //    d.shape = (d.shape) ? d.shape : "circle"; 
            //    d3.select(d).append(d.shape)
            //    .attr("r", function (d) { return radius(d.size); })
            //    .style("fill", function (d) { return color(d.atom); });
            //}); 

            node.filter(function (d) { return (!d.appender || d.appender == "path"); }).append("path")
                .attr("width", 10)
                .attr("height", 10)
                .style("fill", function (d) { return d3_helpers.COLOR_DISCONNECTED; })
                .attr("d", d3.svg.symbol()
                    .size(function (d) { return d.size * 100; })
                    .type(function (d) { return (d.type) ? d.type : "circle"; }));

            node.filter(function (d) { return (d.appender && d.appender == "image"); })
            .append("svg:image")
            .attr("class", "circle")
            .attr("xlink:href", function (d) { return d.href; })
            .attr("x", -70)
            .attr("y", -55)
            .attr("width", "141px")
            .attr("height", "106px");

            node.filter(function (d) { return (!(d.displayName != null && d.displayName != undefined) || d.displayName == true) })
                .append("text")
                .attr("dy", function (d) {
                    switch (d.type) {
                        case "square":
                        case "circle":
                            return ".35em";
                            break;
                        case "triangle-up":
                            return "1.3em"; 
                            break; 
                        default:
                            return ".35em";
                            break; 
                    }
                })
                .attr("text-anchor", "middle")
                .text(function (d) { return d.atom; });

            node.append("text")
            .attr("dy", ".35em")
            .style("font-size", "10px")
            .attr("text-anchor", "middle")
            .attr("y", function (d) {
                switch (d.type) {
                    case "square":
                        return 40;
                    case "circle":
                        return 30;
                        break;
                    case "triangle-up":
                        return 48;
                        break;
                    default:
                        return 30;
                        break;
                }
            })
            //.text(function (d) { return (d.name) ? d.name : ""; });
            .attr("data-text-nodeid", function (d) { return d.id; });

            node.append("text")
            .attr("dy", ".35em")
            .style("font-size", "10px")
            .attr("text-anchor", "middle")
            .attr("y", function (d) {
                switch (d.type) {
                    case "square":
                        return 40 + 15;
                    case "circle":
                        return 30 + 15;
                        break;
                    case "triangle-up":
                        return 48 + 15;
                        break;
                    default:
                        return 30 + 15;
                        break;
                }
            })
            //.text(function (d) { return (d.name) ? d.name : ""; });
            .attr("data-subtext-nodeid", function (d) { return d.id; });

            function tick() {
                link.selectAll("line")
                    .attr("x1", function (d) { return d.source.x; })
                    .attr("y1", function (d) { return d.source.y; })
                    .attr("x2", function (d) { return d.target.x; })
                    .attr("y2", function (d) { return d.target.y; });

                node.attr("transform", function (d) { return "translate(" + d.x + "," + d.y + ")"; });
            }
        }
        , refreshGraph: function (data) {
            var graph = d3.select(".graph-container svg");
            var agentsList = data.AgentsList;
            var newBlinkingObj = { dots: {}, };

            for (var ag = 0, len = agentsList.length; ag < len; ag++) {
                var agent = agentsList[ag];
                var graphnode = graph.select("[data-nodeid='" + agent.ID + "'][data-adqtype='AGENT']");
                var graphnode_link = graph.select("[data-link-nodeid='" + agent.ID + "'][data-link-adqtype='AGENT']").select("line");
                if (!graphnode) break;
                var graphnode_path = graphnode.select("path");

                if (agent.IsConnected) {
                    if (!d3_helpers.hasClass(graphnode_path, "connected")) {
                        graphnode_path.transition().delay(0).duration(150)
                        .attr("d", d3.svg.symbol()
                        .size(function (d) { return d.size * 100 * 2; })
                        .type(function (d) { return (d.type) ? d.type : "circle"; }))
                        .transition().delay(150).duration(100).attr("d", d3.svg.symbol()
                        .size(function (d) { return d.size * 100; })
                        .type(function (d) { return (d.type) ? d.type : "circle"; }));
                    }
                    d3_helpers.addClass(graphnode_path, "connected");
                    graphnode_path.style("fill", function (d) {
                        //return d3.scale.category20()(d.atom)
                        return ("#" + d.color.replace("#", ""));
                    });
                    graphnode_path.style("opacity", 1);
                    graphnode_link.style("opacity", 1);
                } else {
                    graphnode_path.style("fill", function (d) { return d3_helpers.COLOR_DISCONNECTED; });
                    graphnode_path.style("opacity", 0.5);
                    graphnode_link.style("opacity", 0.5);
                    d3_helpers.removeClass(graphnode_path, "connected");
                }

                var gn_text = graphnode.select(("[data-text-nodeid='{{id}}']").replace("{{id}}", agent.ID));
                gn_text.text(agent.Name);

                var gn_subtext = graphnode.select(("[data-subtext-nodeid='{{id}}']").replace("{{id}}", agent.ID));
                gn_subtext.text("Latency: "
                                + (((agent.Latency != undefined) && (agent.Latency != null) && (agent.Latency >= 0))
                                    ? (agent.Latency / 1000).toString().substring(0, 5) + " sec"
                                    : "-"));

                for (var adq = 0, lenadq = agent.AdquiringsList.length; adq < lenadq; adq++) {
                    var adquiring = agent.AdquiringsList[adq];
                    var adq_graphnode = graph.select("[data-nodeid='{{id}}'][data-adqtype='ADQUISITOR']".replace("{{id}}", adquiring.ID));
                    var adq_graphnode_link = graph.select("[data-link-nodeid='{{id}}'][data-link-adqtype='ADQUISITOR']".replace("{{id}}", adquiring.ID)).select("line");
                    var adq_graphnode_path = adq_graphnode.select("path");

                    if (adquiring.IsConnected) {
                        if (!d3_helpers.hasClass(adq_graphnode_path, "connected")) {
                            adq_graphnode_path.transition().delay(0).duration(150)
                            .attr("d", d3.svg.symbol()
                            .size(function (d) { return d.size * 100 * 2; })
                            .type(function (d) { return (d.type) ? d.type : "circle"; }))
                            .transition().delay(150).duration(100).attr("d", d3.svg.symbol()
                            .size(function (d) { return d.size * 100; })
                            .type(function (d) { return (d.type) ? d.type : "circle"; }));
                        }
                        d3_helpers.addClass(adq_graphnode_path, "connected");
                        adq_graphnode_path.style("fill", function (d) {
                            //return d3.scale.category20()(d.atom)
                            return ("#" + d.color.replace("#", ""));
                        });
                        adq_graphnode_path.style("opacity", 1);
                        adq_graphnode_link.style("opacity", 1);
                    } else {
                        adq_graphnode_path.style("fill", function (d) { return d3_helpers.COLOR_DISCONNECTED; });
                        adq_graphnode_path.style("opacity", 0.5);
                        adq_graphnode_link.style("opacity", 0.5);

                        d3_helpers.removeClass(adq_graphnode_path, "connected");
                    }


                    if ((adquiring.Status != 0) && (adquiring.Status != null))
                    {
                        var dotBlinkerObj = this.blinkingObjs.dots[adquiring.ID];

                        if (!dotBlinkerObj) {
                            dotBlinkerObj = new dotBlinker(graph, adquiring);

                            dotBlinkerObj.start();
                        }

                        dotBlinkerObj.update(adquiring);

                        newBlinkingObj.dots[adquiring.ID] = dotBlinkerObj;
                    }



                    var adq_gn_text = adq_graphnode.select(("[data-text-nodeid='{{id}}']").replace("{{id}}", adquiring.ID));
                    adq_gn_text.text(adquiring.Name);

                    var adq_gn_subtext = adq_graphnode.select(("[data-subtext-nodeid='{{id}}']").replace("{{id}}", adquiring.ID));
                    adq_gn_subtext.text("# Tags: "
                                        + adquiring.VariablesActiveCount.toString()
                                        + "/" + adquiring.VariablesCount.toString());
                }
            }

            //Dispose old blinkers
            for (var blinkName in this.blinkingObjs.dots) {
                var blinker = newBlinkingObj.dots[blinkName];

                if (!blinker)
                    this.blinkingObjs.dots[blinkName].destroy();
            }

            this.blinkingObjs = newBlinkingObj;
        }

        , refreshVariableListTOUT: null
        , nodeClick: function (e, d3jsref) {
            try {
                d3.select(".graph-container").select(".selected").each(function (i, elem) {
                    d3_helpers.removeClass(d3.select(this), "selected"); 
                }); 
                var graphelem = d3.select(d3jsref);
                d3_helpers.addClass(graphelem.select("path"), "selected"); 

                var that = this; 
                var elem = e; 

                var go_to_trending_view = this.$el.find(".btn-go-to-trending-view");
                if (elem["adquiringType"] == "ADQUISITOR") go_to_trending_view.attr("disabled", false);
                else go_to_trending_view.attr("disabled", true);

                this.model.set({
                    graphAgentId: ((elem["adquiringType"] == "AGENT") ? elem.id : elem.parentId),
                    graphAdquisitorId: ((elem["adquiringType"] == "ADQUISITOR") ? elem.id : null)
                }); 

                this.refreshVariablesList(((elem["adquiringType"] == "AGENT") ? elem.id : elem.parentId), ((elem["adquiringType"] == "ADQUISITOR") ? elem.id : null));
            } catch (Error) { }
        }
        , refreshVariablesList: function (agentid, adquisitorid) {
            var that = this; 
            var autorefresh = true; 
            var qp = new QueryParameters();

            qp.Add("AgentID", "INT", agentid);
            qp.Add("AdquiringID", "INT", adquisitorid);
            qp.Add("ShowOnlyConnected", "BIT", this.model.get('showOnlyConnected'));

            if (this.refreshVariableListTOUT) { clearTimeout(this.refreshVariableListTOUT); this.refreshVariableListTOUT = null; }

            Core.Json.CallProcedure(
                app.DatabaseNames.IH + ".WEB.GetVariablesValues",
                qp,
                {
                    onSuccess: function (data) {
                        that.options.MYREFERENCES.subviews.variablesList.render(that.parseData(data));

                        if (autorefresh) {
                            that.refreshVariableListTOUT = setTimeout(function () { that.refreshVariablesList(agentid, adquisitorid); }, 5000);
                        }
                    },
                    onFailure: function (error) { },
                },
                app.ConnectionStrings.app
            );
        }
        , refresh: function () {
            try {
            } catch (Error) { }
        }

        , bindEvents: function () {
            //this function should be in every view that uses listenTo anywhere
            //all the model bindings or view-model binding should be here, to manage
            //the show/hide view easily
        }

        , bindViewScopedEvents: function () {
            this._startAutoRefresh(); 
        }

        , unbindViewScopedEvents: function () {
            this._stopAutoRefresh(); 
        }

        , _startAutoRefresh: function () {
            try {
                if (this.options.MYREFERENCES.autoRefresh.toid != null) {
                    clearTimeout(this.options.MYREFERENCES.autoRefresh.toid);
                    this.options.MYREFERENCES.autoRefresh.toid = null;
                }
                this.options.MYREFERENCES.autoRefresh.enabled = true;

                this._autoRefresh();
            } catch (Error) { }
        }

        , _autoRefresh: function () {
            if (this.options.MYREFERENCES.autoRefresh.toid != null) {
                clearTimeout(this.options.MYREFERENCES.autoRefresh.toid);
                this.options.MYREFERENCES.autoRefresh.toid = null;
            }

            this.refreshGraphData();

            if (this.options.MYREFERENCES.autoRefresh.enabled == true) {
                this.options.MYREFERENCES.autoRefresh.toid = setTimeout(this._autoRefresh, 10000);
            }
        }

        , _stopAutoRefresh: function () {
            if (this.options.MYREFERENCES.autoRefresh.toid != null) {
                clearTimeout(this.options.MYREFERENCES.autoRefresh.toid);
                this.options.MYREFERENCES.autoRefresh.toid = null;
            }
            this.options.MYREFERENCES.autoRefresh.enabled = false;
        }

        , close: function () {
            this.options.state = app.view_states.closed;

            this.remove();
            this.unbind();
            this.unbindViewScopedEvents(); 
        }

        , show: function () {
            this.options.state = app.view_states.shown;

            this.bindEvents();
            this.bindViewScopedEvents(); 
            this.$el.show();
        }

        , hide: function () {
            this.options.state = app.view_states.hidden;

            this.$el.hide();
            this.unbind();
            this.unbindViewScopedEvents(); 
            this.stopListening();
        }

        , preRender: function () {
            app.models.subnavbar.set("dateControl", false);
            app.models.subnavbar.set("subnavbar", false); 
        }

        , reRender: function () {
        }
    });

    //subview for the subnavbar controls
    NetworkOverview.Views.VariablesList = Backbone.View.extend({
        id: "variables-list"
        , title: ""
        , template: "network-overview"
        , initialize: function () {
            if (this.options.viewParams) {
            }

            this.options.MYREFERENCES = {};

            this.bindEvents();
            _.bindAll(this);
        },

        events: {
        },

        render: function (variablesData) {
            var that = this;
            var thatContainer = this.options.container;
            var data = (variablesData) ? variablesData : null;
            //the screens have a custompath, so it has to be specified in the customPath variable that is
            //then sent to the template loader.
            var customPath = "/app/custom-screens/IHConfiguration/network-overview-d3js/";

            T.render.call(this, this.template, function (tmp) {

                that.options.MYREFERENCES.tmp = tmp;

                //the ul will dissapear cause of the ::empty selector
                thatContainer.empty();

                //if has data then show it again (empty selector does the logic); 
                if (data) {
                    if (data.AgentsList) {
                        for (var i in data.AgentsList) {
                            var ag = data.AgentsList[i];
                            //CHECK, variableList sometimes comes as undefined when the agent has no tags, this should be fixed
                            ag.VariablesList = (_.isArray(ag.VariablesList)) ? ag.VariablesList.slice(0, 50) : [];
                            for (var e in ag.AdquiringsList) {
                                var adq = ag.AdquiringsList[e];
                                adq.VariablesList = (_.isArray(adq.VariablesList)) ? adq.VariablesList.slice(0, 50) : [];
                            }
                        }
                    }

                    that.$el.html(tmp(data));
                    thatContainer.scrollTop(0);
                    thatContainer.append(that.$el);
                }

            }, customPath, "variables_list");

        }

        , refresh: function (data) {
            try {
                if (this.options.MYREFERENCES.tmp && data) {
                    var tmp = this.options.MYREFERENCES.tmp;
                    this.$el.html(tmp(data));
                }
            } catch (Error) { }
        }

        , reset: function () {
            this.$el.empty();

            //the ul will dissapear cause of the ::empty selector
            this.options.container.empty();
        }

        , bindEvents: function () {
            //this function should be in every view that uses listenTo anywhere
            //all the model bindings or view-model binding should be here, to manage
            //the show/hide view easily
        }

        , close: function () {
            this.remove();
            this.unbind();
        }

        , show: function () {
            this.bindEvents();
            this.$el.show();
        }

        , hide: function () {
            this.$el.hide();
            this.unbind();
            this.stopListening();
        }

        , reRender: function () {
        }
    });

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

});
