/**
 * addAddressToMap() is called when the geocoder returns an
 * answer.  It adds a marker to the map with an open info window
 * showing the nicely formatted version of the address and the country code.
 * 
 * @param {Gmap Object} reponse - server response from google
 * 
 */      
function addAddressToMap( response ) 
{
  map.clearOverlays();
  if (!response || response.Status.code != 200) 
  {
    alert("Sorry, we were unable to retrieve that address");
  } 
  else 
  {
    place = response.Placemark[0];
    point = new GLatLng( place.Point.coordinates[1], place.Point.coordinates[0] );

    // tabbed windows or single window to display
    if( isArray(html_window_element_div) )
    {
      var marker = createTabbedMarker(point, html_window_element_div, html_window_element_div_tab_names);
      map.addOverlay(marker);
    }
    else
    {
      marker = new GMarker(point);
      map.addOverlay(marker);
      marker.openInfoWindowHtml($(html_window_element_div).innerHTML);
    }
    map.setCenter( new google.maps.LatLng( place.Point.coordinates[1], place.Point.coordinates[0] ), 11 );
  }
}

// showLocation() is called when you click on the Search button
// in the form.  It geocodes the address entered into the form
// and adds a marker to the map at that location.
var html_window_element_div = '';
var html_window_element_div_tab_names = '';

/**
 * This function receives a raw address, decodes it and
 * sends the google server response to addAddressToMap function.
 * This also sets the 
 * 
 * @param {string} address                 - Raw address.
 * @param {element}    update_element          - element we will be pulling the infobox information from 
 * @param {global} html_window_element_div - This is where the div that contains the infobox.
 *                                           We will point to this element before calling the geocode
 */
function findLocation( geocoder_instance, address, update_element ) 
{
  html_window_element_div = update_element;
  geocoder_instance.getLocations( address, addAddressToMap );
}

function findLocationTabbed( geocoder_instance, address, update_elements, tab_titles )
{
  html_window_element_div = update_elements;
  html_window_element_div_tab_names = tab_titles;
  geocoder_instance.getLocations( address, addAddressToMap );  
}


/**
 * Used for updating map display without ajax
 * 
 * @param {Gmap Object} map_instance - the gmap instance we will be updating
 * @param {integer}     lat          - updated latitude
 * @param {integer}     lng          - updated longitude
 * @param {integer}     zoom         - new zoom level of the map
 * 
 */
function updateMapView( map_instance, lat, lng, zoom )
{
  map_instance.clearOverlays();
  map_instance.addOverlay( new google.maps.Marker( new google.maps.LatLng( lat, lng ) ) );
  map_instance.setCenter( new google.maps.LatLng( lat, lng ), zoom );
}

/**
 * Used to decode an array of raw addresses
 * 
 * @param {Geocode Object} geocode_instance - the gmap instance that will be pushed to updateArrayMapView
 * @param {Gmap Object}    map_instance     - the gmap instance that will be pushed to updateArrayMapView
 * @param {array}          array_address    - array of raw addresses
 * @param {array}          array_infobox    - array of DOM elements that contain infobox
 * @param {integer}        zoom             - map zoom level
 */
var sfAtlpoint = '';
var decodeArrayMap_geocode_instance = '';
function decodeArrayMap( geocode_instance, map_instance,  array_address, array_infobox, zoom )
{
  var lat         = new Array();
  var lng         = new Array();
  var infobox     = new Array();
  var tmp_results = new Array();    
  var delay       = 1000;
  decodeArrayMap_geocode_instance = geocode_instance;
  
  // keep a consistent array of elements, in case geocoder returns false
  var counter = 0;
  
  // decode each address in array
  for( i=0; i < array_address.length; ++i )
  {
    // store if found true
    setTimeout( 'getGeoCodeInfo( \'' + array_address[i] + '\' );', delay );
    
    if( sfAtlpoint != false && sfAtlpoint != null )
    {
      lat[ counter ]     = sfAtlpoint[ 0 ];
      lng[ counter ]     = sfAtlpoint[ 1 ];
      infobox[ counter ] = array_infobox[ i ];
      ++counter;      
    }
  }
  // send them over to be plotted
  updateArrayMapView( map_instance, lat, lng, infobox, zoom );
}
function getGeoCodeInfo( address )
{
  decodeArrayMap_geocode_instance.getLocations( address, returnAddressLatLng );
}

/**
 * Google server side response with decoded address information
 * 
 * @param  {Object} response - google server response
 * @return {array}           - array of latatude and logtitude 
 */
function returnAddressLatLng( response )
{
  if( !response || response.Status.code != 200 ) 
  {
    sfAtlpoint = false;
    return false;
  } 
  else 
  {
    place = response.Placemark[0];
    
    var point = new Array();
    point[0] = place.Point.coordinates[1];
    point[1] = place.Point.coordinates[0];
    sfAtlpoint = point;
    return sfAtlpoint;
  }  
}

/**
 * Updates the map with an array of points provided
 * 
 * @param {Object} map_instance   - gmap instance to be updated
 * @param {array}  lat_array      - array of latitudes
 * @param {array}  lng_array      - array of longitudes
 * @param {array}  info_box_array - array of div elements for infoboxes
 * @param {Object} zoom           - desired zoomlevel
 */
function updateArrayMapView( map_instance, lat_array, lng_array, info_box_array, zoom )
{
  map_instance.clearOverlays();
  
  for(i=0; i < lat_array.length; i++)
  {
    point  = new GLatLng( lat_array[i], lng_array[i] );
    marker = new GMarker( point );
    marker.openInfoWindowHtml( info_box_array[i].innerHTML );
    map_instance.addOverlay( marker );

  }
  map_instance.setCenter( new google.maps.LatLng( lat_array[0], lng_array[0] ), zoom );
}

//chk if an object is an array or not.
function isArray(obj) 
{
//returns true is it is an array
if (obj.constructor.toString().indexOf('Array') == -1)
return false;
else
return true;
}

function createTabbedMarker( point, htmls, labels ) {
  var marker = new GMarker(point);
  GEvent.addListener(marker, "click", function() {
    // adjust the width so that the info window is large enough for this many tabs
    if (htmls.length > 2) {
      htmls[0] = '<div style="width:'+htmls.length*88+'px">' + htmls[0] + '<\/div>';
    }
    var tabs = [];
    for (var i=0; i<htmls.length; i++) {
      tabs.push(new GInfoWindowTab(labels[i],$(htmls[i]).innerHTML));
    }
    marker.openInfoWindowTabsHtml(tabs);
  });
  return marker;
}

function computeAngle(endLatLng, startLatLng) 
{
  var DEGREE_PER_RADIAN = 57.2957795;
  var RADIAN_PER_DEGREE = 0.017453;

  var dlat = endLatLng.lat() - startLatLng.lat();
  var dlng = endLatLng.lng() - startLatLng.lng();
  // We multiply dlng with cos(endLat), since the two points are very closeby,
  // so we assume their cos values are approximately equal.
  var yaw = Math.atan2(dlng * Math.cos(endLatLng.lat() * RADIAN_PER_DEGREE), dlat)
         * DEGREE_PER_RADIAN;
  return wrapAngle(yaw);
}

function wrapAngle(angle) 
{
  if ( angle >= 360 ) 
  {
    angle -= 360;
  } 
  else if ( angle < 0 ) 
  {
   angle += 360;
  }
  return angle;
}

// used as global placeholders for the gmaps class for street view
var gmap_street_pano;
var gmap_street_lat_lng;
function showPanoData(panoData) {
  if (panoData.code != 200) {
    //GLog.write('showPanoData: Server rejected with code: ' + panoData.code);
    return;
  }
  var angle = computeAngle(gmap_street_lat_lng, panoData.location.latlng);
  gmap_street_pano.setLocationAndPOV(panoData.location.latlng, {yaw: angle});
}

