//This is for the MapData interface

var XEMap = Class.create();
XEMap.getMapData = function(ob) {
  if(ob.MapData) {
    if (typeof(ob.MapData) == "function") {
      return ob.MapData()
    } else {
      return ob.MapData;
    }
  } else {
    return ob;
  }
};

XEMap.prototype = { 
  /**  
   * @ 
   */
  map: null,

  /**  */
  mapData: [],
  
  markerStore: [],

  /**  */
  mapDataHandlers: {},
  
  defaultSettings: {
    
  },
  
  /**
   * Creates a XEMap object. Currently can only use GMap2 but was intended to be subclassed if 
   * other APIs are required in the future.
   * 
   * @param canvasElement This is a block-level element to create the map in.
   * @param settings Object containing settings for the initial state of the map. This must
   * contain Lat and Lng at the very least.
   *
   */
  initialize: function(canvasElement, settings) {
    
    try {
     this.map = new GMap2(canvasElement);
    } catch(e) {
      throw "Google Maps is not available. Check internet connection and API key.";
      return;
    }
    this.settings = settings = Object.extend({
      MapControl: 'large', 
      ScaleControl: 'on', 
      MapTypeControl: 'on',
      Lat: "-25",
      Lng: "133",
      Zoom: 4
    }, settings ? settings : {});
    
    Event.observe(window, "unload", GUnload);
    
    // Use separate handlers to apply all the settings?
    this.resetControls();

    if (settings.DoubleClickZoom == 'on') {
      this.map.enableDoubleClickZoom();
    }
    
    if (settings.ScrollWheelZoom == 'on') {
      this.map.enableScrollWheelZoom();
    }

    if (settings.Dragging == 'off') {
      this.map.disableDragging();
    }

    if (settings.Lat && settings.Lng && settings.Zoom) {
      var mapCenter = new GLatLng(settings.Lat, settings.Lng);
      this.map.setCenter(mapCenter, parseInt(settings.Zoom));
    }
    
    if (settings.MapType) {
      this.map.setMapType(window[settings.MapType]);
    }
    this.map.checkResize();
  },
  
  controls: [],
  /**
   * Resets map controls to what is stored in settings.
   */
  resetControls: function() {
    var settings = this.settings;
    this.clearControls();
        // Use separate handlers to apply all the settings?
    switch (settings.MapControl) {
      case 'small':
        this.controls.push(new GSmallMapControl());
        break;
      case 'large':
        this.controls.push(new GLargeMapControl());
        break;
      case 'zoom-only':
        this.controls.push(new GSmallZoomControl());
        break;
      case 'off':
        break;
    }

    if (settings.ScaleControl == 'on') {
      this.controls.push(new GScaleControl());
    }

    if (settings.OverviewMapControl == 'on') {
      this.controls.push(new GOverviewMapControl());
    }

    if (settings.MapTypeControl == 'on') {
      this.controls.push(new GMapTypeControl());
    }
    
    this.controls.each(function(i) { this.map.addControl(i); }.bind(this));

  },
  
  /**
   * Clears all map controls.
   */
  clearControls: function() {
    this.controls.each(function(i) { this.map.removeControl(i); }.bind(this))
  },
  
  /**
   * Called when the window is resized or when the containing element is resized. This will
   * fix the rendering of the map while still retaining it's current centre point.
   */
  checkResize: function() {
    var centre = this.map.getCenter();
    this.map.checkResize();
  //  this.map.panTo(centre);
  },
  /**
   *
   */
/*  setMapData: function(mapData) {
    mapData = XEMap.getMapData(mapData);
    
    if(typeof(mapData) == "array") 
      this.mapData = mapData;
    else 
      this.mapData.push(mapData);
    this.refresh();
  },
  */
  /**
   * Take a takes a MapData object and passes it to the necessary MapDataHandler
   * 
   * @param mapData any object the ClassName property will be inspected and passed to the appropriate
   * handler
   * @param options Overrides any options specified in {@see initMapDataHandler}
   */
  addMapData: function(mapData, options) {
    options = options ? options : {};
    var marker = null;
    
    handler = this.getHandler(mapData);
    if(handler) {
      var optionsCopy = Object.clone(handler[2]); //Do this so the original options are always stored.
      var marker = handler[0].addMapDataHandler.call(this, handler[1], Object.extend(optionsCopy, options));
      if(marker) {
        this.storeMarker(mapData, marker);
      }
    }
    return marker;
  },
  
  /**
   * Removes given mapData from the map.
   * 
   * @param mapData to remove.
   */
  removeMapData: function(mapData) {
    var handler = this.getHandler(mapData);
    var marker = handler[3];
    if(handler) {
      handler[0].removeMapDataHandler.call(this, handler[1], marker);
      this.removeMarker(mapData);
    }
  },
  
  /**
   * @deprecated
   */
  getMapData: function() {
    if("console" in window) console.log("getMapData is deprecated");
    return this.mapData;
  },
 
 
 /**
  * Takes a mapData object and returns an array containing the appropriate {@see MapDataHandler}, 
  * mapData object (this will not necessarily be what is passed in) and an options object
  * 
  * 
  * @param mapData object to provide the handler for.
  * @return array containing 3 items, the data handler, a mapData Object (This will not necessarily
  *  be the same object that was passed in) and an options object
  */
  getHandler: function(mapData) {
    var marker = this.getMarker(mapData);
    var handler = this.mapDataHandlers[mapData.ClassName];
    if(!handler) {
      mapData = XEMap.getMapData(mapData); //XXX: Possibly don't overwrite mapdata
      handler = this.mapDataHandlers[mapData.ClassName]; 
    }
   
    if(handler) {
      return [handler[0], mapData, handler[1], marker];
    }
    return null;  
  },
  
  /**
   * Returns an implementation specific marker for provided mapData
   * @param mapData
   * @return GMarker 
   */
  getMarker: function(mapData) {
    var index = this.mapData.indexOf(mapData);
    if(index > -1) {
      return this.markerStore[index];
    } else {
      return null;
    }
  },
  
  removeMarker: function(mapData) {
    var index = this.mapData.indexOf(mapData);
    if(index > -1) {
      this.mapData[index] = null;
      this.markerStore[index] = null;
    } else {
      return null;
    }
  },
  storeMarker: function(mapData, marker) {
    var index = this.mapData.indexOf(mapData);
    if(index > -1) {
      this.markerStore[index] = marker;
    } else {
      this.mapData.push(mapData);
      this.markerStore[this.mapData.length-1] = marker;
    }
  },
  
  /**
   * Binds a mapDataHandler to a specific mapData class
   * @param String ClassName to apply this handler to.
   * @param MapDataHandler to be used to display specific data
   * @param Hash Options object to be used when displaying or adding data
   */
  addMapDataHandler: function(className, handler, options) {
    options = options ? options : {};
    this.mapDataHandlers[className] = [handler, options];
    
    if(handler.initMapDataHandler && typeof(handler.initMapDataHandler) == "function" ) {
      handler.initMapDataHandler.call(this, options);
    }
  },
  
  /**
   * Centres the map on specific mapData {@see centreOnMarker}
   */
  centreMapData: function(mapData) {
    var handler = this.getHandler(mapData);
    var marker = handler[3];
    if(marker === null) {
      console.error("MapData not found");
      return null;
    }
    handler[0].centreOnMarker.call(this, handler[1], marker);
  },
  
  /**
   * Unbinds the the MapDataHandler for ClassName 
   * 
   * @param string ClassName to unbind 
   */
  removeMapDataHandler: function(className) {
    this.mapDataHandlers[className] = undefined;
  },
  
  /**
   * Unbinds all MapDataHandlers
   */
  clearMapDataHandlers: function() {
    this.mapDataHandlers = {};
  },
  
  /**
   *
   */
  destroy: function() {
  }

};

XEMap.editor = XEMap.editor ? XEMap.editor : {};

XEMap.editor.initMapDataHandler = function(options) {
  GEvent.bind(this.map, "maptypechanged", this, function() {
    var mapType;
    switch(this.map.getCurrentMapType().getName()) {
      case 'Map':
        mapType = 'G_NORMAL_MAP';
        break;
      case 'Satellite':
        mapType = 'G_SATELLITE_MAP';
        break;
      case 'Hybrid':
        mapType = 'G_HYBRID_MAP';
        break;
    }
    options.callback({MapType: mapType});
  });
  
  GEvent.bind(this.map, "moveend", this, function() {
    var centre = this.map.getCenter();
    options.callback({Lat: centre.lat(), Lng: centre.lng()});
  });
};
