// Generates a tooltip element in a given class name, etc.
// Provides a way to modify text
//
function TooltipElement(args) {
  if(args) {
    this.class_name=args.class_name;
  }

  var div=document.createElement('div');
  div.className=this.class_name ? this.class_name : 'tooltip';
  div.style.position='absolute';
  div.style.visibility='hidden';
  div.style.display='block';
  var txt=document.createTextNode('xxx');
  div.appendChild(txt);

  this.elt=div;
  this.txt=txt;
}

TooltipElement.prototype.setText = function(text) {
  this.txt.data=text;
}

// Custom map overlay that automatically sets element width & position
//
function MapOverlay(args) {
  if(args) {
    this.pane_code=args.pane_code;
    this.elt=args.elt;
  }
}

MapOverlay.prototype = new GOverlay();

MapOverlay.prototype.setCenter = function(center) {
  this.center=center;
}

MapOverlay.prototype.initialize = function(map) {
  this.map=map;
  if(!this.elt) return;
  map.getPane(this.pane_code ? this.pane_code : G_MAP_FLOAT_PANE).appendChild(this.elt);
}

// Remove the main DIV from the map pane
MapOverlay.prototype.remove = function() {
  if(!this.elt) return;
  this.elt.style.display='none';
}

// Copy our data to a new MapOverlay
MapOverlay.prototype.copy = function() {
  return new MapOverlay(this);
}

// Redraw the rectangle based on the current projection and zoom level
MapOverlay.prototype.redraw = function(force) {
  if(!force) return;
  if(!this.elt) return;
  if(!this.center) return;

  this.elt.style.visibility='hidden';
  this.elt.style.display='block';

  var w=this.map.getSize().width;
  if(w>300) w=200;
  else if(w>80) w=w*0.75;
  else return;
  if(this.elt.style.width != (w+'px')) this.elt.style.width=w+'px';
  w=this.elt.offsetWidth;

  var c=this.map.fromLatLngToDivPixel(this.center);
  var br=this.map.fromLatLngToDivPixel(this.map.getBounds().getNorthEast()).x;
  var bl=this.map.fromLatLngToDivPixel(this.map.getBounds().getSouthWest()).x;

  var y=(c.y+5);
  var x=Math.floor(c.x-w/2);
  if(br-bl>w) {
    if(x+w+10>=br) x=br-w-10;
    else if(x-10<=bl) x=bl+10;
  }

  this.elt.style.left=x+'px';
  this.elt.style.top=y+'px';

  this.elt.style.visibility='visible';
}

// Map wrapper that takes care of showing places, etc..
function WTMap(args) {
  this.type=args.type ? args.type : 'large';
  this.marker_list=new Array();
  this.loc_updating=false;

  var self=this;

  if(!GBrowserIsCompatible()) return;

  var zoom=args.zoom;
  var center;
  if(args.latitude!=null && args.longitude!=null) {
    center=new GLatLng(args.latitude,args.longitude);
  }
  else {
    center=new GLatLng(15,0);
    zoom=1;
  }

  if(!zoom && !args.span) {
      zoom=14;
  }

  var elt=args.map_elt;
  if(!elt && args.map_elt_id) {
    elt=document.getElementById(args.map_elt_id);
  }
  if(!elt) {
    alert('Error: no map_elt and no map_elt_id');
    return;
  }

  var map=new GMap2(elt);
  self.map=map;

  if(args.type == 'small') {
    map.addControl(new GSmallZoomControl());
  }
  else {
    map.addControl(new GLargeMapControl());
    map.addControl(new GMapTypeControl());
    map.addControl(new GScaleControl());
    if(!args.no_overview) map.addControl(new GOverviewMapControl());
    GEvent.bind(map,'click',self,self.handleClick);
    new GKeyboardHandler(map);
  }

  if(!zoom && args.span) {
    var hspan=args.span/2;
    zoom=map.getBoundsZoomLevel(new GLatLngBounds(
      new GLatLng(center.lat()-hspan,center.lng()-hspan),
      new GLatLng(center.lat()+hspan,center.lng()+hspan)
    ));
  }

  map.setCenter(center,zoom,G_HYBRID_MAP);

  GEvent.bind(map,'moveend',self,self.handleMoveEnd);
  GEvent.bind(map,'movestart',self,self.handleMoveStart);

  self.handleMoveEnd();

  if(args.type=='small' && args.latitude!=null && args.longitude!=null) {
    self.marker_have_sticky=true;
    self.markerMergeList(new Array({
      latitude:     args.latitude,
      longitude:    args.longitude
    }),true);
    self.markerQueueStep();
  }
}

WTMap.prototype.tooltipShow=function(center,text) {
  var self=this;

  if(!self.tooltip) {
    self.tooltip_elt=new TooltipElement();
    self.tooltip=new MapOverlay({
      elt: self.tooltip_elt.elt
    });
  }

  self.tooltip_elt.setText(text);
  self.tooltip.setCenter(center);
  self.map.addOverlay(self.tooltip);
}

WTMap.prototype.tooltipHide=function() {
  var self=this;
  if(!self.tooltip) return;
  self.map.removeOverlay(self.tooltip);
}

WTMap.prototype.markerBuild=function(args) {
  var self=this;
  var mopts=new Object();
  mopts.clickable=(args.name || args.html) ? true : false;
  if(args.small) {
    if(!self.iconSmall) {
      var icon=new GIcon();
      icon.image='/images/map-icon-small.png';
      icon.iconSize=new GSize(10,10);
      icon.iconAnchor=new GPoint(5,5);
      WTMap.prototype.iconSmall=icon;
    }
    mopts.icon=self.iconSmall;
  }

  var center=new GLatLng(args.latitude,args.longitude);
  var marker=new GMarker(center,mopts);

  if(args.html) {
    marker.wt_html=args.html;
  }

  if(args.name) {
    GEvent.addListener(marker,'mouseover',function () {
      self.tooltipShow(center,args.name);
    });
    GEvent.addListener(marker,'mouseout',function () {
      self.tooltipHide(center,args.name);
    });
  }

  return marker;
}

WTMap.prototype.markerQueueStep=function() {
  var self=this;
  // GLog.write('Timer: delq='+self.marker_del_queue.length+' addq='+self.marker_add_queue.length);

  var have_dels;
  var deleted_idx;
  if(self.marker_del_queue.length) {
    var idx=self.marker_del_queue.shift();
    have_dels=self.marker_del_queue.length;

    self.map.removeOverlay(self.marker_list[idx]);

    self.marker_list[idx]=null;
    deleted_idx=idx;
  }

  var have_adds;
  if(self.marker_add_queue.length) {
    var minfo=self.marker_add_queue.shift();
    have_adds=self.marker_add_queue.length;
    // GLog.write('Adding marker @'+minfo.latitude+','+minfo.longitude+', small='+minfo.small);

    var marker=self.markerBuild(minfo);
    self.map.addOverlay(marker);

    if(deleted_idx==null) {
      for(var i=0; i<self.marker_list.length; ++i) {
        if(!self.marker_list[i]) {
          deleted_idx=i;
          break;
        }
      }
    }
    if(deleted_idx==null) {
      self.marker_list.push(marker);
    }
    else {
      self.marker_list[deleted_idx]=marker;
    }
  }

  // var elt=document.getElementById('debug_ov_list');
  // if(elt) elt.value=self.marker_list.length;
  // elt=document.getElementById('debug_ov_qadd');
  // if(elt) elt.value=self.marker_add_queue.length;
  // elt=document.getElementById('debug_ov_qdel');
  // if(elt) elt.value=self.marker_del_queue.length;

  if(have_adds || have_dels) {
    self.markerQueueStart();
  }
}

WTMap.prototype.markerQueueStart=function() {
  var self=this;
  window.setTimeout(function() {
    self.markerQueueStep();
  },200);
}

WTMap.prototype.markerQueueClear=function() {
  var self=this;
  // GLog.write('Clearing updates');
  self.marker_add_queue=new Array();
  self.marker_del_queue=new Array();
}

WTMap.prototype.markerMergeList=function(mlist,dont_start) {
  var self=this;

  self.markerQueueClear();

  var elist=new Array();

  mlistLoop:
  for(var i=0; i<mlist.length; ++i) {
    var minfo=mlist[i];

    for(var j=0; j<self.marker_list.length; ++j) {
      if(!self.marker_list[j]) continue;
      var pt=self.marker_list[j].getPoint();
      if(Math.abs(pt.lat()-minfo.latitude)<0.00001 && Math.abs(pt.lng()-minfo.longitude)<0.00001) {
        // GLog.write('Existing marker @'+minfo.latitude+','+minfo.longitude);
        elist.push(self.marker_list[j]);
        continue mlistLoop;
      }
    }

    // GLog.write('New marker @'+minfo.latitude+','+minfo.longitude);
    self.marker_add_queue.push(minfo);
  }

  slistLoop:
  for(var i=(self.marker_have_sticky ? 1 : 0); i<self.marker_list.length; ++i) {
    if(!self.marker_list[i]) continue;
    for(var j=0; j<elist.length; ++j) {
      if(self.marker_list[i]==elist[j]) continue slistLoop;
    }
    // GLog.write('Removing #'+i+', '+self.marker_list[i]);
    self.marker_del_queue.push(i);
  }

  if(!dont_start) {
    // GLog.write('Merge: delq='+self.marker_del_queue.length+' addq='+self.marker_add_queue.length);
    if(self.marker_add_queue.length || self.marker_del_queue.length) {
      // GLog.write('Starting updates');
      self.markerQueueStart();
    }
  }
}

WTMap.prototype.handleBoxXML=function(req) {
  var self=this;

  var doc;
  if(req.readyState==4) {
    if(req.status==200) {
      doc=req.responseXML;
    }
  }
  if(!doc) return;

  try {
    var locs=doc.getElementsByTagName('Location');
    // GLog.write('Locations count: '+locs.length);
    var limit=(self.type=='large' ? 25 : 10);
    var mlist=new Array();
    for(var i=0; i<locs.length && mlist.length<limit; ++i) {
      var elt=locs[i];
      var lat=elt.getAttribute('latitude');
      var lng=elt.getAttribute('longitude');
      var name=elt.getAttribute('name');

      var infohtml;
      var ch=elt.getElementsByTagName('InfoHTML');
      if(ch.length) {
        infohtml=ch[0].firstChild.nodeValue;
      }

      mlist.push({
        latitude:  lat,
        longitude: lng,
        name:      name,
        html:      infohtml,
        small:     (self.type=='small')
      });
    }
    self.markerMergeList(mlist);
  }
  catch(e) {
    alert('got an error: '+e.message);
  }

  self.loc_updating=false;
  if(self.missed_move_update) {
    // GLog.write("Forced move update");
    self.missed_move_update=false;
    self.handleMoveEnd();
  }
}

WTMap.prototype.markerRefreshAll=function() {
  var self=this;

  if(self.loc_updating) {
    self.missed_move_update=true;
    return;
  }
  if(!self.map) return;

  window.setTimeout(function() { self.loc_updating=false; },5000);

  self.loc_updating=true;

  var bounds=self.map.getBounds();
  var sw=bounds.getSouthWest();
  var ne=bounds.getNorthEast();

  var url='/api/geo.xml?op=box&bs='+sw.lat()+'&bw='+sw.lng()+'&bn='+ne.lat()+'&be='+ne.lng();
  if(self.type=='large') url+='&details=1';
  url+='&limit='+(self.type=='large' ? 25 : 10);

  // if(document.getElementById('debug_api_url')) {
  //   document.getElementById('debug_api_url').value=url;
  // }

  var req=httpReqNew();
  httpReqGet(req,url,function() {
    self.handleBoxXML(req);
  });
}

WTMap.prototype.handleMoveStart=function() {
  var self=this;

  self.markerQueueClear();
  if(self.moveend_waiting) {
    window.clearTimeout(self.moveend_toid);
    self.moveend_waiting=false;
  }
}

WTMap.prototype.handleMoveEnd=function() {
  var self=this;

  if(self.moveend_waiting) {
    window.clearTimeout(self.moveend_toid);
  }
    
  self.moveend_waiting=true;
  self.moveend_toid=window.setTimeout(function() {
    // GLog.write("Requesting a map update");
    self.moveend_waiting=false;
    self.markerRefreshAll();
  },1000);
}

WTMap.prototype.handleClick=function(ov,pt) {
  var self=this;
  var w=self.map.getSize().width;
  if(w>100) w-=90;
  if(ov) {
    if(ov.wt_html) {
      ov.openInfoWindowHtml(
        ov.wt_html +
        '<DIV CLASS="mapInfoCoord">Loc. @'+ov.getPoint().toUrlValue()+'</DIV>',
        { maxWidth: w }
      );
    }
    return;
  }
  if(self.type!='small') {
    self.map.openInfoWindowHtml(pt,
      '&raquo;&nbsp;<A HREF="/account/place-add.html?position=' + pt.toUrlValue() + '">Create a place here</A><BR>' +
      '&raquo;&nbsp;<A HREF="/map/?lt='+pt.lat()+'&lg='+pt.lng()+'&zm='+self.map.getZoom()+'">Link to this point (permalink)</A>' +
      '<DIV CLASS="mapInfoCoord">Loc. @'+pt.toUrlValue()+'</DIV>',
      { maxWidth: w }
    );
  }
}

function google_map_directions(elt,lat,lng) {
  var dest=prompt('Please enter the starting address','');
  if(!dest.length) return false;
  elt.href='http://maps.google.com/maps?saddr='+encodeURI(dest)+'&daddr=@'+lat+','+lng;
  return true;
}

var registered_maps=new Array();

function register_map(args) {
  if(args.map_elt || args.map_elt_id) registered_maps.push(args);
}

function show_maps() {
  if(!GBrowserIsCompatible()) return;
  for(var i=0; i<registered_maps.length; ++i) {
    var mdesc=registered_maps[i];
    if(mdesc.outer_elt_id) {
      var outer=document.getElementById(mdesc.outer_elt_id);
      outer.style.display='block';
      outer.style.visibility='visible';
    }
    var map=new WTMap(mdesc);
    registered_maps[i]=map;
    if(!map || map.error) continue;
  }
}

register_onload_handler(show_maps);
register_onunload_handler(GUnload);
