﻿/// <reference path="jquery-1.3.2-vsdoc.js" />
/// <reference path="Common.debug.js" />
/// <reference path="GMAPJSHelper_Release.js" />
/// <reference path="elabel.debug.js" />

var MAP_TYPE_GOOGLE = 0;
var MAP_TYPE_CARTOGRAPHIA = 1;

MapPoint = function(lat, lng) {
    /// <summary>
    /// Represents a point on the map.
    /// </summary>

    /// <field name="lat" type="number">The latitude position of the point.</field>
    /// <field name="lng" type="number">The longitude position of the point.</field>

    this.setLat(lat);
    this.setLng(lng);
}

MapPoint.prototype = {
    lat: null,
    lng: null,

    setLat: function(lat) {
        /// <summary>
        /// Sets the latitude position of the point.
        /// </summary>
        /// <param name="lat" type="number">The new latitude.</param>

        this.lat = lat;
    },

    setLng: function(lng) {
        /// <summary>
        /// Sets the longitude position of the point.
        /// </summary>
        /// <param name="lng" type="number">The new longitude.</param>

        this.lng = lng;
    }
};

GenericMap = function() {
    /// <summary>
    /// Creates a new generic map element wrapped around the specified element ID.
    /// </summary>
    /// <param name="elementID" type="String">The ID of the element to wrap as a map.</param>

    /// <field name="mapElement" type="String">Represents the map element that this class wraps around.</field>
    /// <field name="googleMap" type="GMap2">Represents the google map element.</field>
    /// <field name="overlayClick">Callback function for when an overlay is clicked.</field>
    /// <field name="overlayAdded">Callback function for when an overlay is added.</field>
}

GenericMap.prototype = {

    // **********************************************************************
    // Variables
    // **********************************************************************
    mapElement: null,
    googleMap: null,
    locations: Array(),
    overlayClick: null,
    overlayAdded: null,

    // **********************************************************************
    // Functions
    // **********************************************************************
    wrapElement: function(elementID, mapType) {
        /// <summary>
        /// Wraps the specified element with this map class.
        /// </summary>
        /// <param name="elementID" type="String">The element ID to wrap this map around.</param>
        var _mapObject = this;
        this.mapType = mapType || MAP_TYPE_GOOGLE;
        this.mapElement = elementID;

        if (this.mapType != MAP_TYPE_CARTOGRAPHIA) {
            //Google Maps
            this.googleMap = new GMap2(document.getElementById(elementID));
            this.googleMap.setCenter(new GLatLng(40.979898, -93.779297), 4);
            this.googleMap.enableScrollWheelZoom();
            this.googleMap.setUIToDefault();

            GEvent.addListener(this.googleMap, "click", function(overlay, latlng) {
                if (_mapObject.overlayClick)
                    _mapObject.overlayClick(overlay, latlng);
            });
        } else {
            // Cartographia Maps


        }

    },

    setOverlayClicked: function(callback) {
        this.overlayClick = callback;
    },

    addOverlay: function(overlay) {
        /// <summary>
        /// Adds an overlay to the current map.
        /// </summary>
        /// <param name="overlay">The overlay to add to the map</param>

        this.googleMap.addOverlay(overlay);
    },

    openInfoWindowTabsHtml: function(latlng, content) {
        this.googleMap.openInfoWindowTabsHtml(latlng, content);
    },

    closeInfoWindow: function() {
        this.googleMap.closeInfoWindow();
    },

    removeOverlay: function(overlay) {
        /// <summary>
        /// Removes an overlay from the current map.
        /// </summary>
        /// <param name="overlay">The overlay to be removed.</param>

        this.googleMap.removeOverlay(overlay);
    },

    refreshMap: function() {
        /// <summary>
        /// Refreshes the Google Map
        /// </summary>
        this.googleMap.checkResize();
    },

    clearOverlays: function() {
        /// <summary>
        /// Removes an overlay from the current map.
        /// </summary>
        /// <param name="overlay">The overlay to be removed.</param>

        this.googleMap.clearOverlays();
    },

    setCenter: function(center, zoomLevel) {
        /// <summary>
        /// Sets the center of the map.
        /// </summary>
        /// <param name="center">The center point of the map</param>
        /// <param name="zoomLevel">The zoom level of the map</param>

        this.googleMap.setCenter(center, zoomLevel);
    },

    addLocation: function(location) {
        /// <summary>
        /// Adds a location to the map.  Location types: RadiusLocation
        /// </summary>
        /// <param name="location">The location to be added to the map.</param>

        var _locations = this.locations;
        var _overlayClick = this.overlayClick;

        location.setMap(this);
        this.locations.push(location);

        location.redraw();

        if (this.overlayAdded != null)
            this.overlayAdded();
    },

    removeLocation: function(location) {
        location.setMap(null);

        for (var i = 0; i < this.locations.length; i++) {
            if (this.locations[i] == location)
                break;
        }

        if (i == this.locations.length) return;
        this.locations.splice(i, 1);
    },

    clearLocations: function() {
        this.googleMap.clearOverlays();

        this.locations = [];
    },

    saveLocations: function(territoryID, territoryName, callback) {
        /// <summary>
        /// Saves the current locations to the server.
        /// </summary>

        var _data = "";

        for (var i = 0; i < this.locations.length; i++) {
            _data += this.locations[i].name + "|" + this.locations[i].radius + "|" + this.locations[i].centerPoint.lng + "|" + this.locations[i].centerPoint.lat + "|" + this.locations[i].iconColor + "|";
        }

        $.post("/Tools/Pronto/SaveLocations.aspx", { territoryID: territoryID, territoryName: territoryName, data: _data }, function() {
            alert("Locations have been saved.");

            if (callback)
                callback();
        });
    },

    ensureVisibleBounds: function(x1, x2, y1, y2) {
        /// <summary>
        /// Ensures the specified bounds are visible.
        /// </summary>

        var _bounds = new GLatLngBounds(new GLatLng(y1, x1), new GLatLng(y2, x2));
        var _zoom = this.googleMap.getBoundsZoomLevel(_bounds);

        if (_zoom > 17)
            _zoom = 17;

        this.setCenter(_bounds.getCenter(), _zoom)
    },

    getCurrentZoom: function() {
        /// <summary>
        /// Gets the current zoom level of the map.
        /// </summary>

        return this.googleMap.getZoom();
    },
    
    getCenter: function() {
        return this.googleMap.getCenter();
    }
};

TileZipCodeOverlay = function() {
    this.tileURL = '';
}

TileZipCodeOverlay.prototype = new GTileLayer(null, null, null, {
    isPng: true,
    opacity: 1.0
});

TileZipCodeOverlay.prototype.setTileURL = function(newURL) {
    this.tileURL = newURL;
}

TileZipCodeOverlay.prototype.getTileUrl = function(point, zoom) {
    var _subdomain = "zip";

    _subdomain += (point.x % 2 == 0 ? "e" : "o");
    _subdomain += (point.y % 2 == 0 ? "e" : "o");

    if (this.tileURL) {
        //remote server
        return "http://" + _subdomain + ".mapwebserver.com" + this.tileURL.replace(/\{Z\}/, zoom).replace(/\{X\}/, point.x).replace(/\{Y\}/, point.y);

        //localhost
        //return this.tileURL.replace(/\{Z\}/, zoom).replace(/\{X\}/, point.x).replace(/\{Y\}/, point.y);
    } else {
        return "";
    }
}

ZipCodeOverlay = function() {
    /// <summary>
    /// Represents a ZIP code overlay.
    /// </summary>
}

ZipCodeOverlay.prototype = new GTileLayerOverlay(new TileZipCodeOverlay());

ZipCodeOverlay.prototype.setTileURL = function(newURL) {
    this.getTileLayer().setTileURL(newURL);
}


TileFSAOverlay = function() {
    this.tileURL = '';
}

TileFSAOverlay.prototype = new GTileLayer(null, null, null, {
    isPng: true,
    opacity: 1.0
});

TileFSAOverlay.prototype.setTileURL = function(newURL) {
    this.tileURL = newURL;
}

TileFSAOverlay.prototype.getTileUrl = function(point, zoom) {
    var _subdomain = "zip";

    _subdomain += (point.x % 2 == 0 ? "e" : "o");
    _subdomain += (point.y % 2 == 0 ? "e" : "o");

    if (this.tileURL) {
        //remote server
        return "http://" + _subdomain + ".mapwebserver.com" + this.tileURL.replace(/\{Z\}/, zoom).replace(/\{X\}/, point.x).replace(/\{Y\}/, point.y);

        //localhost
        //return this.tileURL.replace(/\{Z\}/, zoom).replace(/\{X\}/, point.x).replace(/\{Y\}/, point.y);
    } else {
        return "";
    }
}

FSAOverlay = function() {

}

FSAOverlay.prototype = new GTileLayerOverlay(new TileFSAOverlay());

FSAOverlay.prototype.setTileURL = function(newURL) {
    this.getTileLayer().setTileURL(newURL);
}



TerritoryOverlay = function() {
    /// <summary>
    /// Represents a ZIP code overlay.
    /// </summary>
}

TerritoryOverlay.prototype = new GTileLayerOverlay(
    new GTileLayer(null, null, null, {
        tileUrlTemplate: '/Overlays/TerritoryTileOverlay.aspx?zoom={Z}&xTile={X}&yTile={Y}',
        isPng: true,
        opacity: 1.0
    })
);

NewTileOverlay = function(className, overlayType, minZoom, extraData) {
    this.className = className;
    this.minZoom = minZoom || 13;
    this.overlayType = overlayType || "geography";
    this.extraData = extraData || "";

}

NewTileOverlay.prototype = new GTileLayer(null, null, null, {
    isPng: true,
    opacity: 1.0
});

NewTileOverlay.prototype.getTileUrl = function(point, zoom) {
    if (zoom < this.minZoom)
        return "";

    var _projection = new GMercatorProjection(19);
    var _point1, _point2;
    var _extents = new GLatLngBounds();

    _point1 = new GPoint((point.x + 1) * 256, point.y * 256);
    _point2 = new GPoint(point.x * 256, (point.y + 1) * 256);

    _extents.extend(_projection.fromPixelToLatLng(_point1, zoom, false));
    _extents.extend(_projection.fromPixelToLatLng(_point2, zoom, false));
    //&rand=" + Math.random() +"

    if (this.overlayType == "location") {
        var _post = "/Overlays/NewTileOverlay.aspx?zoom=" + zoom + "&rand=" + Math.random() + "&extents=" + _extents.getSouthWest().toUrlValue() + ";" +
        _extents.getNorthEast().toUrlValue() + "&x=" + point.x + "&y=" + point.y + "&overlay=" + this.overlayType + "&minZoom=" + this.minZoom + "&class=" + this.className + this.extraData;
        //window.status = _post;
        //alert(_post);
        return _post;
    } else {
        var _post = "/Overlays/NewTileOverlay.aspx?zoom=" + zoom + "&rand=" + Math.random() + "&extents=" + _extents.getSouthWest().toUrlValue() + ";" +
        _extents.getNorthEast().toUrlValue() + "&x=" + point.x + "&y=" + point.y + "&overlay=" + this.overlayType + "&minZoom=" + this.minZoom + "&class=" + this.className + this.extraData;
        //window.status = _post;
        return _post;
    }

}



SuppressOverlay = function() {
    /// <summary>
    /// Represents an overlay that suppresses the other countries.
    /// </summary>
}

SuppressOverlay.prototype = new GTileLayerOverlay(
    new GTileLayer(null, null, null, {
        tileUrlTemplate: '/Images/Overlays/Suppress-Countries/Zoom-{Z}/{X}_{Y}.png',
        isPng: true,
        opacity: 0.5
    })
);

function CustomLocation(centerPoint, name) {
    this.centerPoint = centerPoint;
    this.name = name;
    this.map = null;
    this.zoomEvent = null;
    this.marker = null;
    this.iconColor = "red";
    this.customIcon = new GIcon(G_DEFAULT_ICON);
    this.draggable = true;
    //this.iconImage = "http://maps.google.com/mapfiles/ms/micons/" + this.iconColor + "-dot.png";
    this.iconImage = "http://google-maps-icons.googlecode.com/files/home.png";
}


CustomLocation.prototype = {
    setCenter: function(centerPoint) {
        /// <summary>
        /// Sets the center point of the radius location.
        /// </summary>
        /// <param name="centerPoint" type="MapPoint">The new center point.</param>

        this.centerPoint = centerPoint;
        if (this.calculateDemographics)
            this.calculateDemographics(this);
    },

    setIconImage: function(src) {
        this.iconImage = src;
    },

    setMarker: function(marker) {
        //this.marker = marker;
        //this.map.addOverlay(this.marker);
        var _location = this;
        var _icon = new GIcon();
        _icon.image = "http://google-maps-icons.googlecode.com/files/home.png"; 
        _icon.shadow = "";
        _icon.iconSize = new GSize(32, 37); 
        //_icon.shadowSize = new GSize(32, 37); 
        _icon.iconAnchor = new GPoint(15, 37); 
        _icon.infoWindowAnchor = new GPoint(15, 5); 



        this.marker = new GMarker(new GLatLng(this.centerPoint.lat, this.centerPoint.lng), { draggable: this.draggable, icon: _icon });
        this.map.addOverlay(this.marker);
        this.customIcon = _icon;
    },

    setMarkerDraggable: function(draggable) {
        this.draggable = draggable;
    },

    setMap: function(map) {
        /// <summary>
        /// Sets the map attached to this location.
        /// </summary>
        /// <param name="map" type="GenericMap">The new map to be associated with.</param>

        try { GEvent.clearInstanceListeners(this.label); this.map.removeOverlay(this.label); } catch (e) { }
        try { GEvent.clearInstanceListeners(this.marker); this.map.removeOverlay(this.marker); } catch (e) { }

        this.label = null;
        this.marker = null;
        this.map = map;

        if (this.zoomEvent)
            GEvent.removeListener(this.zoomEvent);

        if (this.map != null) {
            var self = this;

            this.zoomEvent = GEvent.addListener(this.map.googleMap, "zoomend", function(oldZoom, newZoom) {
                self.redraw();
            });
        }
    },

    redraw: function() {
        /// <summary>
        /// Redraws the radius on the map.
        /// </summary>

        if (!this.marker) {
            var _location = this;
            var _icon = this.customIcon;

            this.marker = new GMarker(new GLatLng(this.centerPoint.lat, this.centerPoint.lng), { draggable: this.draggable, icon: _icon });
            this.map.addOverlay(this.marker);

            if (this.loadDemographics) {
                try { this.map.removeOverlay(this.label); } catch (e) { }

                this.label = new ELabel(new GLatLng(this.centerPoint.lat, this.centerPoint.lng),
                    (this.demographicValue == null ? "<em>Loading...</em>" : this.demographicValue),
                    "mapLabel", null, 75);
                this.label.setAutoHide(true);
                this.map.addOverlay(this.label);
            }

            GEvent.addListener(this.marker, "dragend", function() {
                _location.setCenter(new MapPoint(_location.marker.getLatLng().lat(), _location.marker.getLatLng().lng()));
            });
        }
    }
};

function RadiusLocation(centerPoint, radius, name, color, calculateDemographics) {
    /// <summary>
    /// Represents a radius location.
    /// </summary>

    /// <field name="centerPoint" type="MapPoint">Represents the center point of the radius.</field>
    /// <field name="radius" type="number">Represents the radius in miles.</field>
    /// <field name="map" type="GenericMap">Represents the map that this location is associated with.</field>
    /// <field name="marker" type="GMarker">Represents the marker that this location is associated with.</field>
    /// <field name="label" type="ELabel">Represents the marker that this location is associated with.</field>
    /// <field name="calculateDemographics" type="Function">Represents the function to be called when demographics need to be calculated.</field>

    this.centerPoint = centerPoint;
    this.radius = radius;
    this.name = name;
    this.iconColor = color || "red";
    this.iconImage = "http://maps.google.com/mapfiles/ms/micons/" + this.iconColor + "-dot.png";
    this.calculateDemographics = calculateDemographics;

    if (this.calculateDemographics)
        this.calculateDemographics(this);

    this.createRing();

    if (this.map != null) {
        var self = this;

        GEvent.addListener(this.map.googleMap, "zoomend", function(oldZoom, newZoom) {
            self.redraw();
        });
    }
}

RadiusLocation.prototype = {
    circle: null,
    bounds: null,
    map: null,
    marker: null,
    label: null,
    demographicValue: null,
    calculateDemographics: null,
    loadDemographics: true,
    minZoom: 11,
    radiusColor: "#000000",
    circleBounds: null,

    setCenter: function(centerPoint) {
        /// <summary>
        /// Sets the center point of the radius location.
        /// </summary>
        /// <param name="centerPoint" type="MapPoint">The new center point.</param>

        this.centerPoint = centerPoint;
        if (this.calculateDemographics)
            this.calculateDemographics(this);
        this.createRing();
        this.drawCircle(false);
    },

    setName: function(name) {
        /// <summary>
        /// Sets the name of the current radius location.
        /// </summary>
        /// <param name="name">The new name of the radius location.</param>

        this.name = name;
        this.setDemographic(this.demographicValue);
    },

    setIconImage: function(newImage) {
        this.iconImage = newImage;

        if (this.marker)
            this.map.removeOverlay(this.marker);

        if (this.label)
            this.map.removeOverlay(this.label);

        this.marker = null;
        this.label = null;
        this.drawCircle(false);
    },

    setIconColor: function(newColor) {
        this.iconColor = newColor;
    },

    setRadius: function(radius) {
        /// <summary>
        /// Sets the radius of the location.
        /// </summary>
        /// <param name="radius" type="number">The radius of the location.</param>

        this.radius = radius;
        if (this.calculateDemographics)
            this.calculateDemographics(this);
        this.createRing();
        this.drawCircle(false);
    },

    setMap: function(map) {
        /// <summary>
        /// Sets the map attached to this location.
        /// </summary>
        /// <param name="map" type="GenericMap">The new map to be associated with.</param>

        try { this.map.removeOverlay(this.label); } catch (e) { }
        try { this.map.removeOverlay(this.circle); } catch (e) { }
        try { this.map.removeOverlay(this.marker); } catch (e) { }

        this.label = null;
        this.circle = null;
        this.marker = null;
        this.map = map;
        this.createRing();

        if (this.map != null) {
            var self = this;

            this.zoomEvent = GEvent.addListener(this.map.googleMap, "zoomend", function(oldZoom, newZoom) {
                self.redraw();
            });
        } else {
            if (this.zoomEvent)
                GEvent.removeListener(this.zoomEvent);
        }
    },

    setMinZoom: function(zoom) {
        this.minZoom = zoom;
    },

    redraw: function() {
        /// <summary>
        /// Redraws the radius on the map.
        /// </summary>

        // Make sure the radius should be displayed.
        if (this.map.googleMap.getZoom() < this.minZoom && this.circle != null) {
            this.map.removeOverlay(this.circle);
            this.circle = null;
        } else if (this.map.googleMap.getZoom() >= this.minZoom && this.circle == null) {
            this.createRing();
        }

        this.drawCircle(false);
    },

    setDemographic: function(value) {
        this.demographicValue = value;

        if (this.label != null && this.loadDemographics)
            this.label.setContents((this.demographicValue == null ? "<em>Loading... This may take a few minutes.</em>" : this.demographicValue));
    },

    setLoadDemographics: function(shouldLoad) {
        if (shouldLoad && !this.loadDemographics) {
            try { this.map.removeOverlay(this.label); } catch (e) { }
            try { this.map.addOverlay(this.label); } catch (e) { }
        } else if (!shouldLoad && this.loadDemographics) {
            try { this.map.removeOverlay(this.label); } catch (e) { }
        }

        this.loadDemographics = shouldLoad;
    },

    createRing: function() {
        var _circlePoints = Array();
        var _bounds = new GLatLngBounds();
        var _point;

        if (!this.map)
            return;

        // Remove the current circle overlay.
        if (this.circle != null) {
            this.map.removeOverlay(this.circle);
            this.circle = null;
        }

        if (isNaN(this.radius) || this.radius == null || this.radius <= 0 || this.map == null)
            return;

        //        // Determine if we should even draw the ring.
        //        if (this.radius < 5 && this.map.googleMap.getZoom() < 11)
        //            return;

        with (Math) {
            var d = this.radius / 3963.189; // radians
            var lat1 = (PI / 180) * this.centerPoint.lat; // radians
            var lng1 = (PI / 180) * this.centerPoint.lng; // radians

            for (var a = 0; a < 361; a++) {
                var tc = (PI / 180) * a;
                var y = asin(sin(lat1) * cos(d) + cos(lat1) * sin(d) * cos(tc));
                var dlng = atan2(sin(tc) * sin(d) * cos(lat1), cos(d) - sin(lat1) * sin(y));
                var x = ((lng1 - dlng + PI) % (2 * PI)) - PI;
                var point = new GLatLng(parseFloat(y * (180 / PI)), parseFloat(x * (180 / PI)));

                _circlePoints.push(point);
                _bounds.extend(point);
            }
        }

        this.circleBounds = _bounds;

        if (this.bounds && !this.map.googleMap.getBounds().contains(_bounds))
            return;

        // Create the circle element.
        if (_circlePoints.length > 0) {
            this.circle = new GPolygon(_circlePoints, "#000000", 2, 1, this.radiusColor, 0.5);
            this.map.addOverlay(this.circle);
        }
    },

    zoomEnd: function(oldZoom, newZoom) {

    },

    drawCircle: function(recenter) {
        /// <summary>
        /// Draws the circle on the map.
        /// </summary>

        if (this.centerPoint == null)
            return;

        // Make sure there is a marker.
        if (!this.marker) {
            var _location = this;
            var _icon = new GIcon(G_DEFAULT_ICON);

            _icon.image = this.iconImage;
            _icon.iconSize = new GSize(32, 32);
            _icon.iconAnchor = new GPoint(16, 32);

            this.marker = new GMarker(new GLatLng(this.centerPoint.lat, this.centerPoint.lng), { draggable: true, icon: _icon });
            this.map.addOverlay(this.marker);

            if (this.loadDemographics) {
                try { this.map.removeOverlay(this.label); } catch (e) { }

                this.label = new ELabel(new GLatLng(this.centerPoint.lat, this.centerPoint.lng),
                    (this.demographicValue == null ? "<em>Loading...</em>" : this.demographicValue),
                    "mapLabel", null, 75);
                this.label.setAutoHide(true);
                this.map.addOverlay(this.label);
            }

            GEvent.addListener(this.marker, "dragend", function() {
                _location.setCenter(new MapPoint(_location.marker.getLatLng().lat(), _location.marker.getLatLng().lng()));
            });
        }

        if (recenter) {
            if (this.radius != null && this.radius > 0) {
                if (this.bounds != null)
                    this.map.setCenter(this.bounds.getCenter(), this.map.googleMap.getBoundsZoomLevel(this.bounds));
                else if (this.circleBounds != null)
                    this.map.setCenter(this.circleBounds.getCenter(), this.map.googleMap.getBoundsZoomLevel(this.circleBounds));
            } else
                this.map.setCenter(new GLatLng(this.centerPoint.lat, this.centerPoint.lng), 13);
        }

        if (this.loadDemographics)
            this.label.setPoint(new GLatLng(this.centerPoint.lat, this.centerPoint.lng));
    }
};

$(document).unload(function() { GUnload(); });