/**************************************************************
    HeliumGMap class
**************************************************************/
var HeliumGMap = function(container)
{
    var svClient = new GStreetviewClient();
    var svOverlay = new GStreetviewOverlay();
    var svGuyMarker = null;
    var svminimap = null;
    var svPanorama = null;
    
    var mapView = {type:null,zoom:null,lat:null,lng:null,coords:null};
    var mapContainer = container;
    var map = null;
    
    var markerArray = [];
    var activeMarker = null;
    var canFireMapMoveEvent = false;
    
    var options = {
        initLat:    -34.9294304855664525, /* sets the initial lattertude for the center of the map. */
		initLng:    138.60162734985351, /* sets the initial longitude for the center of the map. */
		initZoom:   2, /* sets the initial zoom level for the map. */
		initMapType: G_HYBRID_MAP,
		hasOverviewMap:   true,
		hasMapCtrl: true,
		hasMapTypeCtrl: true,
		hasScaleCtrl: true,
		hideMarkersOnLoad: false,
		backgroundColor: '#fff',
		
		markerInfoEvent: 'click',
		centerOnMarker: false,
		
		pinBaseHref:"",
		pinForceDefaultImage: false,
		pinDefaultImage: "iconr.png",
		pinDefaultShadow: "shadow.png",
		pinHoverSuffix: "_over",
		pinActiveSuffix: "_active",
		
		pinSize: {width:20.0, height:34.0},
        pinShadowSize: {width:38.0, height:34.0},
        pinAnchor: {x:10, y:34.0},
        infoWindowAnchor: {x:10.0, y:0},
		
		infoWinMaxWidth: 250,
		
		geoCountryCode: null,
		minGeoSearchZoom: 14,
		
		hasStreetView: false
    };
    
    /**************************************************************
        Public Functions
    **************************************************************/
    this.getOptions = function (){return options;};
    this.setOptions = function (newOptions)
    {
        if(newOptions) {
            $.extend(options, newOptions);
        }
    };
            
    /* Load the map from XML - uses current settings */
    this.loadMap = function(mapXml){
        if(mapContainer !== null)
        {
            /* lazy loading (for ajax maps) */
            if(map === null)
            {
                map = new GMap2(mapContainer, {backgroundColor:options.backgroundColor});
            }
               
            map.setCenter(new GLatLng(options.initLat, options.initLng), options.initZoom);
            map.savePosition();
            map.setMapType(options.initMapType);
            if(options.hasMapTypeCtrl)
            {
                map.addControl(new HeliumMapTypeControl());
            }
            
            if(options.hasOverviewMap)
            {
                map.addControl(new GOverviewMapControl());
            }
            
            if(options.hasMapCtrl)
            {
                map.addControl(new HeliumMapControl());
                map.enableScrollWheelZoom();
                map.enableContinuousZoom();
                map.enableDoubleClickZoom();
            }
            
            if(options.hasScaleCtrl)
            {
                map.addControl(new GScaleControl());
            }
            
            if(mapXml && mapXml.length !== 0)
            {
                var doc = GXml.parse(mapXML);
                _loadingSettings(doc);
                _loadPlacemarks(doc);
            }
        }
    };
    
    /*** Loads placemarks from xml doc ***/
    this.loadPlacemarks = function(doc)
    {
        _loadPlacemarks(doc);
    };
    
    this.loadPlacemarkArray = function(placemarks)
    {
        _loadPlacemarkArray(placemarks);
    };
    
    this.unload = function()
    {
        if(svPanorama)
        {
            svPanorama.remove();
        }
    }
    
    /*** Clears all map overlays ***/
    this.clearMap = function()
    {
        map.clearOverlays();
        markerArray = [];
        activeMarker = null;
    };
    
    /*** Geo search ***/
    this.geoSearch = function(srchStr)
    {
        var geocoder = new GClientGeocoder();
        geocoder.getLatLng(
          srchStr,
          function(point) {
            if (point) {
              map.setCenter(point, 4);
            }
          }
        );
    };
    
    this.panTo = function(lat, lng, zoom)
    {
        map.setCenter(new GLatLng(lat, lng), zoom);
    };
    
    this.getPlacemark = function(index)
    {
        if(index < markerArray.length && markerArray[index])
        {
            return markerArray[index];
        }
        
        return null;
    };
    
    this.getPlacemarkArray = function()
    {
        return markerArray;
    };
     
    this.resetMap = function(){
        map.returnToSavedPosition();
    };
    
    this.setZoom = function(zoom){
        map.setZoom(zoom);
    };
    
    this.selectPlacemark = function(index){
        _selectMarker(markerArray[index]);
    };
    
    /*** Exposed XML Util functions ***/
    this.serializeNode = function(node){
        return _serializeNode(node);
    };
    
    this.xmlFormat = function(str){
        return _xmlFormat(str);
    };
    
    this.getChildNodeValue = function(parentNode, nodeName, defaultValue){
        return _getChildNodeValue(parentNode, nodeName, defaultValue);
    };
    
    this.getSingleNodeValue = function(parentNode, xPath, defaultValue){
        return _getSingleNodeValue(parentNode, xPath, defaultValue);
    };
    
    /**************************************************************
        Private Functions
     -------------------------------------------------------------
        Loads map settings from xml doc
    **************************************************************/
    function _loadingSettings(doc)
    {
        if(doc)
        {
            //load map settings
            var viewNode = doc.getElementsByTagName("View");
            
            if(viewNode.length !== 0)
            {
                var mapLat = _getChildNodeValue(viewNode[0], "Latitude", 0);
                var mapLng = _getChildNodeValue(viewNode[0], "Longitude", 0);
                var mapZoom = _getChildNodeValue(viewNode[0], "Zoom", 0);
               
                if(mapLat !== 0 && mapLng !== 0)
                {
                    map.setCenter(new GLatLng(mapLat, mapLng));
                    map.setZoom(parseInt(mapZoom, 10));
                }
            }
        }
    }
    
    function _loadPlacemarks(doc)
    {
        if(doc)
        {
            //get placemarkers
            var mapBounds = map.getBounds();
            var hasVisibleMarker = false;
            var markerNodes = doc.getElementsByTagName("Placemark");
         
            var newMarker;
            for(var i=0; i<markerNodes.length; i++)
            {
                newMarker = _createMarkerFromXML(markerNodes[i]);
                if(newMarker)
                {
                    map.addOverlay(newMarker);
                    if(options.hideMarkersOnLoad)
                    {
                        newMarker.hide();
                    }
                }
                 
                if(mapBounds.containsLatLng(newMarker.getLatLng()))
                {
                    hasVisibleMarker = true;
                }
            }

            //If no markers are in viewing range then center map on first marker
            if(!hasVisibleMarker && markerArray.length !== 0)
            {
                map.panTo(markerArray[0].getLatLng());
            }
        }
    }
    
    function _loadPlacemarkArray(placemarks)
    {
        if(placemarks && placemarks.length !== 0)
        {
            //get placemarkers
            var mapBounds = map.getBounds();
            var hasVisibleMarker = false;
            
            for(var i=0; i<placemarks.length; i++)
            {
                map.addOverlay(placemarks[i]);
                if(options.hideMarkersOnLoad)
                {
                    newMarker.hide();
                }
                 
                if(mapBounds.containsLatLng(placemarks[i].getLatLng()))
                {
                    hasVisibleMarker = true;
                }
            }

            //If no markers are in viewing range then center map on first marker
            if(!hasVisibleMarker && placemarks.length !== 0)
            {
                map.panTo(placemarks[0].getLatLng());
            }
            
            markerArray = placemarks;
        }
    }
    
    function _selectMarker(marker)
    {
        if(marker)
        {
            if(svPanorama)
            {
                $(mapContainer).find(".helium-streetview-container .error:visible").hide();
                $(mapContainer).find(".helium-streetview-container:visible").hide();
                svPanorama.remove();
            }
            
            if(activeMarker !== null)
            {
                _unselectMarker(activeMarker);
            }
            
            canFireMapMoveEvent = false;
            activeMarker = marker;
            
            marker.openInfoWindow(_getInfoNode(marker), {maxWidth:options.infoWinMaxWidth});
            
            if(options.pinActiveSuffix && options.pinActiveSuffix.length !== 0)
            {
                var hoverImg = marker.getIcon().image.replace(".", options.pinActiveSuffix+".");
                marker.setImage(hoverImg);
            }
            
            GEvent.addListener(marker, options.markerInfoEvent, function() {  
                _selectMarker(marker);
            });
            
            GEvent.trigger(marker, "heliumGMarkerSelected", marker);
        }
    }
    
    function _unselectMarker(marker)
    {
        marker.closeInfoWindow();
        marker.setImage(marker.getIcon().image);
        GEvent.trigger(marker, "heliumGMarkerUnselected", marker);
    }
    
    function _launchStreetview(marker)
    {
        var cntnr = $(mapContainer);
        
        if(!svPanorama)
        {
            var winW = map.fromLatLngToContainerPixel(map.getBounds().getNorthEast()).x;
            var winH = map.fromLatLngToContainerPixel(map.getBounds().getSouthWest()).y;
            var mapContainer = $(map.getContainer());
            
            var streetviewContainer = $("<div class='helium-streetview-container'><div class='helium-streetview'></div><div class='error'></div><div class='close'><span>Street view</span></div><div class='minimap-container'><div class='minimap'></div></div></div>");
            streetviewContainer.width(winW);
            streetviewContainer.height(winH);
            mapContainer.append(streetviewContainer);
            
            var streetviewElem = streetviewContainer.find("div.helium-streetview");
            streetviewElem.width(winW);
            streetviewElem.height(winH);
            
            svPanorama = new GStreetviewPanorama(streetviewElem.get(0));
            cntnr.find(".helium-streetview-container .close").click(function(){
                $(mapContainer).find(".helium-streetview-container .error:visible").hide();
                $(mapContainer).find(".helium-streetview-container").hide();
                svPanorama.remove();
            });
            
            var minimapContainer = streetviewContainer.find("div.minimap-container");
            minimapContainer.attr("style", "position:absolute;top:"+ (winH - minimapContainer.height()) +"px;left:"+ (winW - minimapContainer.width()) +"px;");
            
            svGuyMarker = new GMarker(marker.getLatLng(), {icon: _createGuyIcon(), draggable: true});
            GEvent.addListener(svGuyMarker, "dragstart", _svOnDragStart);
            GEvent.addListener(svGuyMarker, "dragend", _svOnDragEnd);
            
            svminimap = new GMap2(minimapContainer.find("div.minimap").get(0));
            svminimap.disableScrollWheelZoom();
            svminimap.disableContinuousZoom();
            svminimap.disableDoubleClickZoom();
                
            svminimap.setCenter(svGuyMarker.getLatLng(), 16);
            svminimap.addOverlay(svGuyMarker);
            
            GEvent.addListener(svPanorama, "yawchanged", _svOnYawChange);
            GEvent.addListener(svPanorama, "initialized", function(location){
                svGuyMarker.setLatLng(location.latlng);
                svminimap.setCenter(location.latlng);
            });
            GEvent.addListener(svPanorama, "error", function(errorCode){
                _showStreetviewError(errorCode);
            });
        }
        else
        {
            $(mapContainer).find(".helium-streetview-container:hidden").show();
            svPanorama = new GStreetviewPanorama(cntnr.find(".helium-streetview").get(0));
            svGuyMarker.setLatLng(marker.getLatLng());
            svminimap.setCenter(marker.getLatLng());
        }
        
        svClient.getNearestPanorama(marker.getLatLng(), _svOnResponse);
    }
    
    function _showStreetviewError(errorCode)
    {
        var errorMsg = "Unable to retrieve street view data.";
        if(errorCode == 600)
        {
            errorMsg = "No panorama data was found.";
        }
        else if(errorCode == 603)
        {
            errorMsg = "The flash viewer was not available. Please ensure your browser supports flash."
        }
        
        $(mapContainer).find(".helium-streetview-container .error").text(errorMsg);
        $(mapContainer).find(".helium-streetview-container .error").show();
    }
    
    function _svOnDragStart()
    {
        svminimap.addOverlay(svOverlay);
    }
    
    function _svOnDragEnd()
    {
        svminimap.removeOverlay(svOverlay);
        $(mapContainer).find(".helium-streetview-container.error:visible").hide();
        svClient.getNearestPanorama(svGuyMarker.getLatLng(), _svOnResponse);
    }
    
    function _svOnResponse(response) {
        
        if (response.code != 200) 
        {
            _showStreetviewError(response.code);
        } 
        else 
        {
            $(mapContainer).find(".helium-streetview-container .error:visible").hide();
            var latlng = new GLatLng(response.Location.lat, response.Location.lng);
      
            svGuyMarker.setLatLng(latlng);
            svPanorama.setLocationAndPOV(latlng, null);
            
        }
    }
    
    function _svOnYawChange(newYaw) 
    {
        var GUY_NUM_ICONS = 16;
        var GUY_ANGULAR_RES = 360/GUY_NUM_ICONS;
        if (newYaw < 0) {
            newYaw += 360;
        }
        guyImageNum = Math.round(newYaw/GUY_ANGULAR_RES) % GUY_NUM_ICONS;
        guyImageUrl = "http://maps.google.com/intl/en_us/mapfiles/cb/man_arrow-" + guyImageNum + ".png";
        svGuyMarker.setImage(guyImageUrl);
    }

    function _getInfoNode(marker)
    {
        var infoContent = $("<div class='infoWindow'><h2>"+marker.Title+"</h2><div>"+marker.Info+"</div></div>");
       
        if(options.hasStreetView)
        {
            var streetviewLink = $("<p class='link'><a href='#' title='Streetview' class='streetview-link'>Street view</a></p>"); 
            streetviewLink.click(function(){
                _launchStreetview(marker);
                return false;
            });
            infoContent.append(streetviewLink);
        }
        return infoContent.get(0);
    }
    
    function _createMarkerFromXML(xmlNode)
    {
        //get placemarkers
        var lng = 0;
        var lat = 0;
        var title = "";
        var descNode = null;
        var description = "";
        var iconURL = "";
      
        title = _getChildNodeValue(xmlNode, "name", "");
        description = "";
        descNode = xmlNode.getElementsByTagName("description");
        lng = $.trim(_getChildNodeValue(xmlNode, "Longitude", "0"));
        lat = $.trim(_getChildNodeValue(xmlNode, "Latitude", "0"));
        iconURL = _getSingleNodeValue(xmlNode, "Icon/href", "");
         
        if(descNode && descNode.length !== 0)
        {
		    description = _serializeNode(descNode[0]);
		    description = description.replace("<description>", "");
			description = description.replace("</description>", "");
			description = description.replace("<description/>", "");
        }
        
        if(lat !== 0 && lng !== 0)
        {
            var index = markerArray.length;
            var newMarker = _createMarker(new GLatLng(lat, lng), index, title, description, _createIcon(iconURL));
            markerArray.push(newMarker);
            
            return newMarker;
        }
        
        
        return null;
    }
    
    function _createMarker(latlng, idx, title, info, newIcon)
    {
        var marker = new GMarker(latlng, {icon: newIcon});
        marker.Index = idx;
        marker.Title = title;
        marker.Info = info;
        
        GEvent.addListener(marker, options.markerInfoEvent, function() {  
            _selectMarker(marker);
        });
        
        GEvent.addListener(marker, "infowindowopen", function() {  
            setTimeout(function(){
                canFireMapMoveEvent = true;
            },
            400);
        });
        
        return marker;
    }
    
    /*** Creates a GIcon with the specified image ***/
    function _createIcon(iconURL)
    {
        var icon = new GIcon();
        if(options.pinForceDefaultImage)
        {
            icon.image = options.pinBaseHref + options.pinDefaultImage;
        }
        else
        {
            icon.image = options.pinBaseHref + iconURL;
        }
        icon.shadow = options.pinBaseHref + options.pinDefaultShadow;
        icon.iconSize = new GSize(options.pinSize.width, options.pinSize.height);
        icon.shadowSize = new GSize(options.pinShadowSize.width, options.pinShadowSize.height);
        icon.iconAnchor = new GPoint(options.pinAnchor.x, options.pinAnchor.y);
        icon.infoWindowAnchor = new GPoint(options.infoWindowAnchor.x, options.infoWindowAnchor.y);
        
        return icon;
    }
    
    
    function _serializeNode(node)
    {
        if (typeof XMLSerializer != 'undefined') {
			return new XMLSerializer().serializeToString(node);
		}
		else if (typeof node.xml != 'undefined') {
			return node.xml;
		}
		else if (typeof printNode != 'undefined') {
			return printNode(node);
		}
		else {
			// might want to handle problem here
			return 'undefined';
		}
    }
    
    function _xmlFormat(str)
    {
        var result = str;
		
		result = result.replace(/&/g, "&amp;");
		result = result.replace(/</g, "&lt;");
		result = result.replace(/>/g, "&gt;");
		result = result.replace(/'/g, "&apos;");
		result = result.replace(/"/g, "&quot;");
		
		return result;
    }
    
    function _getSingleNodeValue(parentNode, xPath, defaultValue)
    {
        var nodes;
        var xPathArray = xPath.split('/');
        var val = defaultValue;
        
        if(xPathArray.length > 1)
        {
            nodes = parentNode.getElementsByTagName(xPathArray[0]);
            
            if(nodes.length !== 0)
            {
                val = _getSingleNodeValue(nodes[0], xPathArray.slice(1).join('/'), defaultValue);   
            }
        }
        else
        {   
            val = _getChildNodeValue(parentNode, xPath, defaultValue);
    
        }
     
        return val;
    }
    
    function _getChildNodeValue(parentNode, nodeName, defaultValue)
    {
        var nodes = parentNode.getElementsByTagName(nodeName);
        if(nodes.length !== 0)
        {
            return GXml.value(nodes[0]);
        }
        
        return defaultValue;
    }
    
    function _createGuyIcon()
    {
        var guyIcon = new GIcon(G_DEFAULT_ICON);
        guyIcon.image = "http://maps.google.com/intl/en_us/mapfiles/cb/man_arrow-0.png";
        guyIcon.transparent = "http://maps.google.com/intl/en_us/mapfiles/cb/man-pick.png";
        guyIcon.imageMap = [
        26,13, 30,14, 32,28, 27,28, 28,36, 18,35, 18,27, 16,26,
        16,20, 16,14, 19,13, 22,8
        ];
        guyIcon.iconSize = new GSize(49, 52);
        guyIcon.iconAnchor = new GPoint(25, 35);  // near base of guy's feet
        guyIcon.infoWindowAnchor = new GPoint(25, 5);  // top of guy's head
        
        return guyIcon;
    }
};

/**************************************************************
    Custom Map controls
 -------------------------------------------------------------
    Helium Map Type Control
**************************************************************/
function HeliumMapTypeControl(){}

HeliumMapTypeControl.prototype = new GControl();
HeliumMapTypeControl.prototype.initialize = function(gmap)
{        
    var mapTypeContainer = $("<div class='helium-maptypes'></div>");
    var mapType = null;
    var mapTypeArray = gmap.getMapTypes();
    var currentMapType = gmap.getCurrentMapType();
    
    for(var i=0; i < mapTypeArray.length; i++)
    {
        mapType = $("<span>"+mapTypeArray[i].getName(false)+"</span>");
        mapType.hover(
            function(){$(this).addClass("over");},
            function(){$(this).removeClass("over");}
            );
        mapType.click(function(){
            var typeArray = gmap.getMapTypes();
            var idx = $("div.helium-maptypes > span").index(this);
            gmap.setMapType(typeArray[idx]);
            $(this).parent().find("span.active").removeClass("active");
            $(this).addClass("active");
            
            return false;
        });
        
        if(currentMapType.getName(false) == mapTypeArray[i].getName(false))
        {
            mapType.addClass("active");
        }
        mapTypeContainer.append(mapType);
    }
    
    mapTypeContainer.find("span:first").attr("id", "map-type-first");
    mapTypeContainer.find("span:last").attr("id", "map-type-last");
    
    gmap.getContainer().appendChild(mapTypeContainer.get(0));
    return mapTypeContainer.get(0);
};

HeliumMapTypeControl.prototype.getDefaultPosition = function() {
  return new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(0, 0));
};

/**************************************************************
    Helium Map Control
**************************************************************/
function HeliumMapControl(){}

HeliumMapControl.prototype = new GControl();
HeliumMapControl.prototype.initialize = function(gmap)
{        
    var container = $("<div class='helium-mapcontrols'><span class='up'></span><span class='reset'></span><span class='left'></span><span class='right'></span><span class='down'></span><span class='zoomin'></span><span class='zoomout'></span></div>");
    
    $("span", container).hover(
        function(){$(this).addClass("over");},
        function(){$(this).removeClass("over");}
    );
            
    $("span.up", container).click(function(){
        gmap.panDirection(0, 1);
    });
    
    $("span.down", container).click(function(){
        gmap.panDirection(0, -1);
    });
    
    $("span.left", container).click(function(){
        gmap.panDirection(1, 0);
    });
    
    $("span.right", container).click(function(){
        gmap.panDirection(-1, 0);
    });
    
    $("span.zoomin", container).click(function(){
        gmap.zoomIn();
    });
    
    $("span.zoomout", container).click(function(){
        gmap.zoomOut();
    });
    
    $("span.reset", container).click(function(){
        gmap.returnToSavedPosition();
    });
    
    gmap.getContainer().appendChild(container.get(0));
    return container.get(0);
};

HeliumMapControl.prototype.getDefaultPosition = function() {
  return new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(0, 0));
};

/**************************************************************
    HeliumGMapViewer JQuery Plugin
**************************************************************/
(function($) {
    $.fn.HeliumGMapViewer = function(options, mapXML){
        //set unload function
        $(window).unload = GUnload;
        
        return this.each(function(){
            
            if (GBrowserIsCompatible()) {
                
                var map = new HeliumGMap(this);
                map.setOptions(options);
                map.loadMap(mapXML);
                    
                $(this).data("heliumGMap", map);
            }
            
        });
    };
    
    $.HeliumGMapViewerGetMap = function(id, options){
        var elemId = id;
        var obj = null;
        
        if(elemId.indexOf('#') == -1)
        {
            elemId = "#" + elemId;
        }
        
        obj = $(elemId).data("heliumGMap");
        
        if(options) {
            $.extend(obj.options, options);
            $(elemId).data("heliumGMap", obj);    
        }
            
        return obj;
    };
})(jQuery);