var ignoreNextMapChange = false;

function KMLNSResolver( prefix )
{
  if( prefix == 'kml' ) return "http://earth.google.com/kml/2.0";
  return null;
}

//Represents a KML feed. Used internally by KMLHandler.
function KMLFeed( map, url )
{
  this.map = map;
  this.url = url;
  this.overlays = new Array();
  this.request = undefined;
  this.updates = false;

  this.handleEvent = function()
  {
    //Request completed?
    if( this.request != null && this.request.readyState == 4 )
    {
      this.overlays = new Array();
      this.map.clearOverlays();

      this.parseResult(this.request.responseXML);

      this.request = undefined;
    }
  }

  this.parseResult = function( xml )
  {
    if( xml == null )
    {
      return;
    }
    for( var node = xml.firstChild; node != null; node = node.nextSibling )
    {
      if( node.nodeName == "Placemark" )
      {
        this.parsePlacemark(node);
      }
      else
      {
        this.parseResult(node);
      }
    }
  }

  this.parsePlacemark = function( xml )
  {
    var point = null;
    var opts = new Object();
    opts.clickable = true;
    var name = null;
    var description = null;

    for( var node = xml.firstChild; node != null; node = node.nextSibling )
    {
      if( node.nodeName == "Style" )
      {
        var subnode;
        for( subnode = node.firstChild; subnode != null; subnode = subnode.nextSibling )
        {
          if( subnode.nodeName == "IconStyle" )
          {
            break;
          }
        }
        for( subnode = subnode.firstChild; subnode != null; subnode = subnode.nextSibling )
        {
          if( subnode.nodeName == "Icon" )
          {
            break;
          }
        }
        var icon = new GIcon();
        icon.image = "root://icons/palette-1.png";
        var iconX = -1, iconY = -1, iconW = -1, iconH = -1;
        icon.infoWindowAnchor = new GPoint(0, 0);
        for( subnode = subnode.firstChild; subnode != null; subnode = subnode.nextSibling )
        {
          if( subnode.nodeName == "href" )
          {
            icon.image = subnode.firstChild.nodeValue;
          }
          else if( subnode.nodeName == "x" )
          {
            iconX = subnode.firstChild.nodeValue;
          }
          else if( subnode.nodeName == "y" )
          {
            iconY = subnode.firstChild.nodeValue;
          }
          else if( subnode.nodeName == "w" )
          {
            iconW = subnode.firstChild.nodeValue;
          }
          else if( subnode.nodeName == "h" )
          {
            iconH = subnode.firstChild.nodeValue;
          }
        }
        if( iconW >= 0 && iconH >= 0 )
        {
          icon.iconSize = new GSize(iconW, iconH);
        }
        else
        {
          icon.iconSize = new GSize(32, 32);
        }
        if( iconX >= 0 && iconY >= 0 )
        {
          icon.iconAnchor = new GPoint(iconX, iconY);
        }
        else
        {
          icon.iconAnchor = new GPoint(0, 0);
        }
        opts.icon = icon;
      }
      else if( node.nodeName == "Point" )
      {
        var subnode;
        for( subnode = node.firstChild; subnode != null; subnode = subnode.nextSibling )
        {
          if( subnode.nodeName == "coordinates" )
          {
            var coords = subnode.firstChild.nodeValue;
            coords = coords.split(",");
            point = new GPoint(parseFloat(coords[0]), parseFloat(coords[1]));
          }
        }
      }
      else if( node.nodeName == "name" )
      {
        name = node.firstChild.nodeValue;
      }
      else if( node.nodeName == "description" )
      {
        description = node.firstChild.nodeValue;
      }
    }

    if( name != null )
    {
      opts.title = name;
    }
    if( point != null )
    {
      var marker = new GMarker(point, opts);
      if( description != null )
      {
        var info_html = "<div clss='infowindow'>" + description + "</div>";
        GEvent.addListener(marker, "click", function()
        {
          ignoreNextMapChange = true;
          marker.openInfoWindowHtml(info_html);
        });
      }

      map.addOverlay(marker);
      this.overlays.push(marker);
    }
  }

  this.loadMapData = function()
  {
    if( ignoreNextMapChange )
    {
      ignoreNextMapChange = false;
      return;
    }
    //Trigger fetching the new map
    if( this.request != undefined )
    {
      this.request.abort();
    }

    url = this.url;
    bounds = map.getBounds();

    if( url.indexOf("?") == -1 )
    {
      url = url + "?BBOX=" + bounds.getSouthWest().lng() + "," + bounds.getSouthWest().lat() + "," + bounds.getNorthEast().lng() + "," + bounds.getNorthEast().lat();
    }
    else
    {
      url = url + "&BBOX=" + bounds.getSouthWest().lng() + "," + bounds.getSouthWest().lat() + "," + bounds.getNorthEast().lng() + "," + bounds.getNorthEast().lat();
    }

    this.request = GXmlHttp.create();
    var thisObject = this;
    this.request.onreadystatechange = function ()
    {
      thisObject.handleEvent();
    }
    this.request.open('GET', url, true);

    this.request.send(null);
  }

  this.destroy = function()
  {
    GEvent.removeListener(this.moveendListener);

    for( var i in this.overlays ) this.overlays[i].destroy();
  }

  this.setUpdates = function( updates )
  {
    this.updates = updates;
  }

  //Fetch the feed for the first time
  this.loadMapData();

  //Add event handlers
  var thisObject = this;
  this.moveendListener = GEvent.addListener(map, 'moveend', function ()
  {
    if( thisObject.updates )
    {
      thisObject.loadMapData();
    }
  });
}

//A KMLHandler handles (fetching, updating) KML feeds for a map
function KMLHandler( map )
{
  this.map = map;
  this.feeds = [];
  this.updates = false;

  this.addFeed = function( url )
  {
    //Add the feed to the feeds array
    this.feeds[url] = new KMLFeed(this.map, url);
    this.feeds[url].setUpdates( this.updates );
  }

  this.removeFeed = function( url )
  {
    //Remove the feed from the feeds array
    this.feeds[url].destroy();
    delete this.feeds[url];
  }

  this.setUpdates = function( updates )
  {
    this.updates = updates;
    for( var i=0; i<this.feeds.length; i++ )
    {
      this.feeds[i].setUpdates( updates );
    }
  }
}
