﻿
(function($)
{
    IRGraph = function() { };

    IRGraph.prototype = {
        formatLargeNumber: function(value)
        {
            var newVal = value.toString();
            var regEx = new RegExp(/(\d+)(\d{3})/);
            while (regEx.test(newVal))
            {
                newVal = newVal.replace(regEx, function(a, b, c) { return b + "," + c; });
            }
            return newVal;
        },
        formatTwoDecimals: function(value)
        {
            var newVal = Number(value);
            return newVal.toFixed(2);
        },
        formatDate: function(d)
        {
            if (d.toString() == "") return "";
            d = new Date(d);
            return (d.getMonth() + 1) + "/" + d.getDate() + "/" + d.getFullYear();
        },
        failCall: function() { },
        winCall: function() { }
    };

    StockGraph = function(graph)
    {
        this.graph = $(graph);
        this.eventsDisplayed = [];
        this.tooltipBound = false;

        // Move the TrackLine and ToolTip up to be the first DIVs in the body.
        // This will prevent some absolute positioning problems we ran into.
        $("body").prepend($("div#TrackLine, div#ToolTip, embed#LoadAnim"));
    };

    StockGraph.prototype = $.extend(new IRGraph(), {
        //Requests a new image from the server, presents it, and sets up all the data for tooltips
        getImg: function(keyInstn, keyFndg, peers, indexes, type, startDate, endDate, preview, size, showDivYield, showTotalReturn)
        {
            var data = { obj: {} };
            var that = this;

            //Some javascript libraries extend the default Array object.
            //This causes problems any time we try to serialize an Array for a WCF service requests.
            //So here we are removing any prototyped additions to the Array object.
            for (var i in new Array()) {
                peers[i] = null;
                indexes[i] = null;
            }

            data.obj.KeyInstn = keyInstn;
            data.obj.KeyFndg = keyFndg;
            data.obj.KeyPage = 184;
            data.obj.Peers = peers;
            data.obj.Indexes = indexes;
            data.obj.DivYield = showDivYield;
            data.obj.TotalReturn = showTotalReturn;
            data.obj.Size = size;
            data.obj.Type = type;
            data.obj.StartDate = this.formatDate(startDate);
            data.obj.EndDate = this.formatDate(endDate);
            data.obj.Preview = preview;
            data.obj.IsMobile = false;


            var currentURL = location.href;
            var webServiceURL = "";
            if (currentURL.toLowerCase().indexOf("/mobile/") != -1)
            {  
                data.obj.IsMobile = true;
                webServiceURL = "../GraphProvider.svc/StockGraph";
            }
            else
            {
                webServiceURL = "GraphProvider.svc/StockGraph";
            }

            $.ajax({
                type: "POST",
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                url: webServiceURL,
                data: JSON.stringify(data),
                success: function(msg)
                {
                    try
                    {
                        msg = msg.d;
                        that.graph.attr("src", msg.Link);
                        that.tooltipData = [];
                        for (var i = 0; i < msg.Data.length; i++)
                        {
                            that.tooltipData[msg.Data[i].Key] = msg.Data[i].Value;
                        }
                        that.stock = { "outer": msg.StockOuter, "inner": msg.StockInner };
                        that.volume = { "outer": msg.VolumeOuter, "inner": msg.VolumeInner };
                        that.winCall();
                    }
                    catch (ex)
                    {
                        that.failCall();
                    }
                },
                error: function()
                {
                    that.failCall();
                }
            });
        },
        //Refreshes the tooltip if the user mouses over a point with new data, otherwise just moves the
        //tooltip with the mouse so it doesn't seem "jumpy" to the user
        mouseMove: function(x, y)
        {
            if (this.tooltipData && this.stock && this.volume && this.offSet)
            {
                var left = this.offSet.left + this.stock.inner.X;
                var top = this.offSet.top + this.stock.inner.Y;
                var right = this.offSet.left + this.stock.inner.Width;
                var bottom = this.offSet.top + this.stock.inner.Height;
                var events;

                if (this.eventsDisplayed.length > 0)
                {
                    for (var i = 0; i < this.eventsDisplayed.length; i++)
                    {
                        if (this.eventsDisplayed[i].top <= y
                        && this.eventsDisplayed[i].left <= x
                        && this.eventsDisplayed[i].bottom >= y
                        && this.eventsDisplayed[i].right >= x)
                        {
                            events = this.eventsDisplayed[i];
                        }
                    }
                }

                if (x >= left && y >= top && x <= right && y <= bottom)
                {
                    //Calculate and show the tooltip
                    if (this.tooltip)
                    {
                        var dataObj = this.tooltipData && this.tooltipData[x - Math.round(this.offSet.left)];
                        var ttLeft = x;
                        var ttTop = y - this.tooltip.outerHeight();
                        if (ttTop < window.pageYOffset) ttTop = window.pageYOffset;
                        if (dataObj || events)
                        {
                            if (!events)
                            {
                                if (!this.tooltipBound)
                                {
                                    this.tooltip.mousemove(this.tooltipEvent);
                                    this.tooltipBound = true;
                                }
                                this.tooltip.html(this.formatDataPoint(dataObj));
                            }
                            else
                            {
                                if (this.tooltipBound)
                                {
                                    this.tooltip.unbind("mousemove", this.tooltipEvent);
                                    this.tooltipBound = false;
                                }
                                this.tooltip.html(this.formatEvents(events.data, events.list));
                            }
                            ttLeft = x;
                            ttTop = y - this.tooltip.outerHeight();
                            if (ttTop < window.pageYOffset) ttTop = window.pageYOffset;
                            this.tooltip.css({ "left": ttLeft, "top": ttTop }).show();
                        }
                        else this.tooltip.css({ "left": ttLeft, "top": ttTop });
                    }

                    //Move trackline
                    if (this.trackline)
                    {
                        var tlLeft = x;
                        var tlTop = top;
                        var height = this.volume.inner.Height - this.stock.inner.Y;
                        this.trackline.css({ "left": tlLeft, "top": tlTop, "height": height, "opacity": 0.5 }).show();
                    }
                }
                else
                {
                    if (this.tooltip) this.tooltip.hide();
                    if (this.trackline) this.trackline.hide();
                }
            }
        },
        //The offset is only good after the image is done loading, saves a little effort to only grab this once
        imgLoad: function()
        {
            this.offSet = this.graph.offset();
        },
        showEvents: function(events)
        {
            var that = this;
            if (this.eventsDisplayed.length > 0)
            {
                $.each(this.eventsDisplayed, function(i)
                {
                    that.eventsDisplayed[i].img.remove();
                });
                this.eventsDisplayed = [];
            }
            if (this.tooltipData && this.offSet)
            {
                for (var i = 0; i < this.tooltipData.length; i++)
                {
                    var show = "";
                    var makeImage = [];
                    for (var j = 0; j < events.length; j++)
                    {
                        if (this.tooltipData[i] && this.tooltipData[i][events[j]].length > 0)
                        {
                            //if (!show) show = events[j];
                            //else show = "All";
                            makeImage.push(events[j]);
                        }
                    }
                    if (makeImage.length > 0)
                    {
                        var show = "";
                        if (makeImage.length > 1) show = "All";
                        else show = makeImage[0];

                        var baseImage = $("#" + show);
                        var newImage = baseImage.clone().appendTo($("body"));
                        var top = this.tooltipData[i].Y - baseImage.height() + this.offSet.top;
                        var left = i - (baseImage.width() / 2) + this.offSet.left;
                        var bottom = top + baseImage.height();
                        var right = left + baseImage.width();
                        newImage.css({ "top": top, "left": left, "z-index": 10000 }).mousemove(function(e) { that.mouseMove(e.pageX, e.pageY); });
                        newImage.show();

                        this.eventsDisplayed.push({
                            "img": newImage,
                            "top": top,
                            "left": left,
                            "bottom": bottom,
                            "right": right,
                            "list": makeImage,
                            "data": this.tooltipData[i]
                        });
                    }
                }
            }
        },
        formatDataPoint: function(dataObj)
        {
            var appendText = "<table cellpadding='3'>";
            appendText += "<tr>";
            appendText += "<td style='font-weight:bold;background-color: #ffffe0' class='data'>" + this.formatDate(dataObj.Date) + "</td>";
            appendText += "<td style='background-color: #ffffe0' class='data'>high</td>";
            appendText += "<td style='background-color: #ffffe0' class='data'>low</td>";
            appendText += "<td style='background-color: #ffffe0' class='data'>close</td>";
            appendText += "<td style='background-color: #ffffe0' class='data'>volume</td>";
            appendText += "</tr>";
            for (var i = 0; i < dataObj.Prices.length; i++)
            {
                appendText += "<tr>";
                appendText += "<td style='background-color: #ffffe0' class='data'>" + dataObj.Prices[i].Ticker + "</td>";
                appendText += "<td style='background-color: #ffffe0' align='right' class='data'>";
                if (dataObj.Prices[i].High) appendText += this.formatTwoDecimals(dataObj.Prices[i].High);
                else appendText += "-";
                appendText += "</td>";
                appendText += "<td style='background-color: #ffffe0' align='right' class='data'>";
                if (dataObj.Prices[i].Low) appendText += this.formatTwoDecimals(dataObj.Prices[i].Low);
                else appendText += "-";
                appendText += "</td>";
                appendText += "<td style='background-color: #ffffe0' align='right' class='data'>" + this.formatTwoDecimals(dataObj.Prices[i].Close) + "</td>";
                appendText += "<td style='background-color: #ffffe0' align='right' class='data'>";
                if (dataObj.Prices[i].Volume) appendText += this.formatLargeNumber(dataObj.Prices[i].Volume);
                else appendText += "-";
                appendText += "</td>";
            }
            appendText += "</table>";

            return appendText;
        },
        formatEvents: function(dataObj, list)
        {
            var labelMap = {
                "Divs": "Dividend",
                "PRs": "Press Release",
                "Docs": "Document",
                "Splits": "Split",
                "Filings": "Filing"
            };

            var appendText = "<table>";

            for (var i = 0; i < list.length; i++)
            {
                for (var j = 0; j < dataObj[list[i]].length; j++)
                {
                    appendText += "<tr><td>";
                    appendText += "<span class='data' style='background-color: #ffffe0'><span style='font-weight: bold'>" + dataObj[list[i]][j].Date;
                    appendText += "</span> - " + labelMap[list[i]] + "</span><br/>";
                    if (dataObj[list[i]][j].Link)
                    {
                        appendText += "- <a href='" + dataObj[list[i]][j].Link + "' target='_blank'>";
                        appendText += dataObj[list[i]][j].Desc + "</a>";
                    }
                    else appendText += "<span class='data' style='background-color: #ffffe0'>- " + this.formatTwoDecimals(dataObj[list[i]][j].Desc) + "</span>";
                    appendText += "</td></tr>";
                }
            }

            appendText += "</table>";

            return appendText;
        }
    });

    LOBChart = function(graph)
    {
        this.graph = $(graph);
    };

    LOBChart.prototype = $.extend(new IRGraph(), {
        getImg: function(keyStatEntity, naic, display)
        {
            var data = { initData: {} };
            var that = this;

            //Some javascript libraries extend the default Array object.
            //This causes problems any time we try to serialize an Array for a WCF service requests.
            //So here we are removing any prototyped additions to the Array object.
            for (var i in new Array())
            {
                delete Array.prototype[i];
            }

            data.initData.KeyStatEntity = keyStatEntity;
            data.initData.NAICType = naic;
            data.initData.DisplayType = display;
            var errorText = document.getElementById('ErrorText');
            var titleText = document.getElementById('TitleText');
            var title;
            switch (data.initData.NAICType)
            {
                case "P&C":
                    switch (data.initData.DisplayType)
                    {
                        case "Type":
                            title = "Net Premiums Written by Type";
                            break;
                        case "Geography":
                            title = "Direct Premiums Written by Geography";
                            break;
                    }
                    break;
                case "Life":
                    switch (data.initData.DisplayType)
                    {
                        case "Type":
                            title = "Life and A&H Net Premiums Written by Type";
                            break;
                        case "Time":
                            title = "Life, Annuity and A&H Business over Time";
                            break;
                    }
                    break;
                case "Health":
                    switch (data.initData.DisplayType)
                    {
                        case "Revenue":
                            title = "Underwriting Revenue and Components";
                            break;
                        case "Geography":
                            title = "Health Premiums Written by Geography";
                            break;
                    }
                    break;
            }
            titleText.innerHTML = title;

            $.ajax({
                type: "POST",
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                url: "GraphProvider.svc/LOBGraph",
                data: JSON.stringify(data),
                success: function(msg)
                {
                    try
                    {
                        msg = msg.d;
                        if (msg == "")
                        {
                            that.graph.attr("src", "images/blank.gif");
                            errorText.innerHTML = "There is no data for your combination of selections.  Please make another choice.";
                        }
                        else
                        {
                            that.graph.attr("src", msg);
                            errorText.innerHTML = "";
                        }
                        that.winCall();
                    }
                    catch (ex)
                    {
                        that.failCall();
                    }
                },
                error: function()
                {
                    that.failCall();
                }
            });
        }
    });



    parseDate = function(dateString, timeString)
    {
        var dateParts;
        var datePartsLength;
        var month;
        var day;
        var year;

        if (dateString && typeof dateString === "string")
        {
            dateParts = dateString.split("/");

            for (datePartsLength = dateParts.length; datePartsLength < dateParts.length; ) { }

            if (datePartsLength == 3)
            {
                month = parseInt(trimLeading(dateParts[0], "0"));
                if (isNaN(month) || month < 1 || month > 12) return Number.NaN;

                day = parseInt(trimLeading(dateParts[1], "0"));
                if (isNaN(day) || day < 1 || day > 31) return Number.NaN;

                year = parseInt(dateParts[2]);
                if (isNaN(year) || dateParts[2].length != 4) return Number.NaN;
            }
            else return Number.NaN
        }
        else return Number.NaN;

        if (timeString || timeString === "")
        {
            if (typeof timeString === "string" && timeString !== "")
            {
                var timeParts = timeString.split(" ");

                for (timePartsLength = timeParts.length; timePartsLength < timeParts.length; ) { }

                if (timePartsLength != 2) return Number.NaN;

                timeLabel = timeParts[1].toUpperCase();

                timeParts = timeParts[0].split(":");

                for (timePartsLength = timeParts.length; timePartsLength < timeParts.length; ) { }

                if (timePartsLength != 2) return Number.NaN;

                hours = parseInt(trimLeading(timeParts[0]), "0");

                minutes = parseInt(trimLeading(timeParts[1]), "0");

                if (isNaN(hours) || hours < 1 || hours > 12) return Number.NaN;

                if (isNaN(minutes) || minutes < 0 || minutes > 59) return Number.NaN;

                if (timeLabel != "AM" && timeLabel != "PM") return Number.NaN;

                if (timeLabel == "PM" && hours != 12)
                    hours += 12;
                else if (timeLabel == "AM" && hours == 12)
                    hours = 0;
            }
            else return Number.NaN;

            newDate = new Date(dateString + " " + timeString);
        }
        else
            newDate = new Date(dateString);

        return newDate.getTime();
    }

    trimLeading = function(s, chr)
    {
        while (s.substr(0, 1) === chr && s.length > 1) { s = s.substr(1); };
        return s;
    }

    //Setup for each graph implementation, these are turned into jQuery plugins below
    //These are tightly coupled with above classes that have functions for performing chart related operations
    //(as opposed to chart setup)
    var graphImps = {
        stockGraph: {
            action: function(settings)
            {
                var graph = new StockGraph(this);
                var keyInstn;
                var keyFndg;
                var eventValues = [];
                var optionSelectors = [];
                var controlObj, startDate = "", endDate = "";
                var dateTemp, preview = false;
                var size = 0;

                //This can be modified with event display logic below if the proper settings are passed in
                var showEvents = function() { };

                if (settings.keyInstn)
                {
                    keyInstn = settings.keyInstn;
                    keyFndg = settings.keyFndg;

                    var mouseMove = function(e) { graph.mouseMove(e.pageX, e.pageY); };
                    $(this).load(function()
                    {
                        if (settings.loadAnim) $(settings.loadAnim).hide();
                        graph.imgLoad();
                        showEvents();
                    });

                    $(this).mousemove(mouseMove);

                    if (settings.tooltipControl)
                    {
                        graph.tooltip = $(settings.tooltipControl);
                        graph.tooltip.mousemove(mouseMove);
                        graph.tooltipBound = true;
                        graph.tooltipEvent = mouseMove;
                    }

                    if (settings.tracklineControl)
                    {
                        graph.trackline = $(settings.tracklineControl);
                        graph.trackline.mousemove(mouseMove);
                    }

                    if (settings.preview)
                    {
                        preview = settings.preview;
                    }

                    if (settings.refreshControl && settings.keyInstn)
                    {
                        if (!$.isArray(settings.refreshControl)) controlObj = [settings.refreshControl];
                        else controlObj = settings.refreshControl;

                        for (var i = 0; i < controlObj.length; i++)
                        {
                            if (controlObj[i].selector && controlObj[i].event)
                            {
                                (function(control)
                                {
                                    var refresh = $(control.selector);
                                    refresh.bind(control.event, function()
                                    {
                                        var peers = [], indexes = [], type = "Line";
                                        var startDate = "", endDate = "";
                                        var dateAry = [];
                                        var dYield = false, tReturn = false;

                                        if (control.peers)
                                        {
                                            $(control.peers).each(function()
                                            {
                                                peers.push($(this).val());
                                            });
                                        }

                                        if (control.indexes)
                                        {
                                            $(control.indexes).each(function()
                                            {
                                                indexes.push($(this).val());
                                            });
                                        }

                                        if (control.graphType)
                                        {
                                            type = $(control.graphType).val();
                                        }

                                        if (control.startDate)
                                        {
                                            if ($.isArray(control.startDate))
                                            {
                                                for (var i = 0; i < control.startDate.length; i++)
                                                {
                                                    dateAry.push(control.startDate[i]);
                                                }
                                            }
                                            else dateAry.push(control.startDate);
                                        }

                                        if (settings.startDate) dateAry.push(settings.startDate);

                                        for (var i = 0; i < dateAry.length; i++)
                                        {
                                            dateTemp = $(dateAry[i]).val();
                                            if ($(dateAry[i]).attr("type") != "text" || !isNaN(parseDate(dateTemp)))
                                            {
                                                startDate = dateTemp;
                                                break;
                                            }
                                        }

                                        dateAry = [];
                                        if (control.endDate)
                                        {
                                            if ($.isArray(control.endDate))
                                            {
                                                for (var i = 0; i < control.endDate.length; i++)
                                                {
                                                    dateAry.push(control.endDate[i]);
                                                }
                                            }
                                            else dateAry.push(control.endDate);
                                        }

                                        if (settings.endDate) dateAry.push(settings.endDate);

                                        for (var i = 0; i < dateAry.length; i++)
                                        {
                                            dateTemp = $(dateAry[i]).val();
                                            if ($(dateAry[i]).attr("type") != "text" || !isNaN(parseDate(dateTemp)))
                                            {
                                                endDate = dateTemp;
                                                break;
                                            }
                                        }

                                        if (settings.size) size = $(settings.size).val();

                                        if (control.showDivYield) dYield = control.showDivYield();
                                        if (control.showTotalReturn) tReturn = control.showTotalReturn();

                                        if (startDate && endDate)
                                        {
                                            graph.getImg(keyInstn, keyFndg, peers, indexes, type, startDate, endDate, preview, size, dYield, tReturn);
                                            if (settings.loadAnim)
                                            {
                                                var offSet = graph.graph.offset();
                                                var height, width, top, left;
                                                var loading = $(settings.loadAnim);

                                                if (graph.graph.attr("src"))
                                                {
                                                    left = (offSet.left + (graph.graph.width() / 2)) - (loading.width() / 2);
                                                    top = (offSet.top + (graph.graph.height() / 2)) - (loading.height() / 2);
                                                    loading.css({ "position": "absolute", "top": top, "left": left });
                                                }
                                                else
                                                {
                                                    height = loading.height();
                                                    width = loading.width();
                                                    loading.css({ "height": height, "width": width })
                                                }

                                                loading.show();
                                            }
                                        }
                                    });
                                })(controlObj[i]);
                            }
                        }
                    }

                    if (settings.events && settings.events.control && settings.events.selected && settings.events.event)
                    {
                        showEvents = function()
                        {
                            eventValues = [];
                            $(settings.events.selected).each(function()
                            {
                                eventValues.push($(this).val());
                            });
                            graph.showEvents(eventValues);
                        };
                        $(settings.events.control).bind(settings.events.event, showEvents);
                    }

                    if (settings.showDivYield)
                    {
                        graph.showDivYield = settings.showDivYield;
                    }

                    if (settings.showTotalReturn)
                    {
                        graph.showTotalReturn = settings.showTotalReturn;
                    }

                    if (settings.onerror)
                    {
                        graph.failCall = settings.onerror;
                    }

                    if (settings.onsuccess)
                    {
                        graph.winCall = settings.onsuccess;
                    }

                    startDate = $(settings.startDate).val();
                    endDate = $(settings.endDate).val();

                    if (settings.size) size = $(settings.size).val();

                    if (startDate && endDate)
                    {
                        graph.getImg(keyInstn, keyFndg, [], [], "Area", startDate, endDate, preview, size, false, false);
                        if (settings.loadAnim)
                        {
                            var offSet = graph.graph.offset();
                            var height, width, top, left;
                            var loading = $(settings.loadAnim);

                            if (graph.graph.attr("src"))
                            {
                                height = graph.graph.height();
                                width = graph.graph.width();
                                left = (offSet.left + (graph.graph.width() / 2)) - (loading.width() / 2);
                                top = (offSet.top + (graph.graph.height() / 2)) - (loading.height() / 2);
                                loading.css({ "position": "absolute", "top": top, "left": left });
                            }
                            else
                            {
                                height = loading.height();
                                width = loading.width();
                                loading.css({ "height": height, "width": width })
                            }

                            loading.show();
                        }
                    }
                }
            },
            config: {}
        },
        lobChart: {
            action: function(settings)
            {
                var graph = new LOBChart(this);
                var controlObj;

                if (settings.keySelector && settings.typeSelector && settings.displaySelector)
                {
                    $(this).load(function() { graph.graph.show(); });
                    if (settings.refreshControl)
                    {
                        if (!$.isArray(settings.refreshControl)) controlObj = [settings.refreshControl];
                        else controlObj = settings.refreshControl;

                        for (var i = 0; i < controlObj.length; i++)
                        {
                            if (controlObj[i].selector && controlObj[i].event)
                            {
                                (function(control)
                                {
                                    var refresh = $(control.selector);
                                    refresh.bind(control.event, function()
                                    {
                                        if (control.setup) control.setup();
                                        graph.getImg($(settings.keySelector).val(), $(settings.typeSelector).text(), $(settings.displaySelector).val());
                                    });
                                })(controlObj[i]);
                            }
                        }
                        controlObj = settings.refreshControl;
                    }

                    graph.getImg($(settings.keySelector).val(), $(settings.typeSelector).text(), $(settings.displaySelector).val());
                }
            },
            config: {}
        }
    };

    //Saves the trouble of making each behavior a proper plugin, all the jQuery setup is contained
    $.each(graphImps, function(i)
    {
        $.fn[i] = function(settings)
        {
            var param = {};
            $.extend(param, graphImps[i].config);
            if (settings) $.extend(param, settings);
            this.each(function() { graphImps[i].action.call(this, param) });
            return this;
        };
    });

})(jQuery);
