// ====================================================================
// Copyright (c) 2005 and onwards, Josh Glover <jmglov@jmglov.net>
//
// LICENCE:
//
//   This file is distributed under the terms of the BSD-2 License.
//   See the COPYING file, which should have been distributed
//   with this file, for details. If you did not receive the
//   COPYING file, see:
//
//   http://www.jmglov.net/opensource/licenses/bsd.txt
//
// map.js
//
// DESCRIPTION:
//
//   A Gaijin's Guide to Central Yokohama, main JavaScript functions
//
// MODIFICATIONS:
//
//   Josh Glover <jmglov@jmglov.net> (2005/11/01): Initial revision
// ====================================================================


// Variable: m_cat
//
// Global catalogue of markers

var m_cat;


// Variable: map
//
// GMap object

var map;


// Function: centreAndZoomFromParams()
//
// Extracts centre / zoom commands from params and executes them, if present.
//
// Parameters:
//
//   params - HTTP query parameters
//   zoom   - default zoom level
//
// Returns:
//
//   true if centre / zoom commands were executed, false otherwise

function centreAndZoomFromParams( params, zoom ) {

  if (params == null) { return false }

  // See if we have query parameters for our centring / zoom
  var p_query_cz = getCZFromQuery( params, zoom );
  if (p_query_cz != null) {

    map.centerAndZoom( p_query_cz['point'], p_query_cz['zoom'] );

    // Create a marker and open an info window if we have been told to do so
    if (p_query_cz['marker'] != null) {

      p_query_cz['marker'].getGMap().addOverlay( p_query_cz['marker'].getGMarker() );
      p_query_cz['marker'].showInfo( 'info' );

    } // if (showing marker)
    
    return true;

  } // if (centring and zooming)

  // If control reaches here, we found no centre / zoom commands in the parameters
  return false;

} // centreAndZoomFromParams()


// Function: getCZFromQuery()
//
// Gets centre / zoom commands from the query parameters, if possible.
//
// TODO:
//
//   Support centring on markers by name.
// 
// Parameters:
//
//   params - HTTP query parameters
//   d_zoom - default zoom level
//
// Returns:
//
//   If centre / zoom commands were specified, an associative array where
//   the ['point'] key specifies the centre point, the ['zoom'] key specifies the
//   zoom level, and the ['marker'] key is the Marker object over which to open
//   an info window; if no centre / zoom commands were specified, returns null

function getCZFromQuery( params, d_zoom ) {

  // Validate params
  if (params == null || d_zoom == null) { return null }

  var x = parseFloat( params['pos_lon'] );
  var y = parseFloat( params['pos_lat'] );
  var z = parseInt( params['pos_zoom'] );

  // Parse the FQMN (Fully-Qualified Marker Name--har har) if specified
  var cat  = null;
  var name = null;
  var fqmn = params['pos_marker'];
  if (fqmn != null) {

    var cat_name = fqmn.split( '.' );
    if (cat_name != null && cat_name.length == 2) {

      cat  = cat_name[0];
      name = cat_name[1];

    } // if (we have "cat.name")

  } // if (we have a "marker" param)

  var retval = new Array();

  // If we have x and y, use them as lat / long
  if (x != null && y != null && isNaN( x ) == false && isNaN( y ) == false) {

    retval['point'] = new GPoint( x, y );

  } // if (using x and y for coords)

  // Or, if we have a FQMN, look up the marker by name and use its x and y coords.
  // Also fill in the info window stuff if we were told to open one.
  else if (cat != null && name != null) {

    var m = m_cat.getMarker( cat, name );
    if (m == null) { return null }
    var gm = m.getGMarker();
    if (gm == null) { return null }
    
    retval['marker'] = m;
    retval['point']  = gm.point;

    if (params['pos_window'] == 'true') { retval['gmarker'] = gm }
      
  } // else if (using named marker's coords)

  // Otherwise, we have no coordinates, so just return null
  else { return null }

  // Grab the zoom or use the default
  retval['zoom']  = z != null && isNaN( z ) != true ? z : d_zoom;

  // If we do not yet have a ['marker'] key but we have at least the 'pos_title'
  // param, set up ['marker']
  if (retval['marker'] == null && params['pos_title'] != null) {

    // Add the marker to the catalogue in the 'ad-hoc' category
    retval['marker'] =
      m_cat.addNewMarker( 'ad-hoc', params['pos_title'].toLowerCase(),
                          params['pos_title'].replace( /-/g, ' ' ), true,
                          null, true, unescape( params['pos_info'] ) );

    if (retval['marker'] == null) { return null }

    retval['marker'].setGMarker( retval['marker'].createGMarker( retval['point'] ) );

    // Set the 'ad-hoc' category's colour
    m_cat.getCategory( 'ad-hoc' ).setColour( '#fffb08' );

  } // if (loading up ['window'])
  
  return retval;

} // getCZFromQuery()


// Function: loadInfoPanel()
//
// Populates the information panel.
//
// Parameters:
//
//   html - html to load into the panel

function loadInfoPanel( html ) {

  document.getElementById( "info" ).innerHTML = html;

} // loadInfoPanel()


// Function: onLoad()
//
// Loads the map.

function onLoad() {

  if (GBrowserIsCompatible() != true) {

    reportError( 'Sorry, your browser is not compatible with Google Maps. ' +
                 'Please try visiting this page with Firefox, Safari, ' +
                 'Konquerer, or (if you must) Internet Explorer 5.5 or newer.' );
    return;

  } // if (incompatible browser)

  // Let the user know that we are loading the page
  reportMessage( 'Loading map, please wait...' );

  // Parse the query parameters
  var q_params = parseQueryParams();

  map = new GMap( document.getElementById( "map" ) );

  // Add listeners that will update the latitude / longitude on click or move
  GEvent.addListener( map, 'click', function( overlay, point ) {

    document.getElementById( "pos_loc" ).innerHTML =
      "<b>Location:</b> Click";
    document.getElementById( "pos_lat" ).innerHTML =
      "<b>Latitude:</b> " + point.y.toFixed( 6 ) + "&deg;";
    document.getElementById( "pos_lon" ).innerHTML =
      "<b>Longitude:</b> " + point.x.toFixed( 6 ) + "&deg;";
    document.getElementById( "pos_zoom" ).innerHTML =
      "<b>Zoom Level:</b> " + map.getZoomLevel();

  });

  GEvent.addListener( map, "moveend", function() {

    var point = map.getCenterLatLng();

    document.getElementById( "pos_loc" ).innerHTML =
      "<b>Location:</b> Centre of map";
    document.getElementById( "pos_lat" ).innerHTML =
      "<b>Latitude:</b> " + point.y.toFixed( 6 ) + "&deg;";
    document.getElementById( "pos_lon" ).innerHTML =
      "<b>Longitude:</b> " + point.x.toFixed( 6 ) + "&deg;";
    document.getElementById( "pos_zoom" ).innerHTML =
      "<b>Zoom Level:</b> " + map.getZoomLevel();

  } );

  // Add map controls
  map.addControl( new GLargeMapControl() );
  map.addControl( new GMapTypeControl() );

  // Create the marker catalogue
  m_cat = new MarkerCatalogue( map );

  // Coordinates for Toriumi Haimu (as a fallback)
  var p_home  = new GPoint( 139.642860, 35.436669 );
  var centred = false;

  // If we have been told to suppress markers, mention it
  if (q_params['suppress-xml-markers'] == 'true') {

    reportMessage( 'Loading no markers from XML because of HTTP parameter: ' +
                   '"suppress-xml-markers=true"' );

    // Do the center-and-zoom dance from parameters
    centred = centreAndZoomFromParams( q_params, 3 );
    
  } // if (suppressing markers)

  // Otherwise, download the markers XML file and display markers
  else {

    var request = GXmlHttp.create();
    request.open( "GET", "markers.xml", true );
    request.onreadystatechange = function() {

      if (request.readyState == 4) {

        if (m_cat.populateFromXML( request.responseXML ) == null) {

          reportError( 'Could not load marker catalogue!' );
          return;
        
        } // if (could not populate the catalogue)

        // Do the center-and-zoom dance from parameters
        centred = centreAndZoomFromParams( q_params, 3 );
    
        document.getElementById( "categories" ).innerHTML =
          m_cat.getCategoryHTML( 3 );
      
        // If we have a marker in the "home" category, centre and zoom on it if
        // have not already centred and zoomed (in response to a query param)
        if (centred == false) {

          // Centre and zoom on the first marker in this category
          var m = m_cat.getMarkerAtIndex( 'home', 0 );
          if (m != null) {
          
            map.centerAndZoom( m.getGMarker().point, 3 );
            centred = true;

          } // if (we have a 'home' category)

        } // if (we have not yet centred)

        // Clear the message box
        reportMessage( 'Loaded ' + m_cat.getNumMarkers() + ' markers in ' +
                       m_cat.getNumCategories() + ' categories from XML file' );

      } // if (XML file retrieved)

    } // request.onreadystatechange()

    request.send( null );

  } // if (loading markers from XML file)
  
  // Centre and zoom to hard-coded home if we have not yet centred
  if (centred == false) {

    map.centerAndZoom( p_home, 3 );

  } // if (we have not yet centred)

} // onLoad()


// Function: parseQueryParams()
//
// Parses HTTP query parameters.
//
// Returns:
//
//   Associative array where keys are query parameters and values are
//   their values

function parseQueryParams() {

  var params = new Array();
  var query  = window.location.search.substring( 1 ).split( '&' );

  for (var i = 0; i < query.length; i++) {

    var pos = query[i].indexOf( '=' );

    // If we have a param, save the key and value
    if (pos > 0) {

      params[query[i].substring( 0, pos )] = query[i].substring( pos + 1 );

    } // if (we have a param)

  } // for (traversing query params)
    
  return params;

} // parseQueryParams()


// Function: reportError()
//
// Reports an error to the user.
//
// Parameters:
//
//   msg - error message

function reportError( msg ) {

  if (msg == null || msg == '') { return }

  return reportMessage( "<span style=\"color: red\">" + msg + "</span>" );

} // reportError()


// Function: reportMessage()
//
// Reports a message to the user.
//
// Parameters:
//
//   msg - error message

function reportMessage( msg ) {

  if (msg == null || msg == '') { return }
  
  document.getElementById( "message" ).innerHTML = msg;

} // reportMessage()


// Function: toggleCat()
//
// Toggles the visibility of markers of the given category
//
// Parameters:
//
//   cat - category to toggle

function toggleCat( cat ) {

  var vis = m_cat.getVisibility( cat );

  // Toggle all markers in the category
  var markers = m_cat.getMarkers( cat );
  for (key in markers) {

    var gmarker = markers[key].getGMarker();
    
    if (vis == true) {

      map.removeOverlay( gmarker );
      markers[key].setVisibility( false );

    } // if (visible -> invisible)
    
    else {

      map.addOverlay( gmarker );
      markers[key].setVisibility( true );

    } // else (invisible -> visible)

  } // for (traversing category)

  // If this category is currently visible, make it invisible
  if (vis == true) {

    // Close the info window if it is opened over a marker in this category
    if (m_cat.getOpenInfoWindow() == cat) { map.closeInfoWindow() }

    m_cat.setVisibility( cat, false );

  } // if (visible => invisible)

  // And vice-versa
  else { m_cat.setVisibility( cat, true ) }
  
} // toggleCat()
