/// <reference path="http://localhost/IndustrialDashboard/Scripts/IndustrialDashboard.js" />
define([
    // Application variable, always include it to have access to app methods.
    "app",

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

    "js/ih-runtime-lib",
    "custom-screens/screen-builder/ih-lib",
    "highcharts",
    "moment",

    "underscore", 
], function (app, T, Modal, IHRuntime, DataAPI, Highcharts, moment) {

    var Environment = { Class: null, Enums: {}, Viewer: {} };
    var Viewer = Environment.Viewer = { Models: {}, Views: {}, Collections: {} };

    Environment.Enums.Modes = {
        Editor: 'EDITOR',
        Viewer: 'VIEWER'
    }; 

    var env = Environment.Class = function env(opt) {
        try {
            this.options = {
                frame: null,
                screenId: null,
                tagViewers: null,
                chartViewers: null,
                mode: Environment.Enums.Modes.Live
            }; 

            this.options = _.extend(this.options, _.isObject(opt) ? opt : {});
        } catch (error) { console.log("Error: error while trying to instance Environment class. Error: " + error.toString()); }
    }; 

    env.prototype.injectEnvironmentToFrame = function () {
        try {
            var that = this; 
            var frame = this.options.frame; 
            var cwindow = frame[0].contentWindow;
            var contents = frame.contents();
            var head = contents.find("head");
            var body = contents.find("body");

            this.injectStyle(frame, app.foldersRoot + "/app/custom-screens/screen-builder/editor/api/css/jquery-ui.min.css");
            //this.injectStyle(frame, app.foldersRoot + "/assets/bootstrap/css/bootstrap.min.css");
            this.injectStyle(frame, app.foldersRoot + "/assets/css/font-awesome-4.2.0/css/font-awesome.min.css");

            //var tagViewers = body.find("*[data-sb-type]").filter(function () {
            //    return $(this).data("sb-type").toUpperCase() == "TAGVIEWER"; 
            //});
            //var chartViewers = body.find("*[data-sb-type]").filter(function () {
            //    return $(this).data("sb-type").toUpperCase() == "CHARTVIEWER";
            //});

            var tagViewers = body.find("tagviewer"); 
            var chartViewers = body.find("chartviewer"); 

            var tvviews = [];
            tagViewers.each(function (inx, tv) {
                var placeholder = $(tv);
                tvviews[tvviews.push(new Viewer.Views.TagViewer({
                    container: placeholder,
                })) - 1].render();
            });

            if (this.options.tagViewers && this.options.tagViewers.destroy) this.options.tagViewers.destroy();
            var tviewers = this.options.tagViewers = new Viewer.Collections.TagViewers(_.pluck(tvviews, "model"), {
                screenId: this.options.screenId,
            });

            var chartviews = [];
            chartViewers.each(function (inx, cv) {
                var placeholder = $(cv);
                chartviews[chartviews.push(new Viewer.Views.ChartViewer({
                    container: placeholder,
                })) - 1].render();
            });

            if (this.options.chartViewers && this.options.chartViewers.destroy) this.options.chartViewers.destroy();
            var cviewers = this.options.chartViewers = new Viewer.Collections.ChartViewers(_.pluck(chartviews, "model"), {
                screenId: this.options.screenId,
            });

            var dataAPI = new DataAPI({
                screenId: this.options.screenId,
                user: app.models.user.get("username"),
                timezone: app.models.user.get("timezoneCode"),
            });
            cwindow.DataAPI = dataAPI;

            tviewers.startFetch();
            cviewers.startFetch();

            try {
                if (cwindow && cwindow.screenReady) {
                    cwindow.screenReady.call(cwindow, cwindow.DataAPI);
                }
            } catch (error) { console.log("Screen code error in screenReady function. Error: [" + error.toString() + "]"); }

            this.listenToDataLinks(frame);
            this.applyConfig(contents);
        } catch (error) {
            console.log("Error while trying to load frame environment. Exception: [" + error.toString() + "]");
        }
    };

    env.prototype.applyConfig = function (contents) {
        try {
            var body = contents.find("body"); 

            var backgroundColor = null;
            if (backgroundColor = this.readConfig(contents, "backgroundColor")) { body.css("backgroundColor", backgroundColor); }
        } catch (error) {
            console.log("Error while trying to apply screen config."); 
        }
    }

    env.prototype.readConfig = function(contents, key){
        try {
            var body = contents.find("body");
            var configTag = body.find("config");

            var value = null;
            if (configTag.length > 0) {
                value = configTag.attr(key);
                value = (!_.isString(value)) ? configTag.attr("data-" + key) : value;
                value = (_.isString(value)) ? value : null; 
            }

            return value; 
        } catch (error) {
            console.log("Error while trying to read screen config.");
        }
    }

    env.prototype.listenToDataLinks = function () {
        var that = this;
        var frame = this.options.frame; 
        var links = frame.contents().find("[data-link]");
        links.css("cursor", "pointer");
        links.on("click", function (e) {
            try {
                if (that.options.mode != Environment.Enums.Modes.Viewer) return;

                var linkElem = $(e.target);
                linkElem = linkElem.closest('[data-link]');
                var link = linkElem.data("link");
                link = (_.isString(link)) ? link.trim() : "";

                if (link.length > 0) {
                    var parts = link.split(":");
                    if (parts.length > 1) {
                        var action = parts[0].toUpperCase();
                        var target = link.substring(link.indexOf(':') + 1).trim();

                        switch (action) {
                            case "URL":
                                //go to page
                                if (!target.match(/^[a-zA-Z]+:\/\//)) {
                                    target = 'http://' + target;
                                }

                                window.location.href = target;
                                break;
                            case "SYSTEM":
                                app.router.navigate(target, { trigger: true });
                                break;
                            case "TRENDING":
                                app.router.navigate("!/trending/custom/view=" + target, { trigger: true });
                                break;
                        }
                    }
                }
            } catch (error) { console.log("Error while trying to handle data-link. Error: " + error.toString()); }
        });
    }; 

    env.prototype.injectStyle = function (frame, src) {
        var that = this;
        try {
            frame = frame.contentWindow ? frame : frame.length > 0 && frame[0] && frame[0].contentWindow ? frame[0] : null;
            if (frame) {
                var style = frame.contentWindow.document.createElement("link");
                style.type = "text/css";
                style.href = src;
                style.rel = "stylesheet";
                frame.contentWindow.document.head.appendChild(style);
            }
        } catch (error) {
            console.log("Error while trying to inject style [src=" + src.toString() + "] .");
        }
    }; 

    //<-- custom html views -->//
    Viewer.Models.TagViewer = Backbone.Model.extend({
        defaults: {
            id: null,
            tagId: null,
            tagName: null, 
            label: null,
            timestamp: null,
            value: null,
            unit: null,
        },
    });

    Viewer.Collections.TagViewers = Backbone.Collection.extend({
        model: Viewer.Models.TagViewer,
        initialize: function (models, options) {
            this.options = {
                screenId: options && options.screenId ? options.screenId : null,
                timezone: options && options.timezone ? options.timezone : null,
            };

            this.options.fetch = {
                enabled: false,
                tout: null,
            };
        },
        fetch: function (opt) {
            var that = this;
            var QP = new Core.Database.QueryParameters();

            var tagNames = that.pluck("tagName");

            var items = [];
            if (tagNames.length > 0) {
                IHRuntime.getLastTagValue(tagNames, ("sb-screen-{{screenId}}").replace("{{screenId}}", this.options.screenId), "tagViewers", true, this.options.timezone, function (values) {
                    _.each(values, function (v) {
                        if (v) {
                            var collectionItems = that.where({ tagName: v.name });
                            _.each(collectionItems, function (ii) {
                                ii.set({ value: v.value, timestamp: v.timestamp });
                            });
                        }
                    });

                    //that.set(items); 

                    if (opt && opt.callback && _.isFunction(opt.callback))
                        opt.callback.call(that, that);
                });
            } else {
                if (opt && opt.callback && _.isFunction(opt.callback))
                    opt.callback.call(that, that);
            }
        },
        startFetch: function (callback) {
            var that = this;
            if (this.options.fetch.tout) {
                clearTimeout(this.options.fetch.tout);
                this.options.fetch.tout = null;
            }

            this.options.fetch.enabled = true;
            this.autofetch(callback);
        },
        autofetch: function (callback) {
            var that = this;
            if (this.options.fetch.tout) {
                clearTimeout(this.options.fetch.tout);
                this.options.fetch.tout = null;
            }

            this.fetch({
                callback: function () {
                    if (callback && _.isFunction(callback))
                        callback.call(that, that);

                    if (that.options.fetch.enabled)
                        that.options.fetch.tout = setTimeout(_.bind(that.autofetch, that), 500);
                },
            });
        },
        stopFetch: function () {
            this.options.fetch.enabled = false;
            if (this.options.fetch.tout) {
                clearTimeout(this.options.fetch.tout);
                this.options.fetch.tout = null;
            }
        },
        destroy: function () {
            this.stopFetch();
        }
    });

    Viewer.Views.TagViewer = Backbone.Epoxy.View.extend({
        template: "editor",
        id: "tagviewer",
        title: "tagviewer",
        className: "tagviewer-el",
        //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.container = _.isObject(this.options.container) ? (!this.options.container instanceof jQuery) ? $(this.options.container) : this.options.container : this.options.container;
            this.options.tagName = (_.isString(this.options.tagName)) ? this.options.tagName : (this.options.container) ? this.options.container.data("tagname") : null;
            this.options.tagName = this.options.tagName != null ? this.options.tagName.toString() : null; 
            this.options.label = (_.isString(this.options.label)) ? this.options.label : (this.options.container) ? this.options.container.data("label") : null;
            this.options.unit = (_.isString(this.options.unit)) ? this.options.unit : (this.options.container) ? this.options.container.data("unit") : null;

            var model = new Viewer.Models.TagViewer({
                //id: ("tv-({{tagId}})").replace("{{tagId}}", (this.options.tagId ? this.options.tagId.toString() : '-1')), 
                tagName: this.options.tagName ? this.options.tagName : null,
                label: this.options.label ? this.options.label : null,
                unit: this.options.unit ? this.options.unit.trim() : null,
            });

            this.model = model;

            this.bindEvents();
            //_.bindAll(this);
        },
        events: {
        },
        render: function () {
            var that = this;
            var thatContainer = this.options.container;
            var customPath = "/app/custom-screens/screen-builder/editor/";

            T.render.call(this, this.template, function (tmp) {
                if (!that.options.i18n) that.options.i18n = {};
                app.getI18NJed(that, that.template, function (i18nJED) {
                    that.options.i18n[that.template] = i18nJED;

                    that.$el.html(tmp());
                    that.applyBindings();

                    thatContainer.append(that.$el);
                }, true, customPath);
            }, customPath, "viewer_tagviewer_template");
        },
        refresh: function () {
        },
        bindEvents: function () {
        },
        //close: function () {
        //    this.remove();
        //    this.unbind();
        //},
        //show: function () {
        //    this.bindEvents();
        //    this.$el.show();
        //},
        //hide: function () {
        //    this.$el.hide();
        //    this.unbind();
        //    this.stopListening();
        //},
        //preRender: function () {
        //},
        //reRender: function () {
        //}
    });

    Viewer.Models.ChartDataPoint = Backbone.Model.extend({
        defaults: {
            timestamp: null,
            value: null,
        },
    });

    Viewer.Collections.ChartDataPoints = Backbone.Collection.extend({
        model: Viewer.Models.ChartDataPoint,
    });

    Viewer.Models.ChartDataSet = Backbone.Model.extend({
        defaults: {
            id: null,
            name: null,
            type: "line",
            color: null,
            points: null,
        },
        initialize: function () {
            this.attributes.points = new Viewer.Collections.ChartDataPoints();

            this.listenTo(this.get("points"), "change", this.pointsChanged);
            this.listenTo(this.get("points"), "add", this.pointsChanged);
            this.listenTo(this.get("points"), "remove", this.pointsChanged);
        },
        pointsChanged: function () {
            this.trigger("change");
        },
    });

    Viewer.Collections.ChartDataSets = Backbone.Collection.extend({
        model: Viewer.Models.ChartDataSet,
    });

    Viewer.Models.ChartViewer = Backbone.Model.extend({
        defaults: {
            id: null,
            tagNames: null,
            from: null,
            to: null,
            colors: ["#ff0000"],
            labels: [""],
            timezone: null,
            datasets: null,
            title: null,
        },
        initialize: function () {
            this.attributes.datasets = new Viewer.Collections.ChartDataSets();
            this.attributes.timezone = (!this.attributes.timezone) ? app.models.user.get("timezoneCode") : this.attributes.timezone;
        },
    });

    Viewer.Collections.ChartViewers = Backbone.Collection.extend({
        model: Viewer.Models.ChartViewer,
        initialize: function (models, options) {
            this.options = {
                screenId: options && options.screenId ? options.screenId : null,
            };

            this.options.fetch = {
                enabled: false,
                tout: null,
            };
        },
        fetch: function (opt) {
            var that = this;
            var QP = new Core.Database.QueryParameters();

            var callback = _.after(that.length, function () {
                if (opt && opt.callback && _.isFunction(opt.callback))
                    opt.callback.call(that, that);
            });

            that.each(function (cv) {
                try {
                    var tagNames = cv.get("tagNames");
                    var from = cv.get("from");
                    var to = cv.get("to");
                    var colors = cv.get("colors");
                    var labels = cv.get("labels");
                    var timezone = cv.get("timezone");

                    IHRuntime.getTagValues(tagNames, from, to, timezone, function (values) {
                        var datasets = [];
                        var dsets_points = {};
                        var inx = 0;
                        _.each(values, function (vals, key) {
                            var dataset = new Viewer.Models.ChartDataSet({
                                id: ("dataset-{{key}}").replace("{{key}}", key.toString()),
                                name: (labels[inx]) ? labels[inx].toString() : "",
                                type: "line",
                                color: (colors[inx]) ? colors[inx] : "#ff0000",
                            });

                            datasets.push(dataset);

                            dsets_points[dataset.get("id")] = _.map(vals, function (t) {
                                var point = { id: null, timestamp: null };
                                point.id = new moment(t.timestamp).toDate().getTime();
                                point.timestamp = new moment(t.timestamp).toDate().getTime();
                                point.value = t.value;

                                //_.each(t, function (v, k) {
                                //    if (k.indexOf("Value") != -1) {
                                //        point[k.toLowerCase()] = v;
                                //    }
                                //});

                                return point;
                            });



                            inx++;
                        });

                        if (cv.get("datasets").length == 0)
                            cv.get("datasets").set(datasets);

                        cv.get("datasets").each(function (t) {
                            try {
                                var points = t.get("points");
                                if (dsets_points[t.get("id")])
                                    points.set(dsets_points[t.get("id")]);
                            } catch (error) {
                                console.log(
                                    ("Error while trying to set datapoints to ChartViewer [TagName: {{tagname}} From: {{from}} To: {{to}} .")
                                    .replace("{{tagname}}", cv.get("tagName").toString())
                                    .replace("{{from}}", cv.get("from").toString())
                                    .replace("{{to}}", cv.get("to").toString())
                                )
                            }
                        });

                        callback.call(that);
                    });
                } catch (error) {
                    console.log(
                        ("Error while trying to fetch and set data to ChartViewer [TagName: {{tagname}} From: {{from}} To: {{to}} .")
                        .replace("{{tagname}}", cv.get("tagName").toString())
                        .replace("{{from}}", cv.get("from").toString())
                        .replace("{{to}}", cv.get("to").toString())
                    )

                    callback.call(that);
                }
            });
        },
        startFetch: function (callback) {
            var that = this;
            if (this.options.fetch.tout) {
                clearTimeout(this.options.fetch.tout);
                this.options.fetch.tout = null;
            }

            this.options.fetch.enabled = true;
            this.autofetch(callback);
        },
        autofetch: function (callback) {
            var that = this;
            if (this.options.fetch.tout) {
                clearTimeout(this.options.fetch.tout);
                this.options.fetch.tout = null;
            }

            this.fetch({
                callback: function () {
                    if (callback && _.isFunction(callback))
                        callback.call(that, that);

                    if (that.options.fetch.enabled)
                        that.options.fetch.tout = setTimeout(_.bind(that.autofetch, that), 500);
                },
            });
        },
        stopFetch: function () {
            this.options.fetch.enabled = false;
            if (this.options.fetch.tout) {
                clearTimeout(this.options.fetch.tout);
                this.options.fetch.tout = null;
            }
        },
        destroy: function () {
            this.stopFetch();
        }
    });

    Viewer.Views.ChartViewer = Backbone.Epoxy.View.extend({
        template: "editor",
        id: "chartviewer",
        title: "chartviewer",
        className: "chartviewer-el",
        //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.container = _.isObject(this.options.container) ? (!this.options.container instanceof jQuery) ? $(this.options.container) : this.options.container : this.options.container;
            this.options.tagNames = (_.isString(this.options.tagNames) || _.isString(this.options.tagNames)) ? this.options.tagIds : (this.options.container) ? this.options.container.data("tagnames") : null;
            this.options.colors = (_.isString(this.options.colors)) ? this.options.colors : (this.options.container) ? this.options.container.data("colors") : null;
            this.options.labels = (_.isString(this.options.labels)) ? this.options.labels : (this.options.container) ? this.options.container.data("labels") : null;
            this.options.from = (this.options.from) ? this.options.from : (this.options.container) ? this.options.container.data("from") : null;
            this.options.to = (this.options.to) ? this.options.to : (this.options.container) ? this.options.container.data("to") : null;
            this.options.timezone = (this.options.timezone) ? this.options.timezone : (this.options.container) ? this.options.container.data("timezone") : null;
            this.options.title = (this.options.title) ? this.options.title : (this.options.container) ? this.options.container.data("title") : null;

            this.options.chartGraph = {
                loaded: false,
                loading: false,
                chart: null,
            };

            this.options.tagNames = !_.isArray(this.options.tagNames) ?
            (
                (_.isString(this.options.tagNames))
                ? (this.options.tagNames.toString().split(",")) : []
            )
            : this.options.tagNames;

            this.options.colors = !_.isArray(this.options.colors) ?
            (
                (_.isFinite(this.options.colors) || _.isString(this.options.colors))
                ? (this.options.colors.toString().split(",")) : []
            )
            : this.options.colors;

            this.options.labels = !_.isArray(this.options.labels) ?
            (
                (_.isFinite(this.options.labels) || _.isString(this.options.labels))
                ? (this.options.labels.toString().split(",")) : []
            )
            : this.options.labels;

            var model = new Viewer.Models.ChartViewer({
                tagNames: this.options.tagNames ? this.options.tagNames : Viewer.Models.ChartViewer.prototype.defaults.tagNames,
                from: this.options.from ? this.options.from : Viewer.Models.ChartViewer.prototype.defaults.from,
                to: this.options.to ? this.options.to : Viewer.Models.ChartViewer.prototype.defaults.to,
                colors: this.options.colors ? this.options.colors : Viewer.Models.ChartViewer.prototype.defaults.colors,
                labels: this.options.labels ? this.options.labels : Viewer.Models.ChartViewer.prototype.defaults.labels,
                timezone: this.options.timezone ? this.options.timezone : Viewer.Models.ChartViewer.prototype.defaults.timezone,
                title: this.options.title ? this.options.title : Viewer.Models.ChartViewer.prototype.defaults.title,
            });

            this.model = model;

            this.bindEvents();
        },
        events: {
        },
        render: function () {
            var that = this;
            var thatContainer = this.options.container;
            var customPath = "/app/custom-screens/screen-builder/editor/";

            T.render.call(this, this.template, function (tmp) {
                if (!that.options.i18n) that.options.i18n = {};
                app.getI18NJed(that, that.template, function (i18nJED) {
                    that.options.i18n[that.template] = i18nJED;

                    that.$el.html(tmp());
                    that.applyBindings();

                    //init chart
                    that.instanceChart();

                    thatContainer.append(that.$el);
                }, true, customPath);
            }, customPath, "viewer_chartviewer_template");
        },
        instanceChart: function () {
            var that = this;
            var graphContainer = this.$el.find(".chart-canvas-container");

            var series = this.getSeries(this.model.get("datasets"));
            if (series.length > 0) {
                this.options.chartGraph.loading = true;
                this.options.chartGraph.chart =
                graphContainer.highcharts("Chart", {
                    yAxis: [{
                        plotLines: [{
                            value: 0,
                            width: 2,
                            color: 'silver'
                        }]
                    }, { opposite: false }, { opposite: true }],
                    xAxis: {
                        type: 'datetime',
                        dateTimeLabelFormats: { // don't display the dummy year
                            month: '%e. %b',
                            year: '%b'
                        },
                        title: {
                            text: 'Date'
                        }
                    },
                    series: series,
                    tooltip: {
                        shared: false,
                    },
                    title: {
                        text: this.model.get("title"),
                    },
                }, function () {
                    that.options.chartGraph.loading = false;
                    that.options.chartGraph.loaded = true;
                });
            }
        },
        datasetsChanged: _.debounce(function (a, b, c) {
            var that = this;

            var series = this.getSeries(this.model.get("datasets"));
            var chart = this.options.chartGraph.chart ? this.options.chartGraph.chart.highcharts() : null
            var loading = this.options.chartGraph.loading;
            var loaded = this.options.chartGraph.loaded;

            if (chart && loaded) {
                _.each(series, function (t) {
                    var dd = chart.get(t.id);
                    if (dd && dd.setData) dd.setData(t.data);
                });
            } else if (!loaded && !loading) {
                this.instanceChart();
            }
        }, 500),
        getSeries: function (datasets) {
            datasets = (datasets) ? datasets : this.model.get("datasets");

            var series = datasets.map(function (t) {
                return {
                    name: t.get("name"),
                    id: t.get("id"),
                    type: t.get("type"),
                    lineColor: t.get("color"),
                    data: t.get("points").map(function (p) {
                        var dp = [p.get("timestamp")];

                        _.each(p.keys(), function (k) {
                            if (k.indexOf("value") != -1) {
                                dp.push(p.get(k));
                            }
                        });

                        return dp;
                    }),
                };
            });

            return series;
        },
        refresh: function () {
        },
        bindEvents: function () {
            this.listenTo(this.model.get("datasets"), "change", this.datasetsChanged);
            this.listenTo(this.model.get("datasets"), "add", this.datasetsChanged);
            this.listenTo(this.model.get("datasets"), "remove", this.datasetsChanged);
        },
    });

    return Environment; 
}); 