﻿
function GMap(gmapContainerId) {
    var mapControl = {
        id: CreateUUID(),
        containerId: gmapContainerId,
        map: null,
        markers: [],
        listeners: {
            dragStartListener: null,
            dragEndListener: null
        },
        direction: {
            service: null,
            display: null,
            startPoint: null,
            endPoint: null,
            events: {
                onChanged: null,
                onError: null
            },
            listeners: {
                onChanged: null
            },
            visible: false,
            TRAVEL_MODE: google.maps.DirectionsTravelMode.DRIVING,
            UNIT_SYSTEM: google.maps.DirectionsUnitSystem.METRIC
        },
        geocoder: {
            service: null,
            events: {
                onSuccess: null,
                onError: null
            }
        },
        MAP_TYPE: google.maps.MapTypeId.ROADMAP
    };

    mapControl.Container = function () {
        return GetControl(this.containerId);
    };

    mapControl.SetDragStart = function (onDragStartHandler) {
        if (mapControl.listeners.dragStartListener)
            google.maps.event.removeListener(mapControl.listeners.dragStartListener);
        if (onDragStartHandler)
            mapControl.listeners.dragStartListener = google.maps.event.addListener(mapControl.map, 'dragstart', mapControl.listeners.dragStartListener);
    };
    mapControl.SetDragEnd = function (onDragEndHandler) {
        if (mapControl.listeners.dragEndListener)
            google.maps.event.removeListener(mapControl.listeners.dragEndListener);
        if(onDragEndHandler)
            mapControl.listeners.dragEndListener = google.maps.event.addListener(mapControl.map, 'dragend', mapControl.listeners.dragEndListener);
    };

    mapControl.direction.OnChanged = function (routes, args) {
        if (mapControl.direction.events.onChanged)
            mapControl.direction.events.onChanged(routes, args);
    };
    mapControl.direction.OnError = function (error) {
        if (mapControl.direction.events.onError)
            mapControl.direction.events.onError(error);
    };
    mapControl.direction.SetChangedListener = function (handler) {
        if (mapControl.direction.listeners.onChanged)
            google.maps.event.removeListener(mapControl.direction.listeners.onChanged);
        if (handler)
            mapControl.direction.listeners.onChanged = google.maps.event.addListener(mapControl.direction.display, 'directions_changed', handler);
    };

    mapControl.geocoder.OnSuccess = function (results) {
        Trace("mapControl.geocoder.OnSuccess. mapId: " + mapControl.id);
        if (mapControl.geocoder.events.onSuccess) {
            Trace("mapControl.geocoder.events.onSuccess is not NULL");
            mapControl.geocoder.events.onSuccess(results);
        }
    };
    mapControl.geocoder.OnError = function (error) {
        if (mapControl.geocoder.events.onError)
            mapControl.geocoder.events.onError(error);        
    };

    mapControl.AddPoint = function (latlng, message, zIndex, image) {
        if (mapControl.map) {
            var markerId = CreateUUID();
            var marker = new google.maps.Marker({ position: latlng, map: mapControl.map, draggable: false, zIndex: zIndex });
            if (image)
                marker.icon = image;
            var infowindow = null;
            if (message) {
                infowindow = new google.maps.InfoWindow();
                infowindow.zIndex = zIndex;
                infowindow.setContent(message);
                google.maps.event.addListener(marker, 'click', function () { infowindow.open(mapControl.map, marker); });
                infowindow.open(mapControl.map, marker);
            }
            mapControl.markers.push({ id: markerId, marker: marker, infowindow: infowindow });
            mapControl.map.setCenter(latlng);

            return markerId;
        }
        else
            return null;
    };
    mapControl.UpdatePoint = function (pointId, latlng, message, zIndex) {
        if (pointId) {
            for (var i = 0; i < mapControl.markers.length; i++) {
                if (mapControl.markers[i].id == pointId) {
                    if (latlng)
                        mapControl.markers[i].marker.setPosition(latlng);
                    if (message) {
                        mapControl.markers[i].infowindow.setContent(message);
                        mapControl.markers[i].infowindow.open(mapControl.map, mapControl.markers[i].marker);
                    }
                    if (zIndex != null) {
                        mapControl.markers[i].marker.setZIndex(zIndex);
                        mapControl.markers[i].infowindow.setZIndex(zIndex);
                    }
                    return true;
                }
            }
        }
        return false;
    };
    mapControl.RemovePoint = function (pointId) {
        if (pointId) {
            for (var i = 0; i < mapControl.markers.length; i++) {
                if (mapControl.markers[i].id == pointId) {
                    mapControl.markers.splice(i, 1);
                    return true;
                }
            }
        }
        return false;
    };

    return mapControl;
}

//
// GMap methods

function GMap_Initialization(gmap, latlng, routing) {
    Trace("GMap_Initialization(gmap: " + gmap.id + ", latlng: " + latlng + ")");
    if (gmap && gmap.Container()) {

        gmap.direction.service = new google.maps.DirectionsService();
        gmap.direction.display = new google.maps.DirectionsRenderer(
            {
                draggable: true,
                markerOptions: { draggable: false }
            });

        gmap.geocoder.service = new google.maps.Geocoder();

        if (latlng == null)
            latlng = GMap_CreateLatLng(41.9720588, -87.8673537);

        gmap.map = new google.maps.Map(gmap.Container(), {
            zoom: 8, center: latlng, mapTypeId: gmap.MAP_TYPE
        });

        if (routing && routing == true) {
            gmap.direction.display.setMap(gmap.map);
        }
    }
}

function GMap_Reinit(gmap) {
    if (gmap && gmap.Container()) {
        var mapOptions = { zoom: gmap.map.getZoom(), center: gmap.map.getCenter(), mapTypeId: gmap.MAP_TYPE };
        gmap.map = new google.maps.Map(gmap.Container(), mapOptions);
        if (gmap.markers) {
            var bounds = new google.maps.LatLngBounds();
            for (var i = 0; i < gmap.markers.length; i++) {
                gmap.markers[i].marker.setMap(gmap.map);
                bounds.extend(gmap.markers[i].marker.getPosition());
                if (gmap.markers[i].infowindow)
                    gmap.markers[i].infowindow.open(gmap.map, gmap.markers[i].marker);
            }
            gmap.map.fitBounds(bounds);
        }
        if (gmap.direction.visible)
            GMap_DisplayRouting(gmap,
                gmap.direction.startPoint, gmap.direction.endPoint,
                gmap.direction.events.onChanged, gmap.direction.events.onError);
    }
}

function GMap_DisplayRouting(gmap, startLatLng, endLatLng, onChanged, onError) {
    Trace("GMap_DisplayRouting(gmap: " + gmap.id + ", startLatLng: " + startLatLng + ", endLatLng: " + endLatLng +
        ", onChanged: " + onChanged + ", onError: " + onError + ")");
    if (gmap) {
        gmap.direction.startPoint = startLatLng;
        gmap.direction.endPoint = endLatLng;
        gmap.direction.events.onChanged = onChanged;
        gmap.direction.events.onError = onError;

        if (gmap.direction.service && gmap.direction.display) {
            GMap_BuildRouting(gmap.direction.service,
                gmap.direction.startPoint, gmap.direction.endPoint,
                function (directionResult) {
                    if (directionResult) {
                        gmap.direction.display.setDirections(directionResult);
                        gmap.direction.SetChangedListener(function () {
                            gmap.direction.OnChanged(gmap.direction.display.getDirections().routes[0]);
                        });
                        gmap.direction.OnChanged(directionResult.routes[0]);
                    }
                    else {
                        gmap.direction.onChanged(null);
                    }
                },
                function (status) {
                    gmap.direction.OnError(status);
                });
        }
    }
}

function GMap_FindAddress(gmap, addressString, bounds, acceptableTypes, onSuccess, onError) {
    Trace("GMap_FindAddress(gmap: " + gmap.id + ", addressString: " + addressString + ", bounds: " + bounds +
        ", acceptableTypes: " + acceptableTypes + ", onSuccess: " + onSuccess + ", onError: " + onError + ")");
    if (gmap) {
        gmap.geocoder.events.onSuccess = onSuccess;
        gmap.geocoder.events.onError = onError;

        if (gmap.geocoder.service == null)
            gmap.geocoder.service = new google.maps.Geocoder();

        if (gmap.geocoder.events.onSuccess) {
            var geocodeOptions = { address: addressString, language: 'en' };
            if (bounds) geocodeOptions.bounds = bounds;
            Trace("geocodeOptions: " + geocodeOptions);
            gmap.geocoder.service.geocode(geocodeOptions, function (results, status) {
                Trace("got response: " + status);
                if (status == google.maps.GeocoderStatus.OK ||
                    status == google.maps.GeocoderStatus.ZERO_RESULTS) {
                    Trace("results.length: " + results.length);
                    results = GMap_RemoveDuplicates(results);
                    if (bounds) results = GMap_RemoveOutOfBounds(results, bounds);
                    if (acceptableTypes) results = GMap_RemoveUnaccesptableTypes(results, acceptableTypes);
                    Trace("filtered results.length: " + results.length);
                    gmap.geocoder.OnSuccess(results);
                }
                else {
                    gmap.geocoder.OnError(google.maps.GeocoderStatus);
                }
            });
        }
    }
}

//
// Helpers

function GMap_BuildRouting(directionsService, startLatLng, endLatLng, onSuccess, onError) {
    var directionSvc = directionsService ? directionsService : new google.maps.DirectionsService();
    var request = { origin: startLatLng, destination: endLatLng,
        travelMode: google.maps.DirectionsTravelMode.DRIVING, unitSystem: google.maps.DirectionsUnitSystem.METRIC
    };
    directionSvc.route(request, function (result, status) {
        if (status == google.maps.DirectionsStatus.OK) {
            onSuccess(result);
        }
        else if (status == google.maps.DirectionsStatus.ZERO_RESULTS) {
            onSuccess(null);
        }
        else {
            onError(status);
        }
    });
}

function GMap_GetRoutingDistance(startLatLng, endLatLng, onSuccess, onError) {
    var directionsService = new google.maps.DirectionsService();
    var request = {
        origin: startLatLng, destination: endLatLng,
        travelMode: google.maps.DirectionsTravelMode.DRIVING, unitSystem: google.maps.DirectionsUnitSystem.METRIC
    };
    directionsService.route(request, function (response, status) {
        if (status == google.maps.DirectionsStatus.OK) {
            var distance = 0;
            if (response.routes.length > 0) {
                for (var j = 0; j < response.routes[0].legs.length; j++) {
                    distance = distance + response.routes[0].legs[j].distance.value;
                }
            }
            else {
                distance = 0;
            }
            if (onSuccess) {
                onSuccess(distance);
            }
        }
        else {
            if (onError) {
                onError(status);
            }
        }
    });    
}

function GMap_CreateLatLng(latitude, longitude) {
    Trace("GMap_CreateLatLng(latitude: " + latitude + ", longitude: " + longitude + ")");
    return new google.maps.LatLng(latitude, longitude);
}

function GMap_CreateBounds(sw, ne) {
    return new google.maps.LatLngBounds(sw, ne);
}

function GMap_LatLngMapping(latlng) {
    if (latlng) {
        if (latlng.latitude) { }
        else { latlng.latitude = latlng.lat(); }
        if (latlng.longitude) { }
        else { latlng.longitude = latlng.lng(); }
    }
    return latlng;
}

function GMap_ParseLatLng(point) {
    Trace("GMap_ParseLatLng(point: " + point + ")");
    if (point) {
        var coords = point.split(";");
        return GMap_CreateLatLng(coords[1], coords[0]);
    }
    return null;
}

function GMap_ParseBounds(bounds) {
    Trace("GMap_ParseBounds(bounds)");
    if (bounds) {
        var coords = bounds.split(";");
        Trace("bounds = " + bounds);
        Trace("coords.length = " + coords.length);
        var ne = GMap_CreateLatLng(coords[0], coords[1]);
        var sw = GMap_CreateLatLng(coords[2], coords[3]);
        return GMap_CreateBounds(sw, ne);
    }
    return null;
}

function GMap_IsAcceptableType(address, acceptableTypes) {
    if (address && acceptableTypes) {
        for (var tdx in address.types) {
            for (var atdx in acceptableTypes) {
                if (typeof (address.types[tdx]) != 'function' && typeof(acceptableTypes[atdx]) != 'function' &&
                    address.types[tdx] == acceptableTypes[atdx])
                    return true;
            }
        }
    }
    return false;
}

function GMap_RemoveDuplicates(results) {
    var list = [];
    for (var idx1 in results) {
        var isUnique = true;
        for (var idx2 in list) {
            if (typeof (results[idx1]) != 'function' &&
                typeof (list[idx2]) != 'function' &&
                results[idx1].formatted_address == list[idx2].formatted_address) {
                isUnique = false;
                break;
            }
        }
        if (isUnique) {
            list.push(results[idx1]);
        }
    }
    return list;
}

function GMap_RemoveOutOfBounds(results, bounds) {
    var list = [];
    for (var idx in results) {
        if (typeof(results[idx]) != 'function' &&
            bounds.contains(results[idx].geometry.location)) {
            list.push(results[idx]);
        }
    }
    return list;
}

function GMap_RemoveUnaccesptableTypes(results, acceptableTypes) {
    var list = [];
    for (var i in results) {
        if (typeof(results[i]) != 'function' &&
            GMap_IsAcceptableType(results[i], acceptableTypes))
            list.push(results[i]);
    }
    return list;
}

function GMap_SemicolonSerializationAddress(address) {
    var establishment = '';
    var streetNumber = '';
    var streetName = '';
    var city = '';
    var state = '';
    var postalCode = '';
    var country = '';
    var locationX = address.geometry.location.lat();
    var locationY = address.geometry.location.lng();
    var localities = [];
    var adminAreasLevel3 = [];
    var states = [];
    var establishments = [];
    var formatedAddress = address.formatted_address;

    var isAirportAddress = GMap_IsAcceptableType(address, ["airport"]);

    for (var idx1 in address.address_components) {
        var component = address.address_components[idx1];
        if (typeof (component) == 'function')
            continue;
        for (var idx2 in component.types) {
            if (typeof (component.types[idx2]) == 'function')
                continue;
            var type = component.types[idx2];
            if (type == 'establishment') {
                establishments.push(component.short_name);
                break;
            }
            if (type == 'street_number') {
                streetNumber = component.short_name;
                break;
            }
            else if (type == 'route') {
                streetName = component.short_name;
                break;
            }
            else if (type == 'locality') {
                localities.push(component.short_name);
                break;
            }
            else if (type == 'administrative_area_level_3') {
                adminAreasLevel3.push(component.short_name);
                break;
            }
            else if (type == 'administrative_area_level_1') {
                states.push(component.short_name);
                break;
            }
            else if (type == 'postal_code') {
                postalCode = component.short_name;
                break;
            }
            else if (type == 'country') {
                country = component.short_name;
                break;
            }
        }
    }

    if (establishments.length == 1) {
        establishment = establishments[0];
    }
    else if (establishments.length > 1) {
        for (var idx in establishments) {
            if (address.formatted_address.indexOf(establishments[idx]) > -1) {
                establishment = establishments[idx];
                break;
            }
        }
    }

    if (localities.length == 1) {
        city = localities[0];
    }
    else if (localities.length > 1) {
        for (var idx in localities) {
            if (address.formatted_address.indexOf(localities[idx]) > -1) {
                city = localities[idx];
                break;
            }
        }
    }

    var useAdminAreaAsCity = (city == ''); // SG: commented due to see comment below // || isAirportAddress);

    // SG: WAS: if ORD airport, City is incorrect as it uses PO box address in Rosemont; must be Chicago
    // SG: NOW: ORD airport address now lists Shiller Park & Layden in both locality and adminArea3; there is no Chicago reference at all
    if (useAdminAreaAsCity && adminAreasLevel3.length == 1) {
        city = adminAreasLevel3[0];
    }
    else if (useAdminAreaAsCity && adminAreasLevel3.length > 1) {
        for (var idx in adminAreasLevel3) {
            if (address.formatted_address.indexOf(adminAreasLevel3[idx]) > -1) {
                city = adminAreasLevel3[idx];
                break;
            }
        }
    }

    if (states.length == 1) {
        state = states[0];
    }
    else if (states.length > 1) {
        for (var idx in states) {
            if (address.formatted_address.indexOf(states[idx]) > -1) {
                state = states[idx];
                break;
            }
        }
    }

    // SG: if airport, street adress does not make sense; we pass arport name instead of street address
    if (isAirportAddress) {
        streetNumber = '';

        if (establishment != '') {
            streetName = establishment;
        }
    }

    return locationX + ';' +
        locationY + ';' +
        streetNumber + ';' +
        streetName + ';' +
        city + ';' +
        state + ';' +
        postalCode + ';' +
        country + ';' +
        formatedAddress + ';' +
        isAirportAddress;
}

function GMap_AdjustedAirportCode(airportCode) {
    var airportCodes = ["ORD", "PHX"];
    var pattern = /^[\s\t]+|[\s\t]+$/g;
    var upperCaseTrimmedCode = airportCode.toUpperCase().replace(pattern, '');

    if (upperCaseTrimmedCode == "MDW") {
        return "MDW airport chicago";
    }

    for (var idx in airportCodes) {
        if (airportCodes[idx] == upperCaseTrimmedCode) {
            return airportCode.replace(pattern, '') + " international airport";
        }
    }
    return airportCode;
};

function GMap_FillRouteLegsLatLng(legs) {
    if (legs) {
        for (var i = 0; i < legs.length; i++) {
            legs[i].start_location = GMap_LatLngMapping(legs[i].start_location);
            legs[i].end_location = GMap_LatLngMapping(legs[i].end_location);
            if (legs[i].steps) {
                for (var j = 0; j < legs[i].steps.length; j++) {
                    legs[i].steps[j].start_location = GMap_LatLngMapping(legs[i].steps[j].start_location);
                    legs[i].steps[j].end_location = GMap_LatLngMapping(legs[i].steps[j].end_location);
                    if (legs[i].steps[j].path) {
                        for (var k = 0; k < legs[i].steps[j].path.length; k++) {
                            legs[i].steps[j].path[k] = GMap_LatLngMapping(legs[i].steps[j].path[k]);
                        }
                    }
                }
            }
        }
        return legs;
    }
    else {
        return null;
    }
}

if (typeof (Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();

