/*
*   Library for Dealer Locator: 
*   Search functionality is based on past implementation (so don't blame on me about code quality, etc...).
*   Search results functionality has been fully refactored 
*   @refactored 1/06/10:  Andrea Ottolina
*/

var DealerLocator = (function() {


    // Private variables
    var dealers = [],
        dealersFound = [],
        map,
        search,
        isIE6,

    // Hook
        self = {},

    // Dealer Class declaration
        Dealer = function(data) {
            var self = {},
                id,
                name,
                siteid,
                sitename,
                address1,
                address2,
                town,
                zipcode,
                telephone,
                fax,
                email,
                openingTimes = [],
                services = [],
                serviceNumbers = [],
                campaignPhoneNumbers = [],
                geoPos,
                salesrating,
                servicerating,
                distance = null,
                pushpinid,
                index = 10,
                init = (function() {
                    id = data.DealerId;
                    name = data.DealerName;
                    siteid = data.SiteId;
                    sitename = data.SiteName;
                    address1 = data.Address1;
                    address2 = data.Address2;
                    town = data.Town;
                    zipcode = data.ZipCode;
                    telephone = data.Telephone;
                    fax = data.Fax;
                    email = data.EmailAddress;
                    geoPos = GeoPoint({ latitude: data.Latitude, longitude: data.Longitude });
                    salesrating = data.SalesRating;
                    servicerating = data.ServiceRating;
                    for (var i = 0; i < data.OpeningTimes.length; i++) {
                        var tempOpeningTime = data.OpeningTimes[i];
                        openingTimes.push(tempOpeningTime);
                    }
                    for (var i = 0; i < data.Services.length; i++) {
                        var tempService = Service(data.Services[i]);
                        services.push(tempService);
                    }
                    for (var i = 0; i < data.ServiceNumbers.length; i++) {
                        var tempServiceNumber = ServiceNumber(data.ServiceNumbers[i]);
                        serviceNumbers.push(tempServiceNumber);
                    }
                    for (var i = 0; i < data.PhoneNumbers.length; i++) {
                        var tempPhoneNumber = CampaignPhoneNumber(data.PhoneNumbers[i]);
                        campaignPhoneNumbers.push(tempPhoneNumber);
                    }
                } ());

            //Public Methods
            self.getId = function() {
                return id;
            };
            self.getSiteId = function() {
                return siteid;
            };
            self.getName = function() {
                return sitename;
            };
            self.getAddress1 = function() {
                return address1;
            };
            self.getAddress2 = function() {
                return address2;
            };
            self.getFullAddress = function() {
                return address1 + ((address1 != '' && address2 != '') ? ', ' : '') + address2;
            };
            self.getTown = function() {
                return town;
            };
            self.getZipcode = function() {
                return zipcode;
            };
            self.getTelephone = function() {
                return telephone;
            };
            self.getFax = function() {
                return fax;
            };
            self.getEmail = function() {
                return email;
            };
            self.getOpeningTimesWeek = function() {
                if (openingTimes.length > 0) {
                    return openingTimes[0].Hours;
                }
                return 'Not Available';
            };
            self.getOpeningTimesSat = function() {
                if (openingTimes.length > 0) {
                    return openingTimes[5].Hours;
                }
                return 'Not Available';
            };
            self.getOpeningTimesSun = function() {
                if (openingTimes.length > 0) {
                    return openingTimes[6].Hours;
                }
                return 'Not Available';
            };
            self.getPosition = function() {
                return geoPos.getPoint();
            };
            self.getSalesRating = function() {
                return salesrating;
            };
            self.getServiceRating = function() {
                return servicerating;
            };
            self.getDistance = function() {
                return distance;
            };
            self.getPushPinID = function() {
                return pushpinid;
            };
            self.getIndex = function() {
                return index;
            };

            // Calculate the distance from a given point
            self.distanceFromGeoPoint = function(targetPoint) {
                return Utils.getDistanceBetweenPoints(self.getPosition(), targetPoint, 'miles');
            };

            // Calculate the distance from a given point and store it
            self.setDistanceFromGeoPoint = function(targetPoint) {
                distance = self.distanceFromGeoPoint(targetPoint);
                return self.getDistance();
            };

            // Pass an array of services and returns if the dealer has all of them or not
            self.hasServices = function(requiredServices) {
                var returnValue = true,
                    requiredServices = requiredServices || [];
                if (requiredServices.length > 0) {

                    for (var i = 0; i < requiredServices.length; i++) {
                        var serviceFound = false;
                        for (var j = 0; j < services.length; j++) {
                            if (requiredServices[i].getId().toLowerCase() === services[j].getId().toLowerCase()) {
                                serviceFound = true;
                                break;
                            }
                        }
                        if (!serviceFound) {
                            returnValue = false;
                        }
                    }
                }
                return returnValue;
            };

            //Return service names
            self.getServices = function() {
                return services;
                /*
                returnValue = [];
                for (var i = 0; i < services.length; i++) {
                returnValue.push(services[i].getName());
                }
                return returnValue;
                */
            };

            self.getServiceNumbers = function() {
                return serviceNumbers;
            };

            self.getCampaignPhoneNumbers = function() {
                return campaignPhoneNumbers;
            };

            self.setPushPinID = function(id) {
                pushpinid = id;
            };

            self.setIndex = function(value) {
                index = value;
            };

            return self;

        },

    // DealerList Class declaration
    /*
    DealerList = function (dealers) {
    var self = {},
    list = [],
    init = (function () {
    list = dealers;
    }());
                
    self.addDealer = function (dealer) {
    list.push(dealer);
    };
            
    self.getDealerAtIndex = function (index) {
    return list[index];
    };
            
    self.isNotEmpty = function () {
    var returnValue = false;
    if (list.length > 0) {
    returnValuer = true;
    }
    return returnValue;
    };
            
    return self;
    },
    */

    // PlaceDisambiguation Class declaration
    PlaceDisambiguation = function(name, latitude, longitude) {
        return {
            Name: name,
            LatLong: {
                Latitude: latitude,
                Longitude: longitude
            }
        };
    },



    // Service Class declaration
    Service = function(data) {
        var self = {},
                id,
                name,
                init = (function() {
                    id = String(data.ID);
                    name = String(data.Name);
                } ());

        self.getId = function() {
            //return id.toLowerCase();
            return id;
        };

        self.getName = function() {
            return name;
        };

        return self;

    },

        ServiceNumber = function(data) {
            var self = {},
                name,
                telephone,
                init = (function() {
                    name = String(data.Name);
                    telephone = String(data.Telephone);
                } ());

            self.getName = function() {
                return name;
            };

            self.getTelephone = function() {
                return telephone;
            };

            return self;

        },

        CampaignPhoneNumber = function(data) {
            var self = {},
                campaignId,
                telephone,
                init = (function() {
                    campaignId = String(data.Identifier);
                    telephone = String(data.Number);
                } ());

            self.getCampaignId = function() {
                return campaignId;
            };

            self.getTelephone = function() {
                return telephone;
            };

            return self;
        },

    // GeoPoint Class declaration
        GeoPoint = function(data) {
            var self = {},
                geoCoordinates = {
                    latitude: null,
                    longitude: null
                },
                init = (function() {
                    geoCoordinates.latitude = Number(data.latitude);
                    geoCoordinates.longitude = Number(data.longitude);
                } ());

            self.getPoint = function() {
                return geoCoordinates;
            };

            self.getLatitude = function() {
                return geoCoordinates.latitude;
            };

            self.getLongitude = function() {
                return geoCoordinates.longitude;
            };

            return self;
        },

    // Map Class declaration
        Map = function(container) {
            var self = {},
                mapObj,
                defaultZoomLevel = 10,
                pushPinsOnMap = [],
                init = (function() {
                    // Initialize map on UK
                    mapObj = new VEMap(container);
                    mapObj.SetDashboardSize(VEDashboardSize.Small);
                    mapObj.LoadMap(new VELatLong(55, -3), 5);
                } ());

            self.getMap = function() { return mapObj; }

            self.setCenter = function(geoPoint) {
                mapObj.SetCenter(new VELatLong(geoPoint.latitude, geoPoint.longitude));
            };

            self.setSizeBasedOnPushPins = function() {
                mapObj.SetMapView(pushPinsOnMap);
            };

            self.addPinWithMessage = function(geoPoint, index, title, description, zindex, type) {
                // if no type ('big') is passed it uses the small one by default
                var pushPinIcon = Utils.customPushPinIconWithNumber(index + 1, type),
                    pushPinPosition = new VELatLong(geoPoint.latitude, geoPoint.longitude),
                    pushPin = new VEShape(VEShapeType.Pushpin, pushPinPosition);

                pushPin.SetCustomIcon(pushPinIcon);
                pushPin.SetTitle(title);
                pushPin.SetDescription(description);

                mapObj.AddShape(pushPin);

                pushPinsOnMap.push(pushPin);

                return pushPin.GetID();
            };

            self.attachClickOnPins = function() {
                mapObj.AttachEvent('onclick', function(e) {
                    if (e.elementID != null) {
                        var clickedShape = mapObj.GetShapeByID(e.elementID).GetID();
                        Page.displayMainDealer(Utils.findDealerWithPushPinID(clickedShape));
                    }
                });
            };

            self.setPushPinBigAndResetOthers = function(pushpinid) {
                for (var i = 0; i < pushPinsOnMap.length; i++) {
                    var type = '',
                        defaultZIndex = 1000,
                        pushPin = pushPinsOnMap[i];
                    if (pushPin.GetID() === pushpinid) {
                        type = 'big';
                        defaultZIndex = 1001;
                    }
                    pushPin.SetCustomIcon(Utils.customPushPinIconWithNumber(i + 1, type));
                    pushPin.SetZIndex(defaultZIndex);
                }
            };

            self.findPushPinByID = function(id) {
                return mapObj.GetShapeByID(id);
            };

            self.showRoute = function(searchFrom, dealer) {
                var dealerPosition = dealer.getPosition();
                routePath = [searchFrom, new VELatLong(dealerPosition.latitude, dealerPosition.longitude)];
                routeOptions = new VERouteOptions();
                routeOptions.DrawRoute = true;
                routeOptions.SetBestMapView = true;
                routeOptions.RouteColor = new VEColor(255, 0, 0, 1);
                routeOptions.RouteCallback = Page.displayRoute;
                routeOptions.DistanceUnit = VERouteDistanceUnit.Mile;
                routeOptions.ShowErrorMessages = false;
                routeOptions.ShowDisambiguation = false;
                routeOptions.RouteWeight = 4;
                mapObj.GetDirections(routePath, routeOptions);
            };

            self.deleteRoute = function() {
                mapObj.DeleteRoute();
            };

            self.findLocation = function() {
                if (search.getText().toLowerCase() === 'oldbury' || search.getText().toLowerCase() === 'hayes') {
                    self.ShowCannedResultsDisambiguation(search.getText().toLowerCase());
                    return;
                }
                var searchFor = Utils.formatPostCode(search.getText()),
                    searchRegion = search.getRegion();
                mapObj.Find(null,                           // what
                      searchFor + ', ' + searchRegion, // where
                      null, // VEFindType (always VEFindType.Business, relates to what search)
                      null, // VEShapeLayer
                      null, // start index for results (0 by default)
                      null, // max number of results (default = 10, range 0 - 20)
                      false, // show results (default is true)
                      null, // create pushpin for what results
                      false, // use default disambiguation (default is true)
                      null, // set best map view (default is true)
                      self.onFoundResults
                );
            };

            self.ShowCannedResultsDisambiguation = function(searchLocation) {
                var geoPoints;
                var locationStrings;

                if (searchLocation === 'oldbury') {
                    // Oldbury, sandwell
                    var geoPoint = GeoPoint({ latitude: new Number(52.501212), longitude: new Number(-2.015496) });

                    // Oldbury upon severn, South Gloucestershire
                    var geoPoint2 = GeoPoint({ latitude: new Number(51.6446978), longitude: new Number(-2.576277) });

                    // Oldbury, bridgnorth
                    var geoPoint3 = GeoPoint({ latitude: new Number(52.524), longitude: new Number(-2.43) });

                    geoPoints = [geoPoint, geoPoint2, geoPoint3];

                    var locationString = 'Oldbury, Sandwell' + ', ' + search.getRegion();
                    var locationString2 = 'Oldbury upon Severn' + ', ' + search.getRegion();
                    var locationString3 = 'Oldbury, Bridgnorth' + ', ' + search.getRegion();

                    locationStrings = [locationString, locationString2, locationString3];
                } else if (searchLocation === 'hayes') {
                    // Hayes, Greater London (north west)
                    var geoPoint = GeoPoint({ latitude: new Number(51.5105), longitude: new Number(-0.4189) });

                    // Hayes, Bromley (Kent)
                    var geoPoint2 = GeoPoint({ latitude: new Number(51.376294791698456), longitude: new Number(0.010803937911987305) });

                    // The Hayes, wales
                    var geoPoint3 = GeoPoint({ latitude: new Number(51.479), longitude: new Number(-3.1758) });

                    geoPoints = [geoPoint, geoPoint2, geoPoint3];

                    var locationString = 'Hayes, Greater London' + ', ' + search.getRegion();
                    var locationString2 = 'Hayes, Bromley, Kent' + ', ' + search.getRegion();
                    var locationString3 = 'The Hayes' + ', ' + search.getRegion();

                    locationStrings = [locationString, locationString2, locationString3];
                }

                var geoPoint;
                var validResults = [];
                for (var i = 0; i < geoPoints.length; i++) {
                    geoPoint = geoPoints[i];
                    if (Utils.isValidLocation(geoPoint) && locationStrings[i].toLowerCase() !== search.getRegion().toLowerCase()) {
                        var place = new PlaceDisambiguation(locationStrings[i], geoPoint.getLatitude(), geoPoint.getLongitude());

                        validResults.push(place);
                    }
                }

                if ((validResults.length != 0) && (validResults.length != 1)) {
                    Page.displayDisambiguation(validResults);
                }
            }

            self.onFoundResults = function(shapeLayer, findResults, e, moreResults, errorMsg) {
                var geoPoint,
                    validResults = [];
                if (e !== null) {
                    for (var i = 0; i < e.length; i++) {
                        geoPoint = GeoPoint({ latitude: e[i].LatLong.Latitude, longitude: e[i].LatLong.Longitude });
                        if (Utils.isValidLocation(geoPoint) && e[i].Name.toLowerCase() !== search.getRegion().toLowerCase()) {
                            validResults.push(e[i]);
                        }
                    }
                    if (validResults.length === 0) {
                        Utils.alert('noResults');
                    } else if (validResults.length === 1) {
                        geoPoint = GeoPoint({ latitude: validResults[0].LatLong.Latitude, longitude: validResults[0].LatLong.Longitude });
                        search.setLocation(geoPoint);
                        Utils.redirectToResults();
                    } else {
                        Page.displayDisambiguation(validResults);
                    }
                } else {
                    Utils.alert('noResults');
                }
            };

            return self;
        },

    // SearchData Class declaration
        SearchData = function(searchText, searchType, searchRegion, searchServices, searchGeoPoint) {
            var self = {},
                text = '',
                type,
                region = '',
                services = [],
                location,
                init = (function() {
                    text = searchText;
                    if (searchType === 'searchByLocationRadioButton' || searchType === 1 || searchType === '1') {
                        type = 1;
                    } else if (searchType === 'searchByNameRadioButton' || searchType === 2 || searchType === '2') {
                        type = 2;
                    } else if (searchType === 'dealerchanged' || searchType === 3 || searchType === '3') {
                        type = 3;
                    } else if (searchType === 'deeplink_loc' || searchType === 4 || searchType === '4') {
                        type = 4;
                    } else if (searchType === 'listOnly' || searchType === 5 || searchType === '5') {
                        type = 5;
                    }
                    region = searchRegion;
                    if (Boolean(searchServices && searchServices.jquery)) { // means that it comes from searchpage
                        for (var i = 0; i < searchServices.length; i++) {
                            var tempService = Service({ ID: searchServices.eq(i).attr('ID').split('_').pop().toLowerCase(), Name: searchServices.eq(i).attr('value') });
                            services.push(tempService);
                        }
                    } else if (typeof searchServices === 'string') { // means that it comes from querystring (this should never happen)
                        searchServices = (searchServices !== '') ? searchServices.split(',') : [];
                        for (var i = 0; i < searchServices.length; i++) {
                            // don't necessarily need a Name for the object
                            var tempService = Service({ ID: searchServices[i] });
                            services.push(tempService);
                        }
                    } else { // this is when initialized by the search results page (an Array of Services is generated and passed to this object constructor)
                        services = searchServices;
                    }
                    location = searchGeoPoint;
                } ());

            self.getText = function() {
                return text;
            };

            self.getType = function() {
                var returnType = '';
                if (type === 1) {
                    returnType = 'location';
                } else if (type === 2) {
                    returnType = 'name';
                } else if (type === 3) {
                    returnType = 'dealerchanged';
                } else if (type === 4) {
                    returnType = 'deeplink_loc'
                } else if (type === 5) {
                    returnType = 'listOnly'
                }
                return returnType;
            };

            self.getNumericType = function() {
                return type;
            };

            self.getRegion = function() {
                return region;
            };

            self.getServices = function() {
                return services;
            };

            self.getServicesId = function() {
                var returnServices = [];
                for (var i = 0; i < services.length; i++) {
                    returnServices.push(services[i].getId());
                }
                return returnServices;
            };

            self.getServicesName = function() {
                var returnServices = [];
                for (var i = 0; i < services.length; i++) {
                    returnServices.push(services[i].getName());
                }
                return returnServices;
            };

            self.setLocation = function(geoPoint) {
                location = geoPoint;
            };

            self.getLocation = function() {
                return location;
            };

            return self;
        },

    // Utility methods
        Utils = {
            alert: function(messageCode) {
                var messageToDisplay = 'Something went wrong...';
                switch (messageCode) {
                    case 'noDealersFound':
                        messageToDisplay = "Sorry! No dealers found matching specified criteria";
                        break;
                    case 'browserNotSupported':
                        messageToDisplay = "Sorry! The search doesn't work with this browser";
                        break;
                    case 'locationInvalid':
                        messageToDisplay = "Sorry! The location is outside UK boundaries";
                        break;
                    case 'dealerDataError':
                        messageToDisplay = "Sorry! There's an error retrieving Dealer's data";
                        break;
                    case 'tooManyDealers':
                        messageToDisplay = "Sorry! We found more than one dealer but only one will be displayed on the map";
                        break;
                    case 'noDealersWithName':
                        messageToDisplay = "Sorry! No dealer names matching your search were found, please try again.";
                        break;
                    case 'noResults':
                        messageToDisplay = "Sorry! No results. Please try a different search or check the spelling.";
                        break;
                    case 'outsideBoundaries':
                        messageToDisplay = "Sorry! No results inside allowed boundaries.";
                        break;
                    case 'postcodeInvalid':
                        messageToDisplay = "Sorry! The postcode supplied is invalid.";
                        break;
                    case 'generalError':
                        messageToDisplay = "Sorry! An error occurred. Please search again.";
                        break;
                }
                window.alert(messageToDisplay);
            },
            bookServicePopup: function(url) {
                this.popup = null;
                var width = 800,
                    height = 620,
                    lPos = (screen.width) ? (screen.width - width) / 2 : 0,
                    tPos = (screen.height) ? (screen.height - height) / 2 : 0;
                this.popup = window.open(url, 'bookService', 'height=' + height + ',width=' + width + ',top=' + tPos + ',left=' + lPos + ',scrollbars=yes');
            },
            buildResultLink: function(searchType, geoPoint) {
                /* 
                *   searchType = 0, single dealer found, redirect to dealer info page
                *   otherwise it gets the type from SearchData
                */
                var returnLink = '/dealerlocator/',
                    redirectUrl = '',
                    type = (searchType === 0) ? searchType : search.getNumericType(),
                    id = '',
                    siteId = '',
                    lat = '',
                    lon = '',
                    serviceId = search.getServicesId().toString(),
                    serviceName = search.getServicesName().toString(),
                    q = search.getText();

                switch (type) {
                    case 0:
                        redirectUrl = 'DealerInformation.aspx';
                        id = dealersFound[0].getId();
                        siteId = dealersFound[0].getSiteId();
                        break;
                    case 1:
                        redirectUrl = 'SearchResults.aspx';
                        lat = (geoPoint) ? geoPoint.getLatitude() : search.getLocation().getLatitude();
                        lon = (geoPoint) ? geoPoint.getLongitude() : search.getLocation().getLongitude();
                        break;
                    case 2:
                        redirectUrl = 'SearchResults.aspx';
                        break;
                    case 5:
                        returnLink = '/500byGucci/';
                        redirectUrl = 'dealerlocator.aspx';
                        lat = search.getLocation().getLatitude();
                        lon = search.getLocation().getLongitude();
                        break;
                }
                returnLink += redirectUrl + '?' +
                                'type=' + type +
                                '&' + 'q=' + Utils.encode(q) +
                                '&' + 'id=' + id +
                                '&' + 'siteId=' + siteId +
                                '&' + 'lat=' + lat +
                                '&' + 'lon=' + lon +
                                '&' + 'serviceId=' + serviceId +
                                '&' + 'serviceName=' + Utils.encode(serviceName);
                return returnLink.toLowerCase();
            },
            customPushPinIconWithNumber: function(number, type) {
                var type = (type == null || type == undefined) ? '' : type;
                number = (number == null || number == undefined) ? '' : number,
                    img = '',
                    format = '';

                switch (type) {
                    case 'big': img = 'imgMapFlagBig';
                        break;
                    case 'green': img = 'imgMapFlagGreenSmall';
                        break;
                    default: img = 'imgMapFlagSmall';
                        break;
                }
                format = (Utils.isIE6()) ? '-ie6' : '';

                return '<img src="/library/images/dealerlocator/' + img + format + '.png" class="flag ' + type + '" /><span class="flagNumber ' + type + '">' + number + '</span>'

                // Can't use bg images because they don't get printed
                //return '<div class="flag ' + type + '"><p>' + number + '</p></div>';
            },
            decode: function(string) {
                return decodeURIComponent(string);
            },
            encode: function(string) {
                return encodeURIComponent(string);
            },
            findDealerWithPushPinID: function(id) {
                for (var i = 0; i < dealersFound.length; i++) {
                    if (id === dealersFound[i].getPushPinID()) {
                        return dealersFound[i];
                    }
                }
            },
            formatPostCode: function(postcode) {
                var value = postcode;
                if (value.match("([Gg][Ii][Rr] 0[Aa][Aa]|[A-Pa-pR-Ur-uWwYyZz]([0-9]{1,2}|([A-Ha-hK-Yk-y][0-9]|[A-Ha-hK-Yk-y][0-9]([0-9]|[AaBbEeHhMmNnPpRrV-Yv-y]))|[0-9][A-Ha-hJjKkS-Us-uWw]) *[0-9][AaBbD-Hd-hJjLlNnP-Up-uW-Zw-z]{2})$")) {
                    value = value.substr(0, value.length - 3) + " " + value.substr(value.length - 3, 3);
                }
                return value;
            },
            getRadiantFromDegrees: function(degrees) {
                return degrees * Math.PI / 180;
            },
            getDistanceBetweenPoints: function(point1, point2, units) {
                var units = units || 'km';
                var R = 6371; // earth's mean radius in km
                var d = Math.acos(Math.sin(Utils.getRadiantFromDegrees(point1.latitude)) * Math.sin(Utils.getRadiantFromDegrees(point2.latitude)) +
                        Math.cos(Utils.getRadiantFromDegrees(point1.latitude)) * Math.cos(Utils.getRadiantFromDegrees(point2.latitude)) * Math.cos(Utils.getRadiantFromDegrees(point2.longitude) -
                        Utils.getRadiantFromDegrees(point1.longitude))) * R;
                if (units === 'miles') {
                    d = d * 0.621371192
                }
                return d;
            },
            getQuerystringValue: function(key, default_) {
                if (default_ == null) default_ = "";
                key = key.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]").toLowerCase();
                var regex = new RegExp("[\\?&]" + key + "=([^&#]*)");
                var qs = regex.exec(window.location.href.toLowerCase());
                if (qs == null) {
                    return default_;
                } else {
                    return qs[1];
                }
            },
            isCloserThanFound: function(dealerFound, dealerNewDistance) {
                var returnValue = true;
                try {
                    if (dealerFound.getDistance() < dealerNewDistance) {
                        returnValue = false;
                    }
                } catch (err) {
                    // do nothing
                }
                return returnValue;
            },
            isIE6: function() {
                if (isIE6 === undefined) {
                    if ($j.browser.msie && $j.browser.version.substr(0, 1) == "6") {
                        isIE6 = true;
                    } else {
                        isIE6 = false;
                    }
                }
                return isIE6;
            },
            isValidLocation: function(geopoint) {
                //checks if coordinates are inside the UK area
                var returnValue = false,
                    latitude = geopoint.getLatitude(),
                    longitude = geopoint.getLongitude(),
                    minLat = 49,
                    maxLat = 61,
                    minLon = -8,
                    maxLon = 2;
                if (latitude > minLat && latitude < maxLat && longitude > minLon && longitude < maxLon) {
                    returnValue = true;
                }
                return returnValue;
            },
            redirectToResults: function(searchType) {
                window.location.href = Utils.buildResultLink(searchType);
            },
            countProperties: function(obj) {
                var count = 0;
                for (var prop in obj) {
                    if (obj.hasOwnProperty(prop))
                        ++count;
                }
                return count;
            }
        },

    // Page components methods and variables
        Page = {
            currentDealer: null,
            injectDealerData: function(singleDealer, $container, options) {
                var defaults = {
                    // defaults are set as for data required in the scroller
                    showAsListItem: true,
                    showDistance: true,
                    showEmail: false,
                    showTelephone: false,
                    showOpeningTimes: false,
                    showRatings: true,
                    showContactCta: false
                },
                          options = $j.extend(true, {}, defaults, options),
                          dealerName,
                          bookServiceUrl;

                if (options.showAsListItem) {
                    dealerName = $j('<a href="#' + singleDealer.getPushPinID() + '" class="cufonHover" id="dealerCodeSiteCode-' + singleDealer.getId() + singleDealer.getSiteId() + '">' + (singleDealer.getIndex() + 1) + '. ' + singleDealer.getName() + '</a>');
                    // Get the dealercode and site code from preferred dealer usercontrol
                    var preferredDealerCode = document.getElementById("dealerCode");
                    var preferredSiteCode = document.getElementById("siteCode");
                    if (preferredDealerCode && preferredSiteCode) {
                        if (singleDealer.getId() === preferredDealerCode.value && singleDealer.getSiteId() === preferredSiteCode.value) {
                            dealerName = $j('<a href="#' + singleDealer.getPushPinID() + '" class="cufonHover preferredDealer" id="dealerCodeSiteCode-' + singleDealer.getId() + singleDealer.getSiteId() + '">' + (singleDealer.getIndex() + 1) + '. ' + singleDealer.getName() + '</a>');
                        }
                    }
                    // If the current dealer has this dealercode sitecode combination, add the preferred dealer class
                    dealerName.bind('click', { dealer: singleDealer }, function(e) {
                        e.preventDefault();
                        $j('#dealersFound li.vcard').removeClass('selected');
                        $j(this).closest('li').addClass('selected');
                        Page.displayMainDealer(e.data.dealer);
                        map.setCenter(e.data.dealer.getPosition());
                    });
                } else {
                    dealerName = singleDealer.getName();
                }

                $container.find('.fn').html(dealerName);

                if (options.showDistance) {
                    if (singleDealer.getDistance() !== null) {
                        $container.find('.distance').html(singleDealer.getDistance().toFixed(2) + ' Miles');
                    } else {
                        $container.find('.distance').remove();
                    }
                }
                $container.find('.street-address').html(singleDealer.getFullAddress());
                $container.find('.locality').html(singleDealer.getTown());
                $container.find('.postal-code').html(singleDealer.getZipcode());
                $container.find('.bPop').removeClass('ready');
                if (options.showTelephone) {
                    var telephoneNumbers = singleDealer.getServiceNumbers();
                    var telephoneNumberString = "";
                    for (var j = 0; j < telephoneNumbers.length; j++) {
                        if (telephoneNumbers[j].getName().toLowerCase() == 'sales') { //only displays Slaes Numbers - this should be refactored to pass the list of numbers in with the options
                            telephoneNumberString += telephoneNumbers[j].getTelephone();
                        }
                    }
                    $container.find('.telephone-number').html(telephoneNumberString);
                }
                if (options.showOpeningTimes) {
                    $container.find('.openingHoursWeek').html(singleDealer.getOpeningTimesWeek());
                    $container.find('.openingHoursSat').html(singleDealer.getOpeningTimesSat());
                    $container.find('.openingHoursSun').html(singleDealer.getOpeningTimesSun());
                }
                if (options.showContactCta) {
                    $container.find('.contactCta').html('<a href="mailto:' + singleDealer.getEmail() + '" title="Get in touch with ' + singleDealer.getName() + '">Get in touch</a>');
                }
                if (options.showRatings) {
                    if ($container.find('.CsiRating span').length > 0) {
                        Page.clearRatingClasses($container.find('.CsiRating span'));
                        Page.clearRatingClasses($container.find('.SsiRating span'));
                    }

                    if (Page.isDealerASalesProvider(singleDealer.getServices())) {
                        if (singleDealer.getSalesRating() <= 0) {
                            $container.find('.CsiRating span').addClass('ratingNoStar');
                            $container.find('.CsiRating span').html('Not yet rated');
                        } else {
                            $container.find('.CsiRating span').addClass('starRating' + singleDealer.getSalesRating());
                            $container.find('.CsiRating span').html('Customer rating (Sales): ' + singleDealer.getSalesRating() + ' star(s)');
                        }
                    } else {
                        $container.find('.CsiRating span').addClass('starRatingNA');
                        $container.find('.CsiRating span').html('Sales rating not applicable');
                    }

                    if (Page.isDealerAServiceProvider(singleDealer.getServices())) {
                        if (singleDealer.getServiceRating() <= 0) {
                            $container.find('.SsiRating span').addClass('ratingNoStar');
                            $container.find('.SsiRating span').html('Not yet rated');
                        } else {
                            $container.find('.SsiRating span').addClass('starRating' + singleDealer.getServiceRating());
                            $container.find('.SsiRating span').html('Customer rating (Servicing): ' + singleDealer.getServiceRating() + ' star(s)');
                        }
                    } else {
                        $container.find('.SsiRating span').addClass('starRatingNA');
                        $container.find('.SsiRating span').html('Service rating not applicable');
                    }

                    $container.find('.bPop').html('These ratings show how satisfied people were with their experience of buying a vehicle or having a vehicle serviced at this dealer. The scores are generated by our customer satisfaction survey and to be rated, each dealer must have had at least 15 interviews completed in the last 3-month period. The stars reflect an average score out of 10:<br />&bull; 5 stars - average score more than 9.26<br />&bull; 4 stars - between 9.0 and 9.25<br/>&bull; 3 stars - between 8.5 and 8.99<br />&bull; 2 stars - between 8.0 and 8.49<br />&bull; 1 star - below 7.99');
                }
                if (!options.showAsListItem) {
                    // Add details link
                    $container.find('#dealerDetailsButton').attr('href', '/dealerlocator/DealerInformation.aspx?id=' + singleDealer.getId() + '&siteid=' + singleDealer.getSiteId() + '&searchPosition=' + singleDealer.getIndex());
                    // Add contact details
                    $container.find('#dealerDetailsButton').bind('click', function(e) {
                        self.Omniture.doResultPageClickTracking({
                            'pageName': 'dealer_locator > search_results > secondary_panel : dealer_directions',
                            'eVar50': 'Dealer search:Main panel:dealer_directions',
                            'heir1': 'dealer_locator > search_results',
                            'prop11': 'Dealer Search: Dealer Details'

                        });
                    });
                    var telephoneNumbers = singleDealer.getServiceNumbers();
                    var campaignPhoneNumbers = singleDealer.getCampaignPhoneNumbers();
                    var telephoneNumberString = "";
                    var campaign;


                    //Get the campaign code from the Omniture variable
                    var omniturCampaignCookie = readCookie("s_campaign");
                    if (omniturCampaignCookie != null) {
                        campaign = omniturCampaignCookie.match(/^[a-zA-Z]*/)[0];
                    }
                    var identifier = "";

                    if (campaign == "undefined" || campaign == null) {
                        campaign = "";
                    }

                    switch (campaign.toUpperCase()) {
                        case "PPC":
                            identifier = "P";
                            break;
                        case "DSP":
                            identifier = "D";
                            break;
                        case "SM":
                        case "FB":
                            identifier = "F";
                            break;
                        case "EM":
                            identifier = "C";
                            break;
                        default:
                            identifier = "N"; //Default to Natural Search id if campaign is not defined.
                            break;
                    }

                    var campaignPhone, campaignName;

                    for (var j = 0; j < telephoneNumbers.length; j++) {
                        $container.find('.phoneLabel').show();

                        campaignName = telephoneNumbers[j].getName();
                        campaignPhone = telephoneNumbers[j].getTelephone();

                        if (campaignName.toLowerCase() == "sales") {
                            for (var i = 0; i < campaignPhoneNumbers.length; i++) {
                                if (campaignPhoneNumbers[i].getCampaignId() == identifier) {
                                    campaignPhone = campaignPhoneNumbers[i].getTelephone();
                                    break;
                                }
                            }
                        }

                        if (campaignName.toLowerCase() == "servicing") {
                            for (var i = 0; i < campaignPhoneNumbers.length; i++) {
                                if (campaignPhoneNumbers[i].getCampaignId() == "S") {
                                    campaignPhone = campaignPhoneNumbers[i].getTelephone();
                                    break;
                                }
                            }
                        }

                        telephoneNumberString += campaignPhone + " (" + campaignName + ")<br />";
                    }

                    $container.find('.phone').html(telephoneNumberString);
                    if (singleDealer.getFax().length > 0) {
                        $container.find('.faxLabel').show();
                        $container.find('.fax').html(singleDealer.getFax());
                    }
                    else {
                        $container.find('.faxLabel').hide();
                        $container.find('.fax').empty();
                    }
                    if (singleDealer.getEmail().length > 0 && options.showEmail == true) {
                        $container.find('.emailLabel').show();
                        $container.find('.email').attr('href', 'mailto:' + singleDealer.getEmail()).text(singleDealer.getEmail());
                    }
                    else {
                        $container.find('.emailLabel').hide();
                        $container.find('.email').empty();
                    }
                    // Add services details
                    if (singleDealer.getServices().length > 0) {
                        var $serviceList = $container.find('#services').empty(),
                              dealerServices = singleDealer.getServices(),
                              serviceString = '';
                        for (var j = 0; j < dealerServices.length; j++) {
                            if (dealerServices[j].getId().toLowerCase() != '500bygucci') {
                                serviceString += '<li class="' + dealerServices[j].getId() + '">' + dealerServices[j].getName() + '</li>'
                            }
                        }
                        $serviceList.append(serviceString);
                    }
                    // Add links on "set as preferred dealer" and "book a service" buttons
                    postUrl = location.pathname.toString().toLowerCase();
                    //Dirty stuff... but .Net strips out any other form tag
                    $container.find('#setAsPreferredForm').remove();
                    $form = $j('<form id="setAsPreferredForm" action="' + postUrl + '" method="post">' +
                                '<input name="id" type="hidden" value="' + singleDealer.getId() + '" />' +
                                '<input name="siteid" type="hidden" value="' + singleDealer.getSiteId() + '" />' +
                                '</form>');
                    Page.displaySetPreferredDealerButton($container, $form, singleDealer);
                    $container.find('.email').bind('click', function(e) {
                        self.Omniture.doResultPageClickTracking({
                            'pageName': 'dealer_locator > search_results > main_panel : email_dealer',
                            'eVar50': 'Dealer search:Main panel:email_dealer'
                        });
                    });
                    bookServiceUrl = '/dealerlocator/BookService.aspx' + '?id=' + singleDealer.getId() + '&siteid=' + singleDealer.getSiteId();
                    //$container.find('#bookAServiceButton').attr('href', bookServiceUrl);
                }
                return $container;
            },

            displaySetPreferredDealerButton: function(container, form, singleDealer) {
                if (Page.isDealerALoyaltyParticipant(singleDealer.getServices())) {
                    if (search.getType() === 'dealerchanged') {
                        $j('#DealerChangedMsgInvalid').hide();
                        $j('#DealerChangedMsgContinue').show();
                    }
                    container.find('#setAsPreferredButton').show().before(form).bind('click', function(e) {
                        e.preventDefault();
                        FiatCoUk.UIManager.dealerLocatorLogin(window.location.pathname + window.location.search, singleDealer.getId(), singleDealer.getSiteId());
                    });
                }
                else {
                    container.find('#setAsPreferredButton').hide();
                    if (search.getType() === 'dealerchanged') {
                        $j('#DealerChangedMsgContinue').hide();
                        $j('#DealerChangedMsgInvalid').show();
                    }
                }
            },

            isDealerALoyaltyParticipant: function(services) {
                var isMemberOfTaxonomy = false;
                services.each(function(service) {
                    if (service.getId() == "myFiatParticipatingDealer") {
                        isMemberOfTaxonomy = true;
                    }
                });
                return isMemberOfTaxonomy;
            },

            isDealerAServiceProvider: function(services) {
                var isMemberOfTaxonomy = false;
                services.each(function(service) {
                    if (service.getId() == "partsAndServicing") {
                        isMemberOfTaxonomy = true;
                    }
                });
                return isMemberOfTaxonomy;
            },

            isDealerASalesProvider: function(services) {
                var isMemberOfTaxonomy = false;
                services.each(function(service) {
                    if (service.getId() == "newCarSales") {
                        isMemberOfTaxonomy = true;
                    }
                });
                return isMemberOfTaxonomy;
            },

            clearRatingClasses: function(container) {
                if (container != 'undefined' || container != null) {
                    var cssClasses = container[0].className.split(' ');
                }
                for (var i = 0; i < cssClasses.length; i++) {
                    if (cssClasses[i].indexOf('starRating') > -1) {
                        container.removeClass(cssClasses[i]);
                    }
                }
            },

            displayDealersFound: function(dealerSelected, injectData, displayMap, displayOptions) {
                var injectData = (injectData != null) ? injectData : true;
                var displayMap = (displayMap != null) ? displayMap : false;
                if (dealersFound.length === 0) {
                    Utils.alert('noDealersFound');
                    return;
                }
                Page.displayDealersList(injectData, displayMap, displayOptions);
                if (dealerSelected != null) {
                    Page.displayMainDealer(dealersFound[dealerSelected], injectData);
                }
                if (displayMap) {
                    map.setSizeBasedOnPushPins();
                    map.attachClickOnPins();
                }
            },

            displayDealersList: function(injectData, displayMap, displayOptions) {
                var $container = $j('#dealersFound'),
                    $dealersFoundList = $j('ul', $container),
                    $dealerListItem = $dealersFoundList.find('li').eq(0).clone().remove(),
                    pinTitle,
                    pinDescription;
                $dealersFoundList.empty();
                for (var i = 0; i < dealersFound.length; i++) {
                    var $listItem = $dealerListItem.clone(),
                        $generatedItem,
                        pinID;
                    dealersFound[i].setIndex(i);
                    if (displayMap) {
                        pinTitle = (i + 1) + '. ' + dealersFound[i].getName(),
                                    pinDescription = '<p>' + dealersFound[i].getFullAddress() + ', ' + dealersFound[i].getTown() + ', ' + dealersFound[i].getZipcode() + '</p>';
                        pinID = map.addPinWithMessage(dealersFound[i].getPosition(), i, pinTitle, pinDescription);
                        dealersFound[i].setPushPinID(pinID);
                    }
                    if (injectData) {
                        $generatedItem = Page.injectDealerData(dealersFound[i], $listItem, displayOptions);
                        $dealersFoundList.append($generatedItem);
                    }

                }
                Page.refreshDynamicContent();
                $container.show();
            },

            displayDisambiguation: function(places) {
                var $searchDisambiguate = $j('#searchDisambiguate').empty(),
                    message = '<p>Please select which <strong>' + search.getText() + '</strong> you mean:</p>',
                    list = '<ul>';
                for (var i = 0; i < places.length; i++) {
                    var geoPoint = GeoPoint({ latitude: places[i].LatLong.Latitude, longitude: places[i].LatLong.Longitude });
                    list += '<li><a href="' + Utils.buildResultLink(null, geoPoint) + '">' + places[i].Name + '</a></li>'
                }
                list += '</ul>';
                $searchDisambiguate.append(message + list).show();
            },

            displayMainDealer: function(dealer, injectData) {
                var injectData = (injectData != null) ? injectData : true,
                    $container = $j('#dealerDetailsBox'),
                    $supportCopy = $j('#supportCopy');
                Page.currentDealer = dealer;
                if (injectData) {
                    Page.injectDealerData(dealer, $container, { showAsListItem: false });
                }
                map.setPushPinBigAndResetOthers(dealer.getPushPinID());
                Page.displayRoutePlanner();
                Page.removeRoute();
                Page.refreshDynamicContent();
                $container.show();
                $supportCopy.hide();
                // Uncomment if needed, but it keeps the info bubble fixed onscreen while moving
                // map.setCenter(dealer.getPosition());
            },

            displayRoute: function(route) {
                var leg,
                    turns,
                    steps = '',
                    $container = $j('#dealerRoutePlannerResults').hide(),
                    $list = $j('ul', $container).empty();
                if (route != null && route.RouteLegs.length > 0) {
                    // you can't have more than one leg
                    leg = route.RouteLegs[0];
                    turns = leg.Itinerary.Items;
                    for (var i = 0; i < turns.length; i++) {
                        // Displays direction instructions
                        var description = turns[i].Text,
                            distance = turns[i].Distance,
                            pos = '';
                        if (i == 0) { pos = 'start'; }
                        if (i == turns.length - 1) { pos = 'end'; }
                        steps += '<li class="' + pos + '"><span class="icon">' + ((pos == '') ? i : '') + '</span>';
                        steps += ' <span class="directions">' + description;
                        if (pos != '') {
                            steps += ' (' + distance.toFixed(1) + ' miles)'
                        }
                        steps += '</span></li>';
                    }
                    // Customise icons on map
                    turns[0].Shape.SetCustomIcon(Utils.customPushPinIconWithNumber('', 'green'));
                    turns[turns.length - 1].Shape.Hide();
                    // Append steps and show the panel
                    $list.append(steps);
                    $container.show();
                }
            },

            displayRoutePlanner: function() {
                var $cover = $j('#dealerRoutePlannerCover');
                $cover.fadeTo('slow', 0, function() {
                    $cover.hide();
                });
            },

            refreshDynamicContent: function() {
                Cufon.refresh();

                self.initBalloonPopup();
            },

            removeRoute: function() {
                var $container = $j('#dealerRoutePlannerResults'),
                    $list = $j('ul', $container);
                map.deleteRoute();
                $container.hide();
                $list.empty();
            }

        }

    // Private methods
    function initData() {
        // dealer is the global variable returned by DealerData.aspx
        // dealers is the private array managed by this javascript from this point onwards.
        if (dealer) {
            for (var i = 0; i < dealer.length; i++) {
                var tempDealer = Dealer(dealer[i]);
                dealers.push(tempDealer);
            }
            dealer = null;
        } else {
            Utils.alert('dealerDataError');
        }
    };

    // This function returns an array of services from a matched list of IDs and Names
    function getServicesFromLists(searchServiceId, searchServiceName) {
        var services = [],
            id = (searchServiceId != '') ? searchServiceId.split(',') : searchServiceId,
            name = (searchServiceName != '') ? searchServiceName.split(',') : searchServiceName;

        if (id.length > 0 && name.length > 0 && id.length === name.length) {
            for (var i = 0; i < id.length; i++) {
                var tempService = Service({ ID: id[i], Name: name[i] });
                services.push(tempService);
            }
        }
        return services;
    };

    function initRoutePlanner() {
        var $cover = $j('#dealerRoutePlannerCover'),
            $container = $j('#dealerRoutePlannerBox'),
            $routeButton = $j('#showRouteButton', $container),
            $routeField = $j('input:text', $container),
            routeFrom = $routeField.val();
        $routeField.keypress(function(e) {
            if (e.keyCode == 13) {
                $routeButton.trigger('click');
                return false;
            }
        });
        $routeButton.bind('click', { $routeField: $routeField }, function(e) {
            e.preventDefault();
            map.showRoute(e.data.$routeField.val(), Page.currentDealer);
            self.Omniture.doResultPageClickTracking({
                'pageName': 'dealer_locator > search_results > main_panel : show_route',
                'eVar50': 'Dealer search:Main panel:Show route',
                'hier': 'dealer_locator > search_results',
                'prop11': 'Dealer Search: Route'
            });
        });
        $cover.fadeTo(0, 0.5);
        $container.show();
    };

    function filterDealersByGeoPointAndServices(dealers, displayOptions) {
        var geoPointToCheck = search.getLocation().getPoint(),
            MAX_DEALERS_DISPLAYED = displayOptions.displayLimit || 6,
            returnDealers = [];

        for (var i = 0; i < dealers.length; i++) {
            var currentDealerDistance = dealers[i].setDistanceFromGeoPoint(geoPointToCheck);
            if (dealers[i].hasServices(search.getServices())) {
                for (var k = 0; k < MAX_DEALERS_DISPLAYED; k++) {
                    if (Utils.isCloserThanFound(returnDealers[k], currentDealerDistance)) {
                        returnDealers.splice(k, 0, dealers[i]);
                        break;
                    }
                }
            }
        }
        if (returnDealers.length > MAX_DEALERS_DISPLAYED) {
            returnDealers = returnDealers.slice(0, MAX_DEALERS_DISPLAYED);
        }
        return returnDealers;
    };

    function filterDealersByName(dealers) {
        var regSearch = new RegExp(search.getText(), 'i'),
            returnDealers = [];
        if (regSearch == null) {
            Utils.alert('browserNotSupported');
        }
        for (var i = 0; i < dealers.length; i++) {
            if (dealers[i].getName().test(regSearch)) {
                returnDealers.push(dealers[i]);
            }
        }
        return returnDealers;
    };

    function filterDealersByNameAndServices(dealers) {
        var dealersFoundByName = [],
            returnDealers = [];
        dealersFoundByName = filterDealersByName(dealers);
        for (var i = 0; i < dealersFoundByName.length; i++) {
            if (dealersFoundByName[i].hasServices(search.getServices())) {
                returnDealers.push(dealersFoundByName[i]);
            }
        }
        return returnDealers;
    };

    function filterDealersByIDs(dealers, dealerID, dealerSiteID) {
        var returnDealers = [];
        for (var i = 0; i < dealers.length; i++) {
            if (dealers[i].getId() === dealerID && dealers[i].getSiteId() === dealerSiteID) {
                returnDealers.push(dealers[i]);
            }
        }
        if (returnDealers.length > 1) {
            returnDealers = returnDealers.slice(0, 1);
            Utils.alert('tooManyDealers');
        }
        return returnDealers;
    };

    function searchDealer($searchField, $searchType, $searchRegion, $searchServices) {

        search = SearchData($searchField.find('input:text').val(),
                            $searchType.find('input:radio').filter(':checked').val(),
                            $searchRegion.find('input:radio').filter(':checked').val(),
                            $searchServices.find('input:checkbox').filter(':checked'));

        if (search.getType() === 'location' || search.getType() === 'dealerchanged' || search.getType() === 'deeplink_loc' || search.getType() === 'listOnly') {
            //Perform location based search
            map.findLocation();
        } else if (search.getType() === 'name') {
            //Perform name based search
            dealersFound = filterDealersByNameAndServices(dealers);
            if (dealersFound.length === 1) {
                //redirect to dealer info page
                Utils.redirectToResults(0);
            } else if (dealersFound.length > 1) {
                Utils.redirectToResults();
            } else {
                Utils.alert('noDealersWithName');
            }
        } else {
            // None of the radiobutton is selected?!
        }
    };

    // Public methods
    self.initData = function() {
        initData();
    };

    self.initSearchPage = function() {
        // The map here is initialised only to perform a location search. nothing is gonna be displayed.
        // Would be nice to replace it with Bing services
        var $dlSearchPage = $j('#dlSearchPage').show(),
              $showSearchRegionLink = $j('#showSearchRegionLink'),
              $searchField = $j('#searchField'),
            $searchDisambiguate = $j('#searchDisambiguate').hide(),
            $searchType = $j('#searchType'),
            $searchRegion = $j('#searchRegion').hide(),
            $searchServices = $j('#searchServices'),
            $searchButton = $j('#searchButton').eq(0);

        map = Map('mapPane');

        // Display error messages if user has been redirected here due to error
        if (Utils.getQuerystringValue('err') != "") {
            var errorAsNumber = Number(Utils.getQuerystringValue('err'));
            if (errorAsNumber === 1) {
                Utils.alert('noDealersFound');
            } else if (errorAsNumber === 2) {
                Utils.alert('generalError');
            } else if (errorAsNumber === 3) {
                Utils.alert('postcodeInvalid');
            }
        }

        if (dealers.length > 0) {
            // bind event to link in order to show region's panel
            $showSearchRegionLink.click(function(e) {
                e.preventDefault();
                $searchRegion.slideDown('slow');
            });
            // bind events on return key pressed and button clicked
            $searchField.keypress(function(e) {
                if (e.keyCode == 13) {
                    $searchButton.trigger('click');
                    return false;
                }
            });
            $searchButton.click(function(e) {
                e.preventDefault();
                Page_ClientValidate();
                if (Page_IsValid) {
                    searchDealer($searchField, $searchType, $searchRegion, $searchServices);
                }
            });
        } else {
            Utils.alert('dealerDataError');
        }
    };

    self.initResultsPage = function() {
        var searchType = Number(Utils.getQuerystringValue('type')),
              displayOptions = {
                  displayLimit: Number(Utils.getQuerystringValue('limit')),
                  showTelephone: false,
                  showOpeningTimes: false
              },
                  searchGeoPoint = GeoPoint({ latitude: Utils.getQuerystringValue('lat'), longitude: Utils.getQuerystringValue('lon') }),
              searchFor = Utils.decode(Utils.getQuerystringValue('q')),
              searchServices = getServicesFromLists(Utils.getQuerystringValue('serviceid'), Utils.decode(Utils.getQuerystringValue('servicename')));

        map = Map('mapPane'),

        search = SearchData(searchFor,
                                    searchType,
                                    null,
                                    searchServices,
                                    searchGeoPoint);

        if (dealers.length > 0) {
            if (search.getType() === 'location') {
                dealersFound = filterDealersByGeoPointAndServices(dealers, displayOptions);
                Page.displayDealersFound(0, true, true);
                initRoutePlanner();
                initRoutePlanner();
                self.initPrintButtons();
                self.initPopupButtons();
            } else if (search.getType() === 'name') {
                dealersFound = filterDealersByNameAndServices(dealers, displayOptions);
                Page.displayDealersFound(0, true, true);
                initRoutePlanner();
                self.initPrintButtons();
                self.initPopupButtons();
            } else if (search.getType() === 'dealerchanged') {
                dealersFound = filterDealersByGeoPointAndServices(dealers, displayOptions);
                Page.displayDealersFound(0, true, true);
                initRoutePlanner();
                self.initPrintButtons();
                self.initPopupButtons();

                // If the temporary cookie exists, set the user's preferred dealer
                if (document.cookie.indexOf('overridePreferredDealer') != -1) {
                    var preferredDealer;
                    var loyaltyService = new Service('overridePreferredDealer', 'myFiat participating dealer');
                    for (var i = 0; i < dealersFound.length; i++) {
                        if (dealersFound[i].hasServices(loyaltyService)) {
                            preferredDealer = dealersFound[i];
                            break;
                        }
                    }
                    var PREFERRED_DEALER_SERVICE_URI = "/DealerLocator/PreferredDealerService.ashx";
                    jQuery.post(PREFERRED_DEALER_SERVICE_URI,
                        { DealerCode: preferredDealer.getId(), SiteCode: preferredDealer.getSiteId(), IsSigningUpForLoyalty: false });
                    document.cookie = 'overridePreferredDealer=; expires=Thu, 01-Jan-70 00:00:01 GMT;';
                }
            } else if (search.getType() === 'deeplink_loc') {
                try {
                    map.getMap().Geocode(searchFor, self.geoCodeCallback);
                }
                catch (e) {
                    // Service error so redirect to dealer locator
                    window.location.href = '/dealerlocator?err=2';
                }
            } else if (search.getType() === 'listOnly') {
                displayOptions.showTelephone = true;
                displayOptions.showOpeningTimes = true;
                displayOptions.showRatings = false;
                displayOptions.showContactCta = true;
                displayOptions.displayLimit = 3;
                dealersFound = filterDealersByGeoPointAndServices(dealers, displayOptions);
                Page.displayDealersFound(null, true, false, displayOptions);
            } else {
                Utils.alert();
            }
        } else {
            Utils.alert('dealerDataError');
        }

        if (searchType != 5) {
            self.Omniture.doResultPageLoadTracking();
        }
    };

    self.initGucciResults = function() {
        var searchServices = '500byGucci',
                              searchType = 'listOnly',
                              searchRegion = 'United Kingdom',
                              searchQuery = jQuery('#pnlPostCode input:text').val(),
                              map = Map('mapPane');
        mapObj = new VEMap('mapPane');
        mapObj.SetDashboardSize(VEDashboardSize.Small);
        mapObj.LoadMap(new VELatLong(55, -3), 5);

        search = SearchData(searchQuery, searchType, searchRegion, searchServices);

        var searchFor = Utils.formatPostCode(search.getText()),
                              searchRegion = search.getRegion();
        mapObj.Find(null,                             // what
                              searchFor + ', ' + searchRegion, // where
                              null, // VEFindType (always VEFindType.Business, relates to what search)
                              null, // VEShapeLayer
                              null, // start index for results (0 by default)
                              null, // max number of results (default = 10, range 0 - 20)
                              false, // show results (default is true)
                              null, // create pushpin for what results
                              false, // use default disambiguation (default is true)
                              null, // set best map view (default is true)
                              map.onFoundResults
                              );
    };

    self.geoCodeCallback = function(shapeLayer, findResults, e, moreResults, errorMsg) {
        var geoPoint,
        displayOptions = {
            displayLimit: Number(Utils.getQuerystringValue('limit')),
            showTelephone: false,
            showOpeningTimes: false
        },
        validResults = [];
        if (e !== null) {
            for (var i = 0; i < e.length; i++) {
                geoPoint = GeoPoint({ latitude: e[i].LatLong.Latitude, longitude: e[i].LatLong.Longitude });
                if (Utils.isValidLocation(geoPoint)) {
                    validResults.push(e[i]);
                }
            }

            if (validResults.length === 0) {
                // No results returned so redirect to dealer locator
                window.location.href = '/dealerlocator?err=1';
            } else if (validResults.length === 1) {
                try {
                    geoPoint = GeoPoint({ latitude: validResults[0].LatLong.Latitude, longitude: validResults[0].LatLong.Longitude });
                    search.setLocation(geoPoint);
                    dealersFound = filterDealersByGeoPointAndServices(dealers, displayOptions);
                    Page.displayDealersFound(0, true, true);
                    initRoutePlanner();
                    self.initPrintButtons();
                    self.initPopupButtons();
                }
                catch (err) {
                    // General error so redirect to dealer locator.
                    window.location.href = '/dealerlocator?err=2';
                }
            } else {
                // Ambiguous location returned so redirect to dealer locator.
                window.location.href = '/dealerlocator?err=2';
            }
        } else {
            // Invalid location so redirect to dealer locator.
            window.location.href = '/dealerlocator?err=3';
        }
    };

    self.initDealerPage = function() {
        var dealerID = Utils.getQuerystringValue('id'),
            dealerSiteID = Utils.getQuerystringValue('siteid');
        map = Map('mapPane');
        if (dealers.length > 0) {
            if (dealerID && dealerSiteID) {
                dealersFound = filterDealersByIDs(dealers, dealerID, dealerSiteID);
                Page.displayDealersFound(0, false);
                initRoutePlanner();
                self.initPrintButtons();
                self.initPopupButtons();
            } else {
                // You can't select the dealer without dealerID and dealerSiteID            
            }
        } else {
            Utils.alert('dealerDataError');
        }

        self.Omniture.doDetailsPageLoadTracking();

        $j('.email').bind('click', function(e) {
            self.Omniture.doResultPageClickTracking({
                'pageName': 'dealer_locator > details > entry : email_dealer',
                'eVar50': 'Dealer Contact'
            });
        });
    };

    self.initBalloonPopup = function() {
        var opts = {
            container: '#mainContent',
            animate: true
        },
            $bPop = $j('span.bPop:not(:empty,.ready)', opts.container).addClass('ready').show(),
            $bPopBodyContainer;
        $bPopBodyContainer = $j('span.bPopBodyContainer');
        if ($bPopBodyContainer.length == 0) {
            $bPopBodyContainer = $j('<span class="bPopBodyContainer"></span>').appendTo('body');
        }
        for (var i = 0; i < $bPop.length; i++) {
            var $current = $bPop.eq(i),
                      $bPopLink = $j('<a href="#" class="bPopLink">more info</a>'),
                      $bPopBalloon = $j('<span class="popupWindow"><span class="popupHeader fixPNG"></span><span class="popupContent fixPNG">' + $current.html() + '</span><span class="popupFooter fixPNG"></span><span class="popupChevron fixPNG"></span></span>');

            $current.empty().append($bPopLink, $bPopBalloon);

            if ($current.hasClass('detached')) {
                // THE NEW WAY
                $bPopLink.data('balloon', $bPopBalloon);
                $bPopLink.hover(function() {
                    this.pos = $j(this).offset();
                    $bPopBodyContainer.append($j(this).data('balloon').show()).css({
                        display: 'block',
                        left: this.pos.left,
                        position: 'absolute',
                        top: this.pos.top + 18
                    });
                }, function() {
                    $j(this).after($j(this).data('balloon').hide());
                });
            } else {
                // THE OLD WAY
                $bPopLink.hover(function() {
                    $j(this).next('.popupWindow').stop(true, true).show();
                }, function() {
                    $j(this).next('.popupWindow').stop(true, true).hide();
                });
            }

            $bPopLink.click(function(e) {
                e.preventDefault();
            });
        }
    };

    self.initPrintButtons = function() {
        var $buttons = $j('a.print');
        $buttons.click(function(e) {
            e.preventDefault();
            self.Omniture.doResultPageClickTracking({
                'pageName': 'dealer_locator > search_results > main_panel : print',
                'hier': 'dealer_locator > search_results',
                'prop11': 'Dealer Search: Print',
                'eVar50': 'Dealer search:Main panel:Print'
            });
            window.print();
        });
    };

    self.initPopupButtons = function() {
        var $buttons = $j('a.popupLink[target=_blank]');
        $buttons.live('click', function(e) {
            e.preventDefault();
            Utils.bookServicePopup($j(this).attr('href'));
        });
    };

    // Tracking methods
    self.Omniture = {
        servicesString: {
            newcarsales: 'eVar29',
            partsandservicing: 'eVar30',
            approvedusedcars: 'eVar31',
            approvedbodyshop: 'eVar32',
            mobility: 'eVar33',
            commercialvehiclesspecialist: 'eVar34',
            fleet: 'eVar51',
            myfiatparticipatingdealer: 'eVar52'
        },
        setProps: function(propObj) {
            for (prop in propObj) {
                s[prop] = propObj[prop];
            }
        },
        setProducts: function(value) {
            var stringValue = '';
            for (var i = 0; i < dealersFound.length; i++) {
                stringValue += ';' + dealersFound[i].getId();
                if (i < (dealersFound.length - 1)) {
                    stringValue += ',';
                }
            }
            this.setProps({ 'products': stringValue });
        },
        setType: function() {
            var stringValue = '';
            if (search.getType() === 'location' || search.getType() === 'dealerchanged' || search.getType() === 'deeplink_loc') {
                stringValue = 'location';
            } else {
                stringValue = 'dealer name';
            }
            this.setProps({ 'eVar35': stringValue });
        },
        setServices: function() {
            var servicesId = search.getServicesId(),
                tempObj = {};
            //set all services to no
            for (var key in this.servicesString) {
                tempObj[this.servicesString[key]] = 'no';
            }
            this.setProps(tempObj);
            //then reset selected services to yes
            tempObj = {};
            for (var i = 0; i < servicesId.length; i++) {
                tempObj[this.servicesString[servicesId[i]]] = 'yes';
            }
            this.setProps(tempObj);
        },
        doResultPageClickTracking: function(propObj) {
            var s = s_gi(s_account);
            s.events = 'event9';
            s.linkTrackEvents = 'event9';
            this.setProducts();
            if (Page.currentDealer) {
                this.setProps({ 'eVar27': Page.currentDealer.getId() });
            }
            this.setProps(propObj); // this overrides defaults
            s.linkTrackVars = 'products,events,eVar27,eVar50,prop11,hier1';
            s.tl(this, 'o');
        },
        doResultPageLoadTracking: function(propObj) {
            s.channel = 'Dealer locator';
            s.prop11 = 'Search results';
            s.prop12 = '';
            s.prop19 = 'Dealer search';
            s.pageName = 'dealer_locator > search_results > entry';
            s.hier = 'dealer_locator > search_results';
            s.events = 'event9,event16';
            s.eVar50 = 'Dealer Impression';
            this.setType();
            this.setProducts();
            this.setServices();
            if (Page.currentDealer) {
                this.setProps({ 'eVar27': Page.currentDealer.getId() });
            }
            this.setProps({ 'prop18': dealersFound.length });
            this.setProps({ 'prop37': Utils.decode(Utils.getQuerystringValue('q')) });
            this.setProps({ 'eVar53': Utils.decode(Utils.getQuerystringValue('q')) });
            this.setProps(propObj); // this overrides defaults
            var s_code = s.t(); if (s_code) document.write(s_code);
        },
        doDetailsPageLoadTracking: function(propObj) {
            s.channel = 'Dealer locator';
            s.prop11 = 'Details';
            s.prop12 = '';
            s.pageName = 'dealer_locator > details > entry';
            s.hier = 'dealer_locator > details';
            s.events = 'event9';
            if (Utils.getQuerystringValue('searchPosition')) {
                s.eVar28 = parseInt(Utils.getQuerystringValue('searchPosition')) + 1;
            }
            if (Utils.getQuerystringValue('type') == '0') {
                s.eVar35 = 'dealer name';
            }
            s.eVar50 = 'Dealer Click';
            this.setProducts();
            if (Page.currentDealer) {
                this.setProps({ 'eVar27': Page.currentDealer.getId() });
            }
            this.setProps(propObj); // this overrides defaults
            var s_code = s.t(); if (s_code) document.write(s_code);

        }


    };

    return self;

} ());

