﻿
/* An analytics engine used to track and submit analytics events. */
function AnalyticsEngine(interval) {

    var eventBuffer = [];   // A buffer to hold events
    var intervalID = null;  // The ID of the running interval


    /* Start the analytics submissions on the configured interval.
       Return false if it couldn't be started because it's already running
       or doesn't have a useful interval configured. */
    this.Start = function (newInterval) {

        if (!intervalID) {

            if (newInterval) {
                interval = newInterval;
            }

            if (interval > 0) {
                intervalID = window.setInterval(submitAllEvents, interval);
                return true;
            }
        }
        return false;
    };


    /* Stop the analytics submissions.
       Return false if it couldn't be stopped (probably because it wasn't running). */
    this.Stop = function (submit) {

        // Submit any events waiting in the buffer
        if (submit) {
            submitAllEvents();
        }

        // Clear the running interval
        if (intervalID) {
            window.clearInterval(intervalID);
            intervalID = null;
            return true;
        }

        return false;
    };


    /* Check to see if analytics submissions are running. */
    this.IsRunning = function () {
        return !!intervalID;
    };


    /* Track an AnalyticsEvent.
       Return false if the event is not a proper AnalyticsEvent. */
    this.TrackEvent = function (event) {

        if (event && event instanceof AnalyticsEvent) {

            if (interval > 0) {
                // Put the event in the buffer if we have an interval
                eventBuffer.push(event);
            } else {
                // Otherwise, submit the event as soon as is convenient
                window.setTimeout(function () {event.Submit()}, 0);
            }

            return true;
        }
        return false;
    };


    /* Submit all the events waiting in the buffer, return the number of events submitted. */
    var submitAllEvents = function () {
        var numEvents = eventBuffer.length;
        for (var i = 0; i < numEvents; i++) {
            eventBuffer[i].Submit();
        }
        eventBuffer = [];
        return numEvents;
    };


    /* Track the end session event and stop the Engine. */
    var unload = function () {
        this.TrackEvent(new ApplicationUnloadEvent());
        this.Stop(true);
    };
    // This binds to the window unload, but isn't reliable.
    // $(window).bind('unload', unload);
};



// An object to keep track of event type metadata.
AnalyticsEvent.TYPES = {
    UNSPECIFIED:        {
                            LABEL: 'unspecifiedEvent'
                        },

    APPLICATION_LOAD:   {
                            LABEL: 'sessionStartEvent',
                            PARAMETERS: {
                                REFERRER: 'referrer'
                            }
                        },

    PROVIDER_SEARCH:    {
                            LABEL: 'providerSearchEvent',
                            FILTER: ['maxRows', 'sortBy'],
                            PARAMETERS: {
                                // ProviderSearch parameters
                                PROVIDER      : 'providerType',
                                PLAN          : 'plan',
                                NETWORK       : 'network',
                                NAME          : 'name',
                                IS_ACCEPTING  : 'isAcceptingNewPatients',
                                GENDER        : 'gender',
                                LANGUAGE      : 'language',
                                ISSUE         : 'condProcSurgId',
                                SPECIALTY     : 'specialtyCategoriesType',
                                DISTANCE      : 'distance',
                                ADDRESS       : 'address',
                                CITY          : 'city',
                                COUNTY        : 'county',
                                STATE         : 'state',
                                ZIP           : 'zip',
                                HOSPITAL      : 'hospitalAffiliation',
                                IS_CERTIFIED  : 'isBoardCertified',
                                IS_PCP        : 'isPCP',
                                IS_EXCELLENT  : 'isBlueDistinctionCenterOfExcellence',
                                IS_IN_NETWORK : 'isInNetwork',
                                IS_ACCREDITED : 'isAccreditedFacility',

                                // Human-readable labels
                                PROVIDER_LABEL  : 'providerTypeLabel',
                                PLAN_LABEL      : 'planLabel',
                                NETWORK_LABEL   : 'networkLabel',
                                LANGUAGE_LABEL  : 'languageLabel',
                                ISSUE_LABEL     : 'condProcSurgIdLabel',
                                SPECIALTY_LABEL : 'specialtyCategoriesTypeLabel',
                                HOSPITAL_LABEL  : 'hospitalAffiliationLabel'
                            }
                        },

    APPLICATION_UNLOAD: { 
                            LABEL: 'sessionEndEvent'
                        }
};



/* An object to encapsulate a generic event to be logged in web analytics. */
function AnalyticsEvent(type, parameters) {

    // The type of event this is. 
    type = type || AnalyticsEvent.TYPES.UNSPECIFIED.LABEL;

    // The salient parameters for the event.
    parameters = parameters || {};


    /* Gets a parameter's value. */
    this.GetParameter = function (name) {
        return parameters[name];
    };


    /* Sets a new parameter, return false if a proper name/value pair is missing. */
    this.SetParameter = function (name, value) {
        if (name && value) {
            parameters[name] = value;
            return true;
        }
        return false;
    };


    /* Remove a parameter, return its value or false if it wan't found in the event. */
    this.RemoveParameter = function (name) {
        if (name && parameters[name]) {
            var value = parameters[name];
            delete parameters[name];
            return value;
        }
        return false;
    };


    /* Return the parameters in an object that the framework's BaseWebService expects. */
    this.GetWebServiceParameters = function () {
        var wsParameters = []
        for (var name in parameters) {
            var value = parameters[name];
            wsParameters.push(name, value);
        }

        return {
            ObjectName: type,
            Parameters: wsParameters
        };
    };


    /* Return the CGI parameters in an object. */
    this.GetCGIParameters = function () {
        var cgiParameters = {};
        cgiParameters['type'] = type;

        for (var name in parameters) {
            cgiParameters[name] = parameters[name];
        }

        return cgiParameters;
    };


    /* Submit the event. */
    this.Submit = function () {
        if (MV.WebServices.Analytics.LogWriter) {
            MV.WebServices.Analytics.LogWriter(this.GetWebServiceParameters());
        }
    };


    /* Override Object.toString(). */
    this.toString = function () {

        // An array to hold string bits
        var stringArray = ['{\n\t'/* , '"type":"', type, '"' */];

        for (var name in parameters) {
            var value = parameters[name];
            if (typeof value === "string") {
                if (value === "") {
                    value = null;
                }
                else {
                    // TODO: escape quotes etc to make valid JSON
                    value = '"' + value + '"';
                }
            }
            stringArray.push(',\n\t"', name, '":', value);
        }

        stringArray.push('\n}');
        return stringArray.join();
    };


    /* Override Object.valueOf(). */
    this.valueOf = function () {
        return this.toString();
    };
}


/* An event for application load */
ApplicationLoadEvent.superclass = AnalyticsEvent;
function ApplicationLoadEvent() {

    var parameters = {};
    var referrer = '';
    try {
        referrer = document.referrer || opener.location.href;
    } catch (e) {}
    parameters[AnalyticsEvent.TYPES.APPLICATION_LOAD.PARAMETERS.REFERRER] = referrer;

    arguments.callee.superclass.call(this, AnalyticsEvent.TYPES.APPLICATION_LOAD.LABEL, parameters);

    /* Submit the event. */
    this.Submit = function () {
        if (MV.WebServices.Analytics.LogSessionStart) {
            MV.WebServices.Analytics.LogSessionStart(this.GetWebServiceParameters());
        }
    };
}
ApplicationLoadEvent.prototype = (function () {return new ApplicationLoadEvent.superclass()})();
ApplicationLoadEvent.prototype.constructor = ApplicationLoadEvent.superclass;


/* An event for a provider search */
ProviderSearchEvent.superclass = AnalyticsEvent;
function ProviderSearchEvent(parameters) {

    // Parameter names for a ProviderSearch event.
    var PARAMETER_NAMES = AnalyticsEvent.TYPES.PROVIDER_SEARCH.PARAMETERS;
    var PARAMETER_FILTER = AnalyticsEvent.TYPES.PROVIDER_SEARCH.FILTER;

    for (var i = 0, j = PARAMETER_FILTER.length; i < j; i++) {
        if (typeof parameters[PARAMETER_FILTER[i]] != "undefined") {
            delete parameters[PARAMETER_FILTER[i]];
        }
    }

    arguments.callee.superclass.call(this, AnalyticsEvent.TYPES.PROVIDER_SEARCH.LABEL, parameters);


    /* Submit the event. */
    this.Submit = function () {
        if (MV.WebServices.Analytics.LogProviderSearch) {
            MV.WebServices.Analytics.LogProviderSearch(this.GetWebServiceParameters());
        }
    };


    /* Set the human-readable plan label. */
    this.SetPlanLabel = function (label) {
        parameters[PARAMETER_NAMES.PLAN_LABEL] = label;
    };


    /* Set the human-readable provider type label */
    this.SetProviderTypeLabel = function (label) {
        parameters[PARAMETER_NAMES.PROVIDER_LABEL] = label;
    };


    /* Set the human-readable health issue label */
    this.SetHealthIssueLabel = function (label) {
        parameters[PARAMETER_NAMES.ISSUE_LABEL] = label;
    };


    /* Set the human-readable search configuration labels 
       (decorator returned from GetSearchConfigurationDecorator webservice) */
    this.SetSearchConfigurationLabels = function (decorator) {

        // Iterate through the list of networks.
        var networkCode = parameters[PARAMETER_NAMES.NETWORK];
        var networkList = decorator.NetworkList;
        parameters[PARAMETER_NAMES.NETWORK_LABEL] = null;
        if (networkCode && networkList) {
            for (var i = 0, l = networkList.length; i < l; i++) {
                var networkData = networkList[i];
                if (networkData.Code === networkCode) {
                    parameters[PARAMETER_NAMES.NETWORK_LABEL] = networkData.Name;
                    break;
                }
            }
        }

        // Iterate through the list of languages.
        var languageCode = parameters[PARAMETER_NAMES.LANGUAGE];
        var languageList = decorator.LanguageList;
        parameters[PARAMETER_NAMES.LANGUAGE_LABEL] = null;
        if (languageCode && languageList) {
            for (var i = 0, l = languageList.length; i < l; i++) {
                var languageData = languageList[i];
                if (languageData.Code === languageCode) {
                    parameters[PARAMETER_NAMES.LANGUAGE_LABEL] = languageData.Name;
                    break;
                }
            }
        }

        // Iterate through the list of specialities.
        var specialityCode = parameters[PARAMETER_NAMES.SPECIALTY];
        var specialityList = decorator.SpecialtyCategoryList;
        parameters[PARAMETER_NAMES.SPECIALTY_LABEL] = null;
        if (specialityCode && specialityList) {
            for (var i = 0, l = specialityList.length; i < l; i++) {
                var specialityData = specialityList[i];
                if (specialityData.Code === specialityCode) {
                    parameters[PARAMETER_NAMES.SPECIALTY_LABEL] = specialityData.Name;
                    break;
                }
            }
        }

        // Iterate through the list of affiliated hospitals.
        var hospitalCode = parameters[PARAMETER_NAMES.HOSPITAL];
        var hospitalList = decorator.HosptialFacilityAffliations;
        parameters[PARAMETER_NAMES.HOSPITAL_LABEL] = null;
        if (hospitalCode && hospitalList) {
            for (var i = 0, l = hospitalList.length; i < l; i++) {
                var hospitalData = hospitalList[i];
                if (hospitalData.Code === hospitalCode) {
                    parameters[PARAMETER_NAMES.HOSPITAL_LABEL] = hospitalData.Name;
                    break;
                }
            }
        }
    };
}
ProviderSearchEvent.prototype = (function () {return new ProviderSearchEvent.superclass()})();
ProviderSearchEvent.prototype.constructor = ProviderSearchEvent.superclass;


/* An event for the end of the session. */
ApplicationUnloadEvent.superclass = AnalyticsEvent;
function ApplicationUnloadEvent() {
    arguments.callee.superclass.call(this, AnalyticsEvent.TYPES.APPLICATION_UNLOAD.LABEL);

    /* Submit the event. */
    this.Submit = function () {
        // NOOP - don't have a WS for this yet, not sure about its realiability onunload.
    };
}
ApplicationUnloadEvent.prototype = (function () {return new ApplicationUnloadEvent.superclass()})();
ApplicationUnloadEvent.prototype.constructor = ApplicationUnloadEvent.superclass;
