﻿//Global variables defining various map objects
var map = null;
var webSessionID = null;
var enable_be = false;
var firstClick = true;
var moveShape = null;
var SetPinID = null;
var popupShape = null;
var control;
var width;
var height;
var isFindNearby;
var icon = null;
var firstLoad = false;
var pageIndex = 1;
var isPagingEnabled = false;
var bestMapViewArray = null;
var setMapView = false;
var isPaging = false;
var locLat = 0;
var locLng = 0;
var panOrZoom = false;

/**
* Initializes the map based on the various control property values set
* @param (String) mapID The id given to the place holder for the map in the .aspx page 
* @param (Enum) mapType An enumerated type representing the map view (road, aerial, hybrid, or birdseye)
* @param (Enum) navControlType An enumerated type representing the size of the map (normal, small, or tiny)
* @param (Int) zoomLevel The zoomLevel at which the map is set ranging from 1 to 19
* @param (String) centerlatitude The latitude coordinate in which the map will be centered
* @param (String) centerlongitude The longitude coordinate in which the map will be centered
* @param (Bool) showFindControl specifies whether or not to display the find control on the map
* @param (Bool) findNearby specifies whether a search from the find control will populate nearby results  
*               on the map based on a Mappoint Web Service datasource and properties set
* @param (String) sizeWidth The width of the map
* @param (String) sizeHeight The height of the map
* @param (string) pagingDivID ID of the container to render paging Html
*/
function GetMap(mapID, mapType, navControlType, zoomLevel, centerlatitude, centerlongitude, showFindControl, findNearby, sizeWidth, sizeHeight, pagingDivID)
{    
    try
    {
        // Initialize the map object
        map = new VEMap(mapID);

        webSessionID = mapID.toString();
        firstLoad = true;
        // Setting Nav Control Dashboard to normal, small, or tiny
        if (navControlType == "0")
        {
            map.SetDashboardSize(VEDashboardSize.Normal);
        }
        else if (navControlType == "1")
        {
            map.SetDashboardSize(VEDashboardSize.Small);
        }
        else if (navControlType == "2")
        {
            map.SetDashboardSize(VEDashboardSize.Tiny);
        }      
        // Set map zoom level      
        if (zoomLevel > 19)
        {
            zoomLevel = 19;
        }
        else if (zoomLevel < 1)
        {
            zoomLevel = 1;
        }
        
        // Parse values to double
        centerlatitude = parseFloat(centerlatitude);
        centerlongitude = parseFloat(centerlongitude);
        
        // Load the map on the page
        map.LoadMap(new VELatLong(centerlatitude, centerlongitude), zoomLevel);            
       
       
       
        //attach event for panning or zoooming; 
        map.AttachEvent("onchangeview",MapPanOrZoom);

        // Size the map on the page according to the SizeWidth and SizeHeight properties
        SizeMap(sizeWidth, sizeHeight);
        
        // Diplay the find control
        if (showFindControl)
        {
            ShowFindControl();
        }  
        isFindNearby = findNearby;
        
        // Attach onobliqueenter Event Handler - event fires when birdseye is available
        map.AttachEvent("onobliqueenter", check_birdseye);

        // Setting map type to road, aerial, hybrid, or birdseye - should do this after LoadMap
        if (mapType == "0")
        {
            map.SetMapStyle(VEMapStyle.Road);
        }         
        else if (mapType == "1")
        {
            map.SetMapStyle(VEMapStyle.Aerial);
        }        
        else if (mapType == "2")
        {
            map.SetMapStyle(VEMapStyle.Hybrid);
        }
        else if (mapType == "3")
        {
            enable_be = true;            
        }
        var x = parent.document.getElementsByTagName("link");
        if (x != null)
        {
            var oNewItem = document.createElement("link");	
            oNewItem.rel = "Stylesheet";
            oNewItem.href = "InfoBoxStyles.css";
            x[0].insertAdjacentElement("afterEnd",oNewItem);
        }

        // Set paging enabled to true if an ID of a DIV element was set
        if (pagingDivID != null)
        {
            isPagingEnabled = true;
        }
    }
    catch (e)
    {
        alert(e.message);
    }
}

/**
* Places a single pin at the specified PinLatitude and PinLongitude properties 
* @param (String) pinlatitude The latitude coordinate of the pin to place 
* @param (String) pinlongitude The longitude coordinate of the pin to place
* @param (String) pintitle The title of the mouseover popup box of the pin
* @param (String) pindescription The description of the mouseover popup box of the pin
* @param (String) iconPath The relative path that stores the icon image of the push pin
*/
function SetLocation(pinlatitude, pinlongitude, pintitle, pindescription, iconPath)
{  
    // If icon path is not specified, use default push pin icon 
    if (iconPath == "")
    {
        icon = null;
    }
    else
    {
        icon = iconPath;
    }
    
    // If user does not want to place a pin on the map, the PinLatitude and PinLongitude properties should be set to 0
    if (pinlatitude != 0 && pinlongitude != 0)
    {
        try
        {
            pinlatitude = parseFloat(pinlatitude);
            pinlongitude = parseFloat(pinlongitude);
            pin = new VEShape(VEShapeType.Pushpin, new VELatLong(pinlatitude, pinlongitude));
            pin.SetTitle(pintitle);
            pin.SetDescription(pindescription);
            pin.SetCustomIcon(icon);
            map.AddShape(pin); 
            
            // Storing the internal pin id to access it if user decides to modify its properties
            SetPinID = pin.GetID();
            
            // Event that allows the pin location to be changed on a right mouse click
            map.AttachEvent("onclick", ClickLatLong);  
            
            // Event that allows the pin description to be changed on a left double click
            map.AttachEvent("ondoubleclick", ChangeDesc); 
        }
        catch (e)
        {
            alert(e.message);
        }
    }
}

/**
* Sizes the map right after it is loaded according to specified property values
* @param (String) sizeWidth The desired width of the map 
* @param (String) sizeHeight The desired height of the map
*/
function SizeMap(sizeWidth, sizeHeight) 
{
    // Global width and height variables also used to determine Find Control location
    width = parseInt(sizeWidth);  
    height = parseInt(sizeHeight);
    if ((width != "" && height != "") && (!isNaN(width) && !isNaN(height)))
    {
        map.Resize(width, height);
    }
}

/**
* Event handler that is fired when a mouse button is clicked. 
* If the right mouse button is clicked once on the placed pin, the pin will lifted from the map.
* The user can then move the pin to another location and drop the pin by right clicking the mouse a second time.
* The web service method StarbucksWS.StarbucksMiniLocatorWS.CorrectLocation is then called to store the new 
* latitude and longitude coordinates of the pin in current session object in the back-end which will be
* accessible to a developer 
*/
function ClickLatLong(e)  
{
   if ((e.elementID != null) && e.rightMouseButton && ((e.elementID).match(SetPinID) == SetPinID) && firstClick)
    {
        moveShape = map.GetShapeByID(e.elementID);    
        firstClick = !firstClick;   
        
        // Event that allows the push pin to follow the mouse cursor          
        map.AttachEvent("onmousemove", MoveLatLong);
    }
    // Occurs when the right mouse button is clicked a second time dropping the pin back on the map at a new location
    else if (e.rightMouseButton && !firstClick)
    {     
        var x = e.mapX;
        var y = e.mapY;
        var pixel = new VEPixel(x, y);
        var LL = map.PixelToLatLong(pixel);    
        moveShape.SetPoints(LL);          
        firstClick = !firstClick;      
        
        // Calls web service to propogate new coordinates to the current session control object
        // The new coordinates are also set in the NewPinCoords call back function  
        StarbucksMiniLocatorLibrary.StarbucksMiniLocatorWS.CorrectLocation(parseFloat(LL.Latitude), parseFloat(LL.Longitude), webSessionID, NewPinCoords);                    
    }
    else if (e.elementID == null)
    {
        return true;
    }
}

/**
* Event handler that is fired when the mouse is moved 
* This function dynamically updates the latitude and longitude of the push pin after it has been right clicked 
* and lifted from the map.
*/
function MoveLatLong(e) 
{   
    if (!firstClick)
    {
        var x = e.mapX;
        var y = e.mapY;
        pixel = new VEPixel(x, y);
        var LL = map.PixelToLatLong(pixel);    
        moveShape.SetPoints(LL);
    }
}

/**
* A call back function from the web service that sets the new push pin coordinates to the local front-end latitude and longitude properties
* @param (Array) results the object that stores the new latitude and longitude values
*/
function NewPinCoords(results)
{
    if (results != null)
    {
        Starbucks.MiniLocatorControl.prototype.set_pinlatitude(results[0]);
        Starbucks.MiniLocatorControl.prototype.set_pinlongitude(results[1]);
    }
}

/**
* Event handler that is fired when the mouse is doubled clicked. 
* When the left mouse button is doubled clicked on the previously placed push pin, the web service
* StarbucksWS.StarbucksMiniLocatorWS.MouseOverEvent is called which then calls a delegate to a function that
* must be defined by a developer.  This method will store a new PinTitle and PinDescription in the current session object
*/
function ChangeDesc(e)
{
   if ((e.elementID != null) && (e.elementID).match(SetPinID) == SetPinID)
    {
        popupShape = map.GetShapeByID(e.elementID); 
        var popUpTitle = popupShape.GetTitle();
        var popUpDesc = popupShape.GetDescription();
        StarbucksMiniLocatorLibrary.StarbucksMiniLocatorWS.MouseOverEvent(popUpTitle, popUpDesc, webSessionID, PopUpChange);
        return true;
    }
}

/**
* The call back function that sets the shape's current title and description to its new values.
* This change will be reflected immediately on mouse hover of the push pin.
* @param (Array) results the string array that stores the new title and description
*/
function PopUpChange(results)
{
    if (results != null)
    {
        popupShape.SetTitle(results[0]);
        popupShape.SetDescription(results[1]);
    }
}

/**
* This event is fired when a user enters a region on the map where a birdseye view is available.
*/
function check_birdseye(e)
{
    if (enable_be)
    {
        map.SetMapStyle(VEMapStyle.Birdseye);    
        enable_be = false;  
    }
}

/**
* Creates a find control by embedding an HTML Div tag and placing it on the map area
*/
 function ShowFindControl()
 {
    var control = document.createElement("div"); 
    control.id = "FindControl";
    control.style.top = "335px";
    control.style.left = "1px"; 
    control.style.width = width;
    control.style.background = "White";
    // BUGBUG - Right now, if showFindControl is true, we can't allow ambiguous results
    control.innerHTML = "<input type=\"text\" id=\"loc\" style=\"font-size:0.8em;color:#676767;width:360px;padding-left:1px\" value=\"Enter city, address or landmark\" onblur=\"SetLocatorDefault()\" onfocus=\"ClearLocatorDefault()\" />&nbsp;<input type=\"image\" src=\"Images/goBtn.gif\" onclick=\"FindLoc('loc', false);\" align=\"top\" alt=\"Go\"/>";  
    map.AddControl(control);
 }

/**
* Invoked by the onclick of the Find Control.  
* The map will center to the address or location entered in the text box of the Find Control
* @param (string) destination A where or what value recognized by Virtual Earth's Find function
* @param (bool) showAmbigResults Set to true to render ambiguous results
*/
function FindLoc(destination, showAmbigResults)
{
    if (!ValidateLocatorSearch())
        return;
    try
    {        
        // Reset page index
        pageIndex = 1;
        
        desc = document.getElementById(destination).value;
                
        if (showAmbigResults)
        {            
            map.Find(
            null,           // what
            desc,           // where
            null,           // find type
            null,           // shape layer  
            null,           // start index
            null,           // number of results
            false,          // show results
            false,          // create results
            false,          // default disambig
            false,          // set best map view
            GetLocations_ShowAmbig // callback
            );

        }
        else
        {
            map.Find(null, desc, null, null, null, null, null, null, null, null, GetLocations);
        }
    }
    catch(e)
    {
        alert(e.message);
    }
}

/**
* A call back method from FindLoc() that places a push pin where the found location is.    
* If the developer set property FindNearby is true, the web service method StarbucksWS.StarbucksMiniLocatorWS.FindNearby
* is called.  This will populate nearby push pins on the map based on developer set properties such as DataSourceName,
* DataSourceURL, DataSourceFilter, DataSourceRadius along with the Mappoint Webservice Credentials 
*/
function GetLocations()
{   
    pinlatitude = map.GetCenter().Latitude;
    pinlongitude = map.GetCenter().Longitude;
}

/**
* The call back method if the developer enables a Find Nearby search
* @param (string) results A string returned from the StarbucksWS.StarbucksMiniLocatorWS.FindNearby web service method whose value
* is either various "AddPin()" calls or an error message.  
*/
function FindNearbyCallBack(results)
{
    try
    {  
        // reset array of pins being rendered on map
        bestMapViewArray = new Array();

        map.Clear();
      
        // adds pins and renders paging html
        eval(results);
        
        if (!panOrZoom)
        {
            if (bestMapViewArray.length < 2)
            {
                // No results found
                bestMapViewArray.push(new VELatLong(locLat, locLng));
            }
            setMapView = true;      
            map.SetMapView(bestMapViewArray);  
        }
        
        isPaging = false;
        panOrZoom = false;
    }
    catch(e)
    { 
        alert(e.message + ', ' + results);
    }
}

/**
* Used by the FindNearby Web Service call back to add push pin icons on the map
* @param (string) lat The latitude coordinate of a resulting push pin
* @param (string) long The longitude coordinate of a resulting push pin
* @param (string) description an HTML formatted string retrieved from the property FindResultDescription that
*                 is to be set by the developer to customize the appearance of the pop up boxes of the results.
*/
function AddPin(lat, lng, description, pinNumber)
{
    shape = new VEShape(VEShapeType.Pushpin, new VELatLong(lat, lng));  
    shape.SetTitle("");
    shape.SetDescription(description);   
    shape.SetCustomIcon(icon);
    try
    {
        map.AddShape(shape);
        if (isPagingEnabled)
        {
            shape.SetCustomIcon("<div class='pinStyleStore' ><div class='text'>" + pinNumber + "</div></div>"); 
        }
        
        bestMapViewArray.push(new VELatLong(lat, lng));
                    
    }
    catch (err)
    {
        alert(err);
    }
}

/**
* Called when the map is zoomed.
**/
function MapPanOrZoom(e)
{
    if (setMapView) 
    {
        setMapView = false;
        return;
    }
    
    map.Clear();
        
    pageIndex = 1;

    if (!firstLoad)
    {
        if(isFindNearby)
        {            
            panOrZoom = true;
            
            dist = GetSearchDist(map.GetMapView());
            
            if (isPagingEnabled)
            {
                StarbucksMiniLocatorLibrary.StarbucksMiniLocatorWS.FindNearbyWithPaging(map.GetCenter().Latitude, map.GetCenter().Longitude, dist, webSessionID, pageIndex, FindNearbyCallBack);
            }
            else
            {
                StarbucksMiniLocatorLibrary.StarbucksMiniLocatorWS.FindNearby(map.GetCenter().Latitude, map.GetCenter().Longitude, webSessionID, FindNearbyCallBack);
            }
            
        }  
    }
    else
    {
        firstLoad = false;
    }
}

/**
* Called after find function to render paging elements.
* @param (string) pageDiv The ID of the paging <DIV> element.
* @param (int) start The starting index.
* @param (int) end The ending index.
* @param (int) total The total number of results found.
**/
function RenderPaging(pageDiv, start, end, total)
{
    var pagingDiv = document.getElementById(pageDiv);
    
    var prevHtml = "";
    var nextHtml = "";
    if (pageIndex > 1)
        prevHtml = '<a href="javascript: goToPage(-1);"><<</a>';
    if (end < total)
        nextHtml = '<a href="javascript: goToPage(1);">>></a>';
        
    pagingDiv.innerHTML = '<span class="copytext">' + prevHtml + '&nbsp;Showing ' + start + ' - ' + end + ' of ' + total + '&nbsp;' + nextHtml + '</span>';
}

/**
* Called when user clicks paging controls.
* @param (int) pageIncrement Paging increment.
**/
function goToPage(pageIncrement)
{    

    isPaging = true;
    pageIndex += pageIncrement;
    
    map.Clear();
    if (isFindNearby)
    {       
        dist = GetSearchDist(map.GetMapView()); 
        if (isPagingEnabled)
        {
            StarbucksMiniLocatorLibrary.StarbucksMiniLocatorWS.FindNearbyWithPaging(locLat, locLng, dist, webSessionID, pageIndex, FindNearbyCallBack);
        }
        else
        {
            StarbucksMiniLocatorLibrary.StarbucksMiniLocatorWS.FindNearby(map.GetCenter().Latitude, map.GetCenter().Longitude, webSessionID, FindNearbyCallBack);
        }
    }
}

 /**
* Callback when location(s) found.  Handles ambiguous results as needed.
* @param (VEShapeLayer) layer 
* @param (VEFindResult[]) results
* @param (VEPlace[]) places
* @param (bool) hasmore : Indicates whether there are more results after the current set.
*/
function GetLocations_ShowAmbig(layer, results, places, hasmore)
{
    if ((places == null) || (typeof places == 'undefined') || places.length == 0)
    {
        alert('places empty!');
    }
    
    ambigResults = places;
    
    if (places.length > 1)
    {
        
        // fill ambigs with links to results page.
        var ambigHTML = "Did you mean... <br/><table>";
        
        // TODO: replace inline style with CSS styles.
        var formatString = "<tr><td style='vertical-align:top;'>{0}</td><td><a href='javascript: onFoundResultsHelper({1});'>{2}</a></td></tr>";
        for (var i = 0; i < places.length; i++)
        {
            ambigHTML += formatString.replace("{0}", i+1).replace("{1}", i).replace("{2}", places[i].Name);
        }
        ambigHTML += "</table>";
        
        var ambigDiv = document.getElementById('ambigStoreList');
        ambigDiv.innerHTML = ambigHTML;
        ambigDiv.style.display = "block";
    }
    else
    {
        onFoundResultsHelper(0);
    }
}

 /**
* Click event when user selects location from ambiguous results.
* @param (int) index : Index of selected item.
*/
function onFoundResultsHelper(index)
{
    locLat = ambigResults[index].LatLong.Latitude;
    locLng = ambigResults[index].LatLong.Longitude;
        
    var ambigDiv = document.getElementById('ambigStoreList');
    ambigDiv.style.display = "none";
    
    if (isPagingEnabled)
    {
        StarbucksMiniLocatorLibrary.StarbucksMiniLocatorWS.FindNearbyWithPaging(locLat, locLng, 0, webSessionID, pageIndex, FindNearbyCallBack);
    }
    else
    {
        StarbucksMiniLocatorLibrary.StarbucksMiniLocatorWS.FindNearby(locLat, locLng, webSessionID, FindNearbyCallBack);
    }
}
///////////////////////////
function GetSearchDist(mapView)
{
    var rect = mapView;
    var d;
    if ((typeof distCosineLaw != 'undefined') && (distCosineLaw != null))
    {
        // find 1/2 the width of the current zoom
        try
        {
            d = Math.min(250, Math.ceil(distCosineLaw(rect.TopLeftLatLong.Latitude, rect.TopLeftLatLong.Longitude, rect.BottomRightLatLong.Latitude, rect.BottomRightLatLong.Longitude) * .5));
        }
        catch(e)
        {
           alert(e.message);
        }
        
    }
    else
    {
        d = 1;
    }
    
    return d;
}

// Calc the distance between two locations
function distCosineLaw(lat1, lon1, lat2, lon2) 
{
  var R = 6371; // earth's mean radius in km
  var d;
  // formula:  http://www.census.gov/cgi-bin/geo/gisfaq?Q5.1
  d = Math.acos(Math.sin(toRad(lat1))*Math.sin(toRad(lat2)) + Math.cos(toRad(lat1))*Math.cos(toRad(lat2))*Math.cos(toRad(lon2-lon1))) * R;
  return d * 0.621371192; // convert to miles
}

function toRad(v) 
{
    return (v * Math.PI / 180);
}

if(typeof(Sys)!=='undefined')Sys.Application.notifyScriptLoaded();