// ====================================================================
// 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
//
// MarkerCategory.js
//
// DESCRIPTION:
//
//   A Gaijin's Guide to Central Yokohama, MarkerCategory class
//
// MODIFICATIONS:
//
//   Josh Glover <jmglov@jmglov.net> (2005/11/01): Initial revision
// ====================================================================


// Object: MarkerCategory
//
// Category of markers


// Constructor: MarkerCategory()
//
// Parameters:
//
//   catalogue - MarkerCatalogue to which this category belongs
//   name      - name of this category
//   vis       - [optional; default: true] is category visible? 
//   gmap      - [optional] GMap object associated with this catalogue

function MarkerCategory( catalogue, name, vis, gmap ) {

  if (name == null) { return null }

  this.catalogue = catalogue;
  this.colour    = null;
  this.gmap      = gmap;
  this.markers   = new Array();
  this.name      = name;
  this.size      = 0;
  this.visible   = vis != null ? vis : true;

  this.addMarker        = catAddMarker;
  this.addNewMarker     = catAddNewMarker;
  this.delMarker        = catDelMarker;
  this.getCatalogue     = catGetCatalogue;
  this.getColour        = catGetColour;
  this.getGMap          = catGetGMap;
  this.getMarker        = catGetMarker;
  this.getMarkerAtIndex = catGetMarker;
  this.getMarkers       = catGetMarkers;
  this.getName          = catGetName;
  this.getNumMarkers    = catGetNumMarkers;
  this.getVisibility    = catGetVisibility;
  this.populateFromXML  = catPopulateFromXML;
  this.renameMarker     = catRenameMarker;
  this.setCatalogue     = catSetCatalogue;
  this.setColour        = catSetColour;
  this.setGMap          = catSetGMap;
  this.setName          = catSetName;
  this.setVisibility    = catSetVisibility;
  this.updateMarker     = catUpdateMarker;

} // MarkerCategory()


// Method: addMarker()
//
// Adds a new marker to this category. <addMarker()> *will not* overwrite an
// existing marker; if this is what you want, use <updateMarker()> instead.
//
// You probably do not need to call this method directly from user code; use
// <MarkerCatalogue::addMarker()> instead.
// 
// Parameters:
//
//   marker - Marker object
//   name   - [optional; default: name of marker param] name of the marker
//
// Returns:
//
//   Newly added Marker object on success; null on failure

function catAddMarker( marker, name ) {

  if (marker == null) { return null }

  if (name == null) { name = marker.getName() }
  if (name == null || name == '') { return null }

  // No overwriting existing markers!
  if (this.markers[name] != null) { return null }

  // Set the marker's category and add it
  marker.setCategory( this.getName() );
  this.markers[name] = marker;
  this.size++;

  return marker;

} // catAddMarker()


// Method: addNewMarker()
//
// Creates a new Marker object and adds it to this category. Like
// <addMarker()>, <addNewMarker()> *will not* overwrite an existing marker.
//
// You probably do not need to call this method directly from user code; use
// <MarkerCatalogue::addNewMarker()> instead.
// 
// Parameters:
//
//   Same as for <Marker::Marker()>
//
// Returns:
//
//   Newly added Marker object on success; null on failure

function catAddNewMarker( name, title, gmarker, info, date ) {

  var marker = new Marker( this.getCatalogue(), this.getName(), name, title,
                           this.getVisibility(), gmarker, this.getGMap(),
                           info, date );

  return this.addMarker( marker );

} // catAddNewMarker()


// Method: delMarker()
//
// Deletes a marker from this category.
//
// You probably do not need to call this method directly from user code; use
// <MarkerCatalogue::delMarker()> instead.
// 
// Parameters:
//
//   name - name of the marker
//
// Returns:
//
//   Marker object that was just deleted from this category on success;
//   null on failure

function catDelMarker( name ) {

  var marker = this.getMarker( name );
  if (marker == null) { return null }

  this.markers[name] = null;
  this.size--;

  // The marker no longer belongs to a category
  marker.clearCategory();

  return marker;

} // catDelMarker()


// Method: getCatalogue()
//
// Gets the catalogue to which this category belongs.
//
// Returns:
//
//   The MarkerCatalogue object on success, null on failure

function catGetCatalogue() {

  return this.catalogue;

} // catGetCatalogue()


// Method: getColour()
//
// Gets the colour of this category.
//
// Returns:
//
//   The colour of this category on success, null on failure

function catGetColour() {

  return this.colour;

} // catGetColour()


// Method: getGMap()
//
// Gets the GMap object associated with this category.
//
// Returns:
//
//   GMap object on success, null on failure

function catGetGMap() {

  return this.gmap;

} // catGetGMap()


// Method: getMarker()
//
// Gets a marker from this category.
//
// You probably do not need to call this method directly from user code; use
// <MarkerCatalogue::getMarker()> instead.
// 
// Parameters:
//
//   name - name of the marker
//
// Returns:
//
//   Marker object on success; null on failure

function catGetMarker( name ) {

  if (name == null || this.markers[name] == null) { return null }

  return this.markers[name];

} // catGetMarker()


// Method: getMarkerAtIndex()
//
// Gets a marker from this category at an alpabetically sorted index.
//
// You probably do not need to call this method directly from user code; use
// <MarkerCatalogue::getMarkerAtIndex()> instead.
// 
// Parameters:
//
//   index - [optional; default: 0] index
//
// Returns:
//
//   Marker object on success; null on failure

function catGetMarkerAtIndex( index ) {

  if (index == null) { index = 0 }
  
  if (this.getNumMarkers() == 0) { return null }
  
  var keys = new Array();
  for (k in this.markers) { keys.push( k ) }

  return keys.sort()[index];

} // catGetMarkerAtIndex()


// Method: getMarkers()
//
// Gets all markers in this category.
//
// You probably do not need to call this method directly from user code; use
// <MarkerCatalogue::getMarkers()> instead.
// 
// Returns:
//
//   Associative array of Marker objects on success; null on failure

function catGetMarkers() {

  return this.markers;

} // catGetMarkers()


// Method: getName()
//
// Gets the name of this category.
//
// Returns:
//
//   The name of this category on success, null on failure

function catGetName() {

  return this.name;

} // catGetName()


// Method: getNumMarkers()
//
// Returns the number of markers in this category.
//
// Returns:
//
//   Number of markers on success, 0 on failure

function catGetNumMarkers() {

  if (this.markers == null) { return 0 }

  return this.size;

} // catGetNumCategories()


// Method: getVisibility()
//
// Gets the visibility of this category.
//
// Returns:
//
//   The visibility of this category on success, null on failure

function catGetVisibility() {

  return this.visible;

} // catGetVisibility()


// Method: populateFromXML()
//
// Populates a category from a chunk of an XML DOM object. Note that you probably
// do not need to call <populateFromXML()> directly; see
// <MarkerCatalogue::populateFromXML()>.
//
// Parameters:
//
//   xml - chunk of XML DOM object
//
// Returns:
//
//   The category that was populated on success, null on failure

function catPopulateFromXML( xml ) {

  if (xml == null) { return null }

  var name   = xml.getAttribute( "name" );
  var colour = xml.getAttribute( "colour" );
  var vis_s  = xml.getAttribute( "visible" );

  // JavaScript does not seem to have a parseBoolean() function (*real*
  // orthogonal, folks!), so we have to roll our own
  var vis;
  if      (vis_s == "true")  { vis = true  }
  else if (vis_s == "false") { vis = false }

  if (name == null || colour == null) { return null }

  this.setName( name );
  this.setColour( colour );
  if (vis != null) { this.setVisibility( vis ) }

  var xml_marker =
    xml.getElementsByTagName( "marker" );

  // Having no markers in a category is strange, but not an error; if this is the
  // case, just return this, as our work is done
  if (xml_marker == null) { return this }

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

    // We have to set some bullshit values since the constructor requires several
    // parameters. This hack will go away when I figure out static class methods.
    var m = new Marker( this.getCatalogue(), name, '', '', this.getVisibility(),
                        null, this.getGMap() );
    if (m.populateFromXML( xml_marker[i] ) == null) { return null }

    if (this.addMarker( m ) == null) { return null }

    // Add an overlay for this marker if this category is visible
    if (vis == true) { map.addOverlay( m.getGMarker() ) }

  } // for (populating categories)

  return this;

} // catPopulateFromXML()


// Method: catRenameMarker()
//
// Renames a marker that is already stored in this category.
//
// Paramters:
//
//   cur_name - current name of the marker
//   new_name - new name of the marker
//
// Returns:
//
//   The new Marker object on success; null on failure

function catRenameMarker( cur_name, new_name ) {

  if (cur_name == null || new_name == null ||
      cur_name == '' || new_name == '') { return null }

  var marker = this.getMarker( cur_name );
  if (marker.setName( new_name ) == null) { return null }
  this.markers[cur_name] = null;
  this.markers[new_name] = marker;

  return marker;

} // catRenameMarker()


// Method: setCatalogue()
//
// Sets the catalogue to which this category belongs.
//
// Parameters:
//
//   catalogue - MarkerCatalogue object
//
// Returns:
//
//   The new MarkerCatalogue object on success, null on failure

function catSetCatalogue( catalogue ) {

  this.catalogue = catalogue;
  return this.catalogue;

} // catSetCatalogue()


// Method: catSetColour()
//
// Sets the colour of this category.
//
// Paramters:
//
//   colour - new colour of category
//
// Returns:
//
//   The new colour of this category on success, null on failure

function catSetColour( colour ) {

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

  this.colour = colour;
  
  return this.colour;

} // catSetColour()


// Method: setGMap()
//
// Sets the GMap object associated with this category.
//
// Parameters:
//
//   gmap - GMap object
//
// Returns:
//
//   New GMap object on success, null on failure

function catSetGMap( gmap ) {

  this.gmap = gmap;
  return this.gmap;

} // catSetGMap()


// Method: catSetName()
//
// Sets the name of this category.
//
// Paramters:
//
//   name - new name of category
//
// Returns:
//
//   The new name of this category on success, null on failure

function catSetName( name ) {

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

  this.name = name;
  
  return this.name;

} // catSetName()


// Method: setVisibility()
//
// Sets the visibility of this category.
//
// Parameters:
//
//   vis - new visibility
//
// Returns:
//
//   The new visibility of this category on success, null on failure

function catSetVisibility( vis ) {

  if (vis == null) { return null }

  this.visible = vis;

  return this.visible;

} // catSetVisibility()


// Method: catUpdateMarker()
//
// Updates a marker that is already stored in this category. <updateMarker()>
// *will not* create a new marker if it does not already exist; use <addMarker()>
// instead.
//
// Paramters:
//
//   name    - name of the marker
//   title   - [optional] title
//   vis     - [optional] visibility
//   gmarker - [optional] GMarker object
//   gmap    - [optional] GMap object
//   info    - [optional] info
//   date    - [optional] date
//
// Returns:
//
//   The new Marker object on success; null on failure

function catUpdateMarker( name, title, vis, gmarker, gmap, info, date ) {

  var marker = this.getMarker( name );
  if (marker == null) { return null }

  if (title   != null) { marker.setTitle( title )     }
  if (vis     != null) { marker.setVisibility( vis )  }
  if (gmarker != null) { marker.setGMarker( gmarker ) }
  if (gmap    != null) { marker.setGMap( gmap )       }
  if (info    != null) { marker.setInfo( info )       }
  if (date    != null) { marker.setDate( date )       }

  return marker;

} // catUpdateMarker()
