﻿// SCOTT.CSC.Tools.Gas.Calculator.Application

/// <reference name="MicrosoftAjax.debug.js" />
/// <reference name="MicrosoftAjaxTimer.debug.js" />
/// <reference name="MicrosoftAjaxWebForms.debug.js" />

/// <reference path="../jquery-1.2.6-vsdoc.js"/>

Type.registerNamespace('SCOTT.CSC.Tools.Gas.Calculator');

SCOTT.CSC.Tools.Gas.Calculator.Application = function(panelId) 
{   
    var __app = this;
    this.ClientID = panelId;
    this.Selector = '#' + panelId;
    this.IsByAddress = true;
    this.AutoCalculateEnabled = false;
    
    this.HighlightColor = '#e9f6fb';
    this.ErrorColor = '#f55';
    
    // inheritance (of sorts)
    SCOTT.CSC.Extend(SCOTT.CSC.Tools.Gas.Calculator.Application, SCOTT.CSC.Core);
    
    this.Initialize();
    this.Start();
};

SCOTT.CSC.Tools.Gas.Calculator.Application.prototype =
{
    __type : 'SCOTT.CSC.Tools.Gas.Calculator.Application',
    Start : function()
    {
        var __app = this;
        var context = $(this.Selector);
        
        this.ClearResults();
        $('#Results .Inner', context).fadeTo(0, 0.45);
        setTimeout(function()
        {
            $('#Application', context).show();
        }, 20);
        
        if ($.browser.msie) 
        {
            $('#Results .Inner').css('background-color', '#fff');
        }
    },
    AttachEvents : function()
    {
        var __app = this;
        var context = $(this.Selector);
        
        var onClickCalcByAddress = function()
        {
            __app.AutoCalculateEnabled = false;
            __app.IsByAddress = true;
            __app.ShowCalculator();
            
            return false;
        };
        
        $('#CalcByAddress', context).click(onClickCalcByAddress);
        
        var onClickCalcByMiles = function()
        {
            __app.AutoCalculateEnabled = false;
            __app.IsByAddress = false;
            __app.ShowCalculator();
            
            return false;
        };
        
        $('#CalcByMiles', context).click(onClickCalcByMiles);

        var runCalculate = function()
        {
            $('#Results .Inner', context).fadeTo(0, 0.45, function()
            {
                if (__app.IsByAddress)
                {
                    __app.FetchDistance(function()
                    {
                        __app.UpdateResults();
                    });
                }
                else
                {
                    __app.UpdateResults();
                }
            });
        };
                
        var onClickCalculate = function()
        {
            __app.AutoCalculateEnabled = true;
            runCalculate();
            return false;
        };
        
        $('#CalculateButton', context).click(onClickCalculate);
        
        var autoValidate = function()
        {
            if (!__app.AutoCalculateEnabled)
            {
                return;
            }
            
            __app.ValidateForm();
        };
        
        var autoCalculate = function()
        {
            if (!__app.AutoCalculateEnabled)
            {
                return;
            }
            
            var onValidateCallback = function(isValid)
            {
                if (isValid)
                {
                    runCalculate();
                }
            };
            
            __app.ValidateForm(onValidateCallback);
        };
        
        var onKeyDown = function(ev)
        {
            if (ev.keyCode === 13)
            {
                __app.AutoCalculateEnabled = true;
                autoCalculate();
                return false;
            }
        };
        
        $('input', context).add('select').
            change(autoCalculate).
            keydown(onKeyDown);
            
        $('input', context).add('select').
            blur(autoValidate);
    },
    ShowCalculator : function()
    {
        var __app = this;
        var context = $(this.Selector);
    
        if (this.IsByAddress)
        {
            if ($('#Calculator .Form .ByAddress', context).is(':hidden'))
            {
                $('#CalcByMiles', context).removeClass('Active');
                $('#CalcByAddress', context).addClass('Active');
                
                this.ClearErrorMessages();
                
                $('#Calculator .Form .ByMiles', context).hide();
                $('#Calculator .Form .ByAddress', context).show();
                $('#Calculator .EndSpacer', context).hide();
                $('#Calculator .Form', context).show();
                $('#Calculator .Form .StartAddress', context).focus();
                $('#Results .Inner', context).fadeTo(0, 0.45);
            }
        }
        else
        {
            if ($('#Calculator .Form .ByMiles', context).is(':hidden'))
            {
                $('#CalcByAddress', context).removeClass('Active');
                $('#CalcByMiles', context).addClass('Active');
                
                this.ClearErrorMessages();
                
                $('#Calculator .Form .ByAddress', context).hide();
                $('#Calculator .Form .ByMiles', context).show();
                $('#Calculator .EndSpacer', context).show();
                $('#Calculator .Form', context).show();
                $('#Calculator .Form .TripDistance', context).focus();
                $('#Results .Inner', context).fadeTo(0, 0.45);
            }
        }
    },
    ValidateForm : function(callback)
    {
        var __app = this;
        var context = $(this.Selector);
        
        var isValid = true;
        var i, html, val;
        var errorMessages = [];
        
        var selector = '#Calculator .Form ';
        
        this.ClearErrorMessages();
        
        var requireNumber = function(fieldSelector, errorMessage, minValue, maxValue)
        {
            val = $(selector + fieldSelector, context).val();
            
            minValue = isNaN(minValue) ? -2147483648 : minValue;
            maxValue = isNaN(maxValue) ?  2147483648 : maxValue;
            
            if (isNaN(val) || val === '' || val < minValue || val > maxValue)
            {
                isValid = false;
                errorMessages[errorMessages.length] = errorMessage;
                $(selector + fieldSelector, context).
                    next('.ErrorIcon').show().
                    attr('title', errorMessage);
            }
        };
        
        var requireValue = function(fieldSelector, errorMessage)
        {
            val = $(selector + fieldSelector, context).val();
            if (!val || val === '')
            {
                isValid = false;
                errorMessages[errorMessages.length] = errorMessage;
                $(selector + fieldSelector, context).
                    next('.ErrorIcon').show().
                    attr('title', errorMessage);
            }
        };
        
        if (__app.IsByAddress)
        {
            requireValue('input.StartAddress', 'Please enter a starting street address');
            requireValue('input.EndAddress', 'Please enter an ending street address');
        }
        else
        {
            requireNumber('input.TripDistance', 'Please enter the trip distance',   0.001);
        }
        
        requireNumber('input.CostPerGallon', 'Please enter your cost per gallon',   0.001);
        requireNumber('input.MilesPerGallon', 'Please enter your miles per gallon', 0.001);
        requireNumber('input.TripFrequency', 'Please enter the number of trips',    1.00);
        requireNumber('select.TripFrequencySelection', 'Trip frequency selection must be set');
        requireNumber('input.TripType:checked', 'Trip type must be selected');
        
        if (!isValid)
        {
            //this.ClearResults();
            $('#Results .Inner', context).fadeTo(0, 0.45);
        }
        
        if (__app.IsFunction(callback))
        {
            callback(isValid);
        }
        
        if (!isValid)
        {
            this.ShowErrorMessages(errorMessages);
        }
        
        return isValid;
    },
    ClearErrorMessages : function()
    {
        var __app = this;
        var context = $(this.Selector);
        
        $('#Calculator .Errors', context).hide().empty();
        $('.ErrorIcon', context).hide();
    },
    ShowErrorMessages : function(errorMessages)
    {
        var __app = this;
        var context = $(this.Selector);
        var html, i;
        
        html = '<ul>';        
        for (i=0;i<errorMessages.length;i++)
        {
            html += '<li>' + errorMessages[i] + '</li>';
        }
        html += '</ul>';
        $('#Calculator .Errors', context).css('display', '');
        $('#Calculator .Errors', context).html(html);
    },
    UpdateResults : function()
    {
        var __app = this;
        var context = $(this.Selector);
        
        var isValid = this.ValidateForm();
        
        if (!isValid)
        {
            return;
        }
        
        var tripDistance, costPerGallon, milesPerGallon, 
            tripFrequency, tripFrequencySelection, tripType;
        var tripDistanceResult, totalMilesResult = totalCostResult = costPerMileResult =
            yearlyCostResult = monthlyCostResult = weeklyCostResult = 
            dailyCostResult = 0;
        var tripDistanceSuffix = ' miles';
        var totalMilesSuffix = ' miles';
        
        var selector = '#Calculator .Form ';
        
        if (this.IsByAddress)
        {
            // get the trip distance from the object context
            tripDistance = this.TripDistance;
        }
        else
        {
            tripDistance = parseFloat($('#Calculator input.TripDistance', context).val());
        }
        
        costPerGallon = parseFloat($(selector + 'input.CostPerGallon', context).val());
        milesPerGallon = parseFloat($(selector + 'input.MilesPerGallon', context).val());
        tripFrequency = parseFloat($(selector + 'input.TripFrequency', context).val());
        tripFrequencySelection = $(selector + 'select.TripFrequencySelection', context).val();
        tripType = $(selector + 'input.TripType:checked', context).val();
        
        // validate the values
        
        // cost per mile (cpm) = totalMilesResult / milesPerGallon
        // year cost = miles * cpm * 365
        // monthly cost = miles * cpm * 30
        // weekly cost = miles * cpm * 7
        // daily cost = miles * cpm * 1
        
        if (tripType === '1' && this.IsByAddress)
        {
            tripDistanceResult = tripDistance;
            totalMilesResult = tripDistance;
            totalMilesSuffix = ' miles (one way)';
        }
        else if (!this.IsByAddress)
        {
            tripDistanceResult = tripDistance;
            totalMilesResult = tripDistance;
            totalMilesSuffix = ' miles (total)';
        }
        else
        {
            // if it is by address and a round trip (double the total miles)
            tripDistanceResult = tripDistance;
            totalMilesResult = tripDistance * 2;
            totalMilesSuffix = ' miles (round trip)';
        }
        
        // tripFrequencySelection (string) uses tripFrequency (int)
        // 1 = Day
        // 2 = Week
        // 3 = Month
        // 4 = Year
        
        costPerMileResult = costPerGallon / milesPerGallon;
        totalCostResult = costPerMileResult * totalMilesResult;
        
        if (tripFrequencySelection === '1') // day
        {
            yearlyCostResult = totalCostResult * tripFrequency * 365;
        }
        else if (tripFrequencySelection === '2') // week
        {
            yearlyCostResult = totalCostResult * tripFrequency * 52;
        }
        else if (tripFrequencySelection === '3') // month
        {
            yearlyCostResult = totalCostResult * tripFrequency * 12;
        }
        else if (tripFrequencySelection === '4') // year
        {
            yearlyCostResult = totalCostResult * tripFrequency;
        }
        else
        {
            yearlyCostResult = 1;
            monthlyCostResult = 0;
            weeklyCostResult = 0;
            dailyCostResult = 0;
        }
        
        if (yearlyCostResult === 0)
        {
            // this is an error!
            yearlyCostResult = 1;
        }
        
        monthlyCostResult = yearlyCostResult / 12;
        weeklyCostResult = yearlyCostResult / 52;
        dailyCostResult = yearlyCostResult / 365;
        
        // account for frequency of trip
        
        var format = this.FormatCurrency;
        
        $('#TripDistanceResult', context).text(tripDistanceResult.toFixed(2) + tripDistanceSuffix);
        $('#TotalMilesResult', context).text(totalMilesResult.toFixed(2) + totalMilesSuffix);
        $('#TotalCostResult', context).text(format(totalCostResult));
        $('#CostPerMileResult', context).text(format(costPerMileResult));
        $('#YearlyCostResult', context).text(format(yearlyCostResult));
        $('#MonthlyCostResult', context).text(format(monthlyCostResult));
        $('#WeeklyCostResult', context).text(format(weeklyCostResult));
        $('#DailyCostResult', context).text(format(dailyCostResult));
        
        $('#Results .Inner', context).fadeTo(25, 1.0);
        
        if ($('#GasTips').is(':hidden'))
        {
            $('#GasTips').slideDown(500);
        }
    },
    ClearResults : function()
    {
        var __app = this;
        var context = $(this.Selector);
        
        $('#TripDistanceResult', context).text('');
        $('#TotalMilesResult', context).text('');
        $('#TotalCostResult', context).text('');
        $('#CostPerMileResult', context).text('');
        $('#YearlyCostResult', context).text('');
        $('#MonthlyCostResult', context).text('');
        $('#WeeklyCostResult', context).text('');
        $('#DailyCostResult', context).text('');
    },
    FetchDistance : function(callback)
    {
        var __app = this;
        var context = $(this.Selector);
        
        var isValid = this.ValidateForm();
        
        if (!isValid)
        {
            return;
        }

        var from = $('#Calculator input.StartAddress', context).val();
        var to = $('#Calculator input.EndAddress', context).val();
        
        // See: http://code.google.com/apis/maps/documentation/reference.html#GDirections
        var gdir = new google.maps.Directions();

        var onLoad = function()
        {
            var distance = gdir.getDistance();
            // convert meters to miles
            var miles = distance.meters * 0.000621371192;
            //var formattedMiles = miles.toFixed(2);
            
            __app.TripDistance = miles;
            
            // set the miles if it is not already defined
            var tripDistance = $('#Calculator input.TripDistance', context).val();
            if (tripDistance === '')
            {
                $('#Calculator input.TripDistance', context).val(miles.toFixed(2));
            }
            
            if (__app.IsFunction(callback))
            {
                callback();
            }
        };
        
        var onError = function()
        {
            var status = gdir.getStatus();
            var errorMessage;
            if (status.code == G_GEO_UNKNOWN_ADDRESS)
            {
                //errorMessage = 'No corresponding geographic location could be found for one of the specified addresses. This may be due to the fact that the address is relatively new, or it may be incorrect.';
                errorMessage = 'One or both of the addresses provided is not recognized.';
            }
            else if (status.code == G_GEO_SERVER_ERROR)
            {
                errorMessage = 'A geocoding or directions request could not be successfully processed, yet the exact reason for the failure is not known.';
            }
            else if (status.code == G_GEO_MISSING_QUERY)
            {
                errorMessage = 'The HTTP q parameter was either missing or had no value. For geocoder requests, this means that an empty address was specified as input. For directions requests, this means that no query was specified in the input.';
            }
            else if (status.code == G_GEO_BAD_KEY)
            {
                errorMessage = 'The given key is either invalid or does not match the domain for which it was given.';
            }
            else if (status.code == G_GEO_BAD_REQUEST)
            {
                errorMessage = 'A directions request could not be successfully parsed.';
            }
            else 
            {
                errorMessage = 'Address is not recognized.';
            }
            
            if (errorMessage)
            {
                
                __app.ShowErrorMessages([errorMessage]);
                
                $('.ByAddress input[type="text"]', context).
                    next('.ErrorIcon').show().
                    attr('title', errorMessage);
            }
            
            $('#Calculator input.StartAddress', context).
                next('.ErrorIcon').show().
                attr('title', errorMessage);
            $('#Calculator input.EndAddress', context).
                next('.ErrorIcon').show().
                attr('title', errorMessage);
        };
        
        GEvent.addListener(gdir, "load", onLoad);
        GEvent.addListener(gdir, "error", onError);
        gdir.load('from: ' + from + ' to: ' + to, {getSteps: true});
    },
    FormatCurrency : function (number)
    {
        var strValue = number + '';
        strValue = strValue.toString().replace(/\$|\,/g,'');
        dblValue = parseFloat(strValue);

        blnSign = (dblValue == (dblValue = Math.abs(dblValue)));
        dblValue = Math.floor(dblValue*100+0.50000000001);
        intCents = dblValue%100;
        strCents = intCents.toString();
        dblValue = Math.floor(dblValue/100).toString();
        if (intCents<10)
        {
            strCents = "0" + strCents;
        }
        for (var i = 0; i < Math.floor((dblValue.length-(1+i))/3); i++)
        {
            dblValue = dblValue.substring(0,dblValue.length-(4*i+3))+','+
            dblValue.substring(dblValue.length-(4*i+3));
        }
        return (((blnSign)?'':'-') + '$' + dblValue + '.' + strCents);
    }

};
