﻿/*
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=true"></script>
/// <reference path="http://jquery.gmap.js" />
*/
(function ($) {

    $.fn.gmaps3 = function (options) {
        var defaults = {
        	distance: 12,
            lat: "42.39253",
			lng: "-71.319809",
            navControl: true,
			mapTypeId: (typeof(google) !== "undefined") ? google.maps.MapTypeId.ROADMAP : "",
			address: "",
			mapContainer: ".map",
			centre: true,
			centreMarker: true,
			icon: "",
			addMarker: {},
			path: {},
			route: {},
			routeContainer: "#route",
			tilesLoadedCallback: null
        };
        var options = $.extend(defaults, options);
        var bounds = new google.maps.LatLngBounds();
        
      
        // Allows multiple elements to be selected.
		var container = $(this);															
		
        if (container.length > 1) {
            container.each(function () { $(this).gmap(options); });
            return container;
        }

        // global var to access the google map object
        container.map;

        // overlay hash used for clearing
        container.overlays = [];
		
		// global var to store latitude and longitude obj
		container.latlng;

		// global var to store DESTINATION latitude and longitude obj		
		container.destLatLng;

        // Initializes the map
        container.initialize = function () {
        
             	
			if( options.address != "" ){														
				$.fn.gmaps3.geoCodeAddress(options.address, function (latlng) {						
					container.map.setCenter(latlng);
					container.addMarkerByAddress( address , "", "", "");	
				});												
			}else{
				container.latlng = new google.maps.LatLng(options.lat, options.lng);								
			}
			
            var settings = {
                zoom: options.zoom,    
				center: container.latlng,			
                mapTypeControl: true,
                mapTypeControlOptions: { style: google.maps.MapTypeControlStyle.DROPDOWN_MENU },
                navigationControl: options.navControl,
                navigationControlOptions: { style: google.maps.NavigationControlStyle.SMALL },
                mapTypeId: options.mapTypeId
            };		

            container.map = new google.maps.Map( container[0] , settings);
            
            /* Centre the map if it is needed */
            if( options.centre ){            	
            	container.map.setCenter(container.latlng);
            	if( options.centreMarker ){
            		container.addMarkerByLatLng( options.lat , options.lng , "" , options.icon );	            	
            	}
            }
            
            /* Checks if there are additional markers / pinpoints */
            if( options.addMarker.length > 0 ){ 
            	for( var i = 0 ; i < options.addMarker.length ; i++ ){             		
            		container.addMarkerByLatLng( options.addMarker[i].lat , options.addMarker[i].lng , "" , options.addMarker[i].icon );
            	}
            	
            	//Automatically fits the markers in the map if they are too far away from each other
            	for (var i = 0; i < options.addMarker.length; i++) {
            		bounds.extend(new google.maps.LatLng(options.addMarker[i].lat, options.addMarker[i].lng));
            	}
                container.map.fitBounds(bounds);
            }
             
            if( options.route != undefined ) {
            	//container.destLatLng = new google.maps.LatLng( options.route.latDest , options.route.lngDest );	
            	container.updateDestinationByLatLng( options.route.latDest , options.route.lngDest );
            	container.addRoute( { origin: container.latlng , destination: container.destLatLng } , { routeContainer: options.routeContainer } );	
            }

            return container;
        };
        
        container.updateDestinationByLatLng = function ( lat , lng ) {
        	container.destLatLng = new google.maps.LatLng( lat , lng );
        };
        
        container.updateRouteStartByAddress = function ( address, callback ) {
        	var temp = $.fn.gmaps3.geoCodeAddress( address , function ( latlng ) { 
        		$(options.routeContainer).html("");
        		options.lat = latlng.lat();
        		options.lng = latlng.lng();
        		container.initialize();
        		
        		if(typeof(callback) == "function"){
        			callback({ 
        				location: address,
        				latitude: latlng.lat(),
        				longitude: latlng.lng()
        			});
        		}
			});
        	var changeLocationURL = String.format("/en-US/tires/ubox/changelocation/{0}/{1}/{2}/",address,options.lat,options.lng);
        	$.ajax({
				type: "GET",
				url: changeLocationURL,				
				success: function( data ){										
						$.log("Location Successfuly Updated");
						$.log( data );
					}
			});	
        };
        
        // set map type
        container.setTypeRoadMap = function () {
            this.map.setMapTypeId(google.maps.MapTypeId.ROADMAP);
        };
        container.setTypeSatellite = function () {
            this.map.setMapTypeId(google.maps.MapTypeId.SATELLITE);
        };
        container.setTypeHybrid = function () {
            this.map.setMapTypeId(google.maps.MapTypeId.HYBRID);
        };
        
        // zoom methods
        container.getZoom = function () {
            return this.map.getZoom();
        };
        container.setZoom = function (level) {
            this.map.setZoom(level);
        };

        // add a marker to the map by address
        container.addMarkerByAddress = function (address, title, icon, html) {
            var localmap = this.map;
            $.fn.gmaps3.geoCodeAddress(address, function (latlng) {
                return _addMarkerByLatLng(latlng, title, icon, html, localmap);
            });
        };
        // add a marker to the map by lat / lng
        container.addMarkerByLatLng = function (lat, lng, title, icon, html) {
            var latlng = new google.maps.LatLng(lat, lng);
            return _addMarkerByLatLng(latlng, title, icon, html, this.map, this.overlays);
        };

        // add a path to the map, draw lines
        container.addPath = function (data, opts) {
            var defOpts = {
                color: "#ff0000",
                opacity: 1.0,
                strokeWeight: 2.0
            };
            var opts = $.extend(defOpts, opts);

            if (data != undefined) {

                var path = new google.maps.Polyline({
                    path: _convertData(data),
                    strokeColor: opts.color,
                    strokeOpacity: opts.opacity,
                    strokeWeight: opts.strokeWeight
                });

                path.setMap(this.map);
                this.overlays.push(path);
            }
            return this;
        };
        
        // creates a route between two points
        container.addRoute = function (data, opts) {
            if(typeof(options.tilesLoadedCallback) == "function"){
	            var tilesLoadListener = google.maps.event.addListener(container.map, "tilesloaded", function(){
	            	$.log("gmaps3:tilesloaded:calling callback.");
	            	google.maps.event.removeListener(tilesLoadListener);
	            	options.tilesLoadedCallback();
	            });
            }
        	
            var defOpts = {
                color: "#ff0000",
                opacity: 1.0,
                strokeWeight: 2.0,
                routeContainer: "",
                travelMode: google.maps.DirectionsTravelMode.DRIVING
            };            
            var opts = $.extend(defOpts, opts);

            if (data != undefined) {
            	var directionsService = new google.maps.DirectionsService();
            	var directionsDisplay = new google.maps.DirectionsRenderer();             	
        	    directionsDisplay.setMap(container.map);         	    

        	    if( opts.routeContainer != "" ){
        	    	directionsDisplay.setPanel( $(opts.routeContainer)[0] );	
        	    }
        	    
        	    var request = {
    	            origin: data.origin, 
    	            destination: data.destination ,
    	            travelMode: opts.travelMode
    	        };
    	        directionsService.route(request, function(response, status) {
    	          if (status == google.maps.DirectionsStatus.OK) {
    	            directionsDisplay.setDirections(response);
    	          }
    	        });
            }
            return this;
        };

        // add a polygon to the map
        container.addPolygon = function (data, opts) {
            return _addPolygonToMap(data, null, opts, this.map, this.overlays);
        };

        // add a polygon to the map
        container.addClickablePolygon = function (data, html, opts) {
            return _addPolygonToMap(data, html, opts, this.map, this.overlays);
        };

        // clear the overlays
        container.clear = function () {
            if (this.overlays != undefined) {
                for (var i = 0; i < this.overlays.length; i++) {
                    this.overlays[i].setMap(null);
                }
                this.overlays = [];
            }
        };

        container.toggleDebug = function () {

            // Create new control to display latlng and coordinates under mouse.
            var latLngControl = new _latLngControl(this.map);

            // Register event listeners
            google.maps.event.addListener(this.map, 'mouseover', function (mEvent) {
                latLngControl.set('visible', true);
            });
            google.maps.event.addListener(this.map, 'mouseout', function (mEvent) {
                latLngControl.set('visible', false);
            });
            google.maps.event.addListener(this.map, 'mousemove', function (mEvent) {
                latLngControl.updatePosition(mEvent.latLng);
            });

            return this;
        };

        container.onclickReverseGeocode = function (callback) {
            geocode = google.maps.event.addListener(this.map, 'click', function (me) {
                $.fn.gmaps3.geoCodeLatLng(me.latLng.lat(), me.latLng.lng(), function (address) {
                    if (callback != undefined) {
                        callback(address);
                    }
                });
            });
        };

        container.onclickGetLatLng = function (callback) {
            geocode = google.maps.event.addListener(this.map, 'click', function (me) {
                var result = [me.latLng.lat(), me.latLng.lng()];
                if (callback != undefined) {
                    callback(result);
                }
            });
        };
        
        /* ------------- Globals functions ------------------ */

        // Updates a registered element with the address (reverse geocode)
        $.fn.gmaps3.geocoder = new google.maps.Geocoder();
        $.fn.gmaps3.geoCodeLatLng = function (lat, lng, callback) {
            var latlng = new google.maps.LatLng(lat, lng);
            $.fn.gmaps3.geocoder.geocode({ 'latLng': latlng }, function (results, status) {
                if (status == google.maps.GeocoderStatus.OK) {
                    var str = results[0].formatted_address;                   
                    callback(str);
                } else {
                    $.log("Geocoder failed due to: " + status);
                }
            });
        };		
		
        $.fn.gmaps3.geoCodeAddress = function (address, callback) {
            $.fn.gmaps3.geocoder.geocode({ 'address': address }, function (results, status) {
                if (status == google.maps.GeocoderStatus.OK) {
                    if (callback != undefined) {
                        callback(results[0].geometry.location);
                    }
                    else {
                        return results;
                    }
                } else {
                    $.log("Geocoder failed due to: " + status);
                }
            });
        };


        /* ------------- Private functions ------------------ */

        // Adds a marker to the map
        function _addMarkerByLatLng(latlng, title, icon, html, theMap, overlays) {
            container.marker = new google.maps.Marker({
            	icon: icon,
                position: latlng,
                map: theMap,
                title: title
            });
            //overlays.push(marker);

            if (html != undefined) {
                var infowindow = new google.maps.InfoWindow();
                google.maps.event.addListener(container.marker, 'click', function () {
                    infowindow.setContent(html);
                    infowindow.open(theMap, container.marker);
                });
            }
            return this;
        }

        // Adds a polygon to the map
        function _addPolygonToMap(data, html, opts, theMap, overlays) {
            var defOpts = {
                strokeColor: "#ff0000",
                strokeOpacity: 0.8,
                strokeWeight: 2.0,
                fillColor: "#ff0000",
                fillOpacity: 0.35
            };
            var opts = $.extend(defOpts, opts);

            if (data != undefined) {

                var polygon = new google.maps.Polygon({
                    paths: _convertData(data),
                    strokeColor: opts.strokeColor,
                    strokeOpacity: opts.strokeOpacity,
                    strokeWeight: opts.strokeWeight,
                    fillColor: opts.fillColor,
                    fillOpacity: opts.fillOpacity
                });

                polygon.setMap(theMap);
                overlays.push(polygon);

                if (html != undefined) {
                    var infowindow = new google.maps.InfoWindow();
                    google.maps.event.addListener(polygon, 'click', function (event) {
                        infowindow.setContent(html);
                        infowindow.setPosition(event.latLng);
                        infowindow.open(theMap);
                    });
                }
            }

            return this;
        }

        // Converts array of JSON lat/lng into google array
        function _convertData(data) {
            var pts = [];
            for (var i = 0; i < data.length; i++) {
                pts[i] = new google.maps.LatLng(data[i].lat, data[i].lng);
            }
            return pts;
        }

        // Creates html that follows the mouse and displays lat/lng.
        function _latLngControl(map) {
            /**
            * Offset the control container from the mouse by this amount.
            */
            this.ANCHOR_OFFSET_ = new google.maps.Point(8, 8);

            /**
            * Pointer to the HTML container.
            */
            this.node_ = this.createHtmlNode_();

            // Add control to the map. Position is irrelevant.
            map.controls[google.maps.ControlPosition.TOP].push(this.node_);

            // Bind this OverlayView to the map so we can access MapCanvasProjection
            // to convert LatLng to Point coordinates.
            this.setMap(map);

            // Register an MVC property to indicate whether this custom control
            // is visible or hidden. Initially hide control until mouse is over map.
            this.set('visible', false);
        }

        // Extend OverlayView so we can access MapCanvasProjection.
        _latLngControl.prototype = new google.maps.OverlayView();
        _latLngControl.prototype.draw = function () { };

        _latLngControl.prototype.createHtmlNode_ = function () {
            var divNode = document.createElement('div');
            divNode.id = 'latlng-control';
            divNode.index = 100;
            return divNode;
        };

        _latLngControl.prototype.visible_changed = function () {
            this.node_.style.display = this.get('visible') ? '' : 'none';
        };

        _latLngControl.prototype.updatePosition = function (latLng) {
            var projection = this.getProjection();
            var point = projection.fromLatLngToContainerPixel(latLng);

            // Update control position to be anchored next to mouse position.
            this.node_.style.left = point.x + this.ANCHOR_OFFSET_.x + 'px';
            this.node_.style.top = point.y + this.ANCHOR_OFFSET_.y + 'px';

            // Update control to display latlng and coordinates.
            this.node_.innerHTML = [
                          latLng.toUrlValue(4),
                          '<br/>',
                          point.x,
                          'px, ',
                          point.y,
                          'px'
                        ].join('');
        };

        // Initialize the map
        if( container.length > 0 ){
        	return container.initialize();	
        }
        
    };

})(jQuery);
