/**
 * Wrap object for @class GMap2
 * @constructor
 */
function Map() {
  /**
   * GMap2 Object
   */
  var _map;

  /**
   * Hash with Marker Object User ID as the key
   */
  var _markers = {};

  /**
   * Marker opened by InfoWindow
   */
  var _openMarker;

  /**
   * Base icon
   */
  var _baseIcon = function() {
    var icon = new GIcon();
    icon.iconSize = new GSize(32,32);
    icon.iconAnchor = new GPoint(16,16);
    icon.infoWindowAnchor = new GPoint(16, 24);
    //icon.shadowSize = new GSize(59, 32);

    return icon;
  }();

  /**
   * Default Center coordinate
   *  Hokkaido 
   *  Tohoku
   *  Kanto
   *  Kinki
   *  Shikoku-Chugoku
   *  Kyusyu
   *  OKinawa
   */
  var _centers = [
                  new GLatLng(43.47684, 142.624512),
                  new GLatLng(39.13006, 140.482178),
                  new GLatLng(35.626047, 140.119629),
                  new GLatLng(34.606085, 135.582275),
                  new GLatLng(34.116352, 133.395996),
                  new GLatLng(32.532921, 130.874634),
                  new GLatLng(27.186242, 128.452148)
                  ];
  
  /**
   * Hash with retrieved kml layer name as the key
   */
  var _kmlLayers = {};

  /**
   * Hash with retrieved GroundOverlay layer name as the key
   */
  var _groundLayers = {};

  /**
   * Define where to read kml
   */
  var _kmls = {
    'co2': 'http://greenprojectco2.googlepages.com/co2.kml',
    'product_per_pref': 'http://greenprojectco2.googlepages.com/product_per_pref.kml',
    'powerplant_hydro': 'http://greenprojectco2.googlepages.com/powerplant_hydro.kml',
    'powerplant_solar': 'http://greenprojectco2.googlepages.com/powerplant_solar.kml',
    'powerplant_geothermal': 'http://greenprojectco2.googlepages.com/powerplant_geothermal.kml',
    'powerplant_wind': 'http://greenprojectco2.googlepages.com/powerplant_wind.kml',
    'powerplant_biomass': 'http://greenprojectco2.googlepages.com/powerplant_biomass.kml'
  };

  /**
   * Define where to read GroundOverlay and coordination
   */
  var _grounds = {
    'co2': {
      'image': 'images/layer/co2.png',
      's': 24.43578924489374,
      'w': 122.14599609375,
      'n': 47.004167162931844,
      'e': 150.88623046875
    }
  };

  /**
   * As GControl cannot be referenced before reading MapAPI, 
   * this is set as a private class
   * GControl @class div#toolbar
   * @private
   * @constructor
   */
  function LayerControl() {
    this.initialize = function(map) {
      var overlay = document.getElementById('toolbar');
      map.getContainer().appendChild(overlay);
      return overlay;
    }
    
    /**
     * position of toolbar
     * 7px to left, down to 35 px from the upper right hand side of the maps
     */
    this.getDefaultPosition = function() {
      return new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(7, 35));
    }
  }
  LayerControl.prototype = new GControl();

  /**
   * Initialize the Maps
   */
  this.init = function() {
    var center = _centers[Math.floor(Math.random() * _centers.length)];

    _map = new GMap2(document.getElementById("map"));
    _map.addControl(new GLargeMapControl());
    _map.addControl(new GMapTypeControl());
    _map.setMapType(G_SATELLITE_MAP);
    _map.setCenter(center, 7);

    if (document.getElementById('toolbar')) {
      _map.addControl(new LayerControl(false, false));
    }
  }

  /**
   * Point the marker of entry user
   * @param {Object} entry
   */
  this.point = function(entry) {
    var map = new GMap2(document.getElementById("map"));
    map.setCenter(new GLatLng(entry.lat, entry.lon), 4);
    map.setMapType(G_SATELLITE_MAP);

    var icon = new GIcon(_baseIcon, 'images/icons/' + entry.icon);
    var marker = new GMarker(new GLatLng(entry.lat, entry.lon), icon);
    map.addOverlay(marker);
  }

  /**
   * Plot Marker
   * @param {Array} entries -Entry Alignment
   * Marker user ID that opens @param {number} userId infoWindow
   */
  this.plot = function(entries, userId) {
    for (var i = 0; i < entries.length; i++) {
      var user_id = entries[i].user_id;
      _markers[user_id] = new Marker(this, entries[i], _baseIcon);
    }
    if (userId) {
      this.openInfo(userId);
    }
  }

  /**
   * Fit to the marker that is plotted with the zoom level
   * Pan to 1/4 screen to the right from the center
   * by adjust to one level broader range from getBoundsZoomLevel
   * so that it does not block toolbar
   */
  this.fitZoom = function() {
    var bounds = new GLatLngBounds();
    var count = 0;
    for (var k in _markers) {
      var marker = _markers[k];
      bounds.extend(marker.getPoint());
      count++;
    }
    if (count > 1) {
      var zoom = _map.getBoundsZoomLevel(bounds) - 1;
      if (zoom < 2) {
        zoom = 2;
      }
      _map.setZoom(zoom);
      var center = bounds.getCenter();
      var quarter = new GLatLngBounds();
      quarter.extend(center);
      quarter.extend(bounds.getNorthEast());
      _map.panTo(new GLatLng(center.lat(), quarter.getCenter().lng()));
    }
  }
  
  /**
   * Only after the initial load of the maps
   * Open InfoWindow of the Marker of the logged in user
   * @param {number} userId User ID
   * @param {boolean} open Open InfoWindow or not
   */
  this.plotLoginUser = function(userId, open) {
    if (_markers[userId]) {
      if (open) {
        this.openInfo(userId);
      }
    } else {
      var url = Green.API + 'baloon_profile';
      var params = {user_id: userId, timedelta: Green.TIMEDELTA};
      var json = new JSON();
      
      var self = this;

      /**
       * Call back api:baloon_profile
       * Define as Closure to reference userId
       */
      function _plotLoginUser(entry) {
        entry.user_id = userId;
        _markers[userId] = new Marker(self, entry, _baseIcon);
        if (open) {
          self.openInfo(userId);
        }
      }
      
      json.load(url, _plotLoginUser, params);
    }
  }

  /**
   * Delete Marker
   * Let it Jump to Marker Object once
   *because does not know GMarker Object that it wants to delete
   */
  this.removeMarkers = function() {
    for (var k in _markers) {
      _markers[k].remove();
    }
    _markers = {};
  }

  /**
   * Execution unit of delete marker
   * @param {GMarker} marker GMarker object
   */
  this.removeMarker = function(marker) {
    _map.removeOverlay(marker);
  }

  /**
   * Run addOverlay of GMap2 object
   * @param {GMarker} marker Marker object
   */
  this.addOverlay = function(marker) {
    _map.addOverlay(marker);
  }

  /**
   * switching to InfoWindow
   * @param {number} userId
   */
  this.openInfo = function(userId) {
    if (_markers[userId]) {
      this.markerOnClick(_markers[userId]);
    } else {
      var url = Green.API + 'baloon_profile';
      var params = {user_id: userId, timedelta: Green.TIMEDELTA};
      var json = new JSON();
      
      var self = this;

      /**
       * Call back api:baloon_profile
       * Define as Closure to reference userId
       */
      function _baloon_profile_callback(entry) {
        entry.user_id = userId;
        _markers[userId] = new Marker(self, entry, _baseIcon);
        self.openInfo(userId);
      }
      
      json.load(url, _baloon_profile_callback, params);
    }
  }

  /**
   * force InfoWindow into run
   * @param {number} userId
   * @param {boolean} error do_approval
   */
  this.reopenInfo = function(userId, error) {
    if (_markers[userId]) {
      _markers[userId].open(error);
    }
  }

  /**
   * switching to InfoWindow
   * @param {Marker} marker Marker object
   */
  this.markerOnClick = function(marker) {
    if (_openMarker == marker) {
      marker.close();
      _openMarker = null;
    } else {
      marker.open();
      _openMarker = marker;
    }
  }

  /**
   * Run after closing InfoWindow
   */
  this.markerOnClose = function() {
    _openMarker = null;
  }

  /**
   * read kml data
   * @param {string} name
   */
  this.loadLayer = function(name) {
    var layer;
    if (_kmlLayers[name]) {
      layer = _kmlLayers[name];
    } else {
      layer = new GGeoXml(_kmls[name]);
      _kmlLayers[name] = layer;
    }
    _map.addOverlay(layer);
  }

  /**
   * read GroundOverlay
   * @param {string} name
   */
  this.loadGroundLayer = function(name) {
    var layer;
    if (_groundLayers[name]) {
      layer = _groundLayers[name];
    } else {
      var data = _grounds[name];
      var sw = new GLatLng(data.s, data.w);
      var ne = new GLatLng(data.n, data.e);
      layer = new GGroundOverlay(data.image, new GLatLngBounds(sw, ne));
      _groundLayers[name] = layer;
    }
    _map.addOverlay(layer);
  }

  /**
   * hide layer
   * @param {string} name
   */
  this.removeLayer = function(name) {
    if (_kmlLayers[name]) {
      _map.removeOverlay(_kmlLayers[name]);
    }
    if (_groundLayers[name]) {
      _map.removeOverlay(_groundLayers[name]);
    }
  }

  /**
   * Switch the ScrollWheelZoom enable
   * @param {boolean} enable Enable or not
   */
  this.switchScrollWheelZoom = function(enable) {
    if (enable) {
      _map.enableScrollWheelZoom();
    } else {
      _map.disableScrollWheelZoom();
    }
  }
}

/**
 * lap object of @class GMarker
 * @constructor
 * @param {Map} map Ma object
 * @param {Object} entry
 * @param {GIcon} baseIcon
 */
function Marker(map, entry, baseIcon) {

  var _self = this;

  /**
   * GMarker object
   */
  var _marker;

  function _init() {
    var icon = new GIcon(baseIcon, 'images/icons/' + entry.icon);

    _marker = new GMarker(new GLatLng(entry.lat, entry.lon), icon);
    map.addOverlay(_marker);

    GEvent.addListener(_marker, 'click', _onClick);
    GEvent.addListener(_marker, 'infowindowclose', _onClose);
  }

  function _onClick() {
    map.markerOnClick(_self);
  }

  function _onClose() {
    map.markerOnClose();
  }

  /**
   * Open InfoWindow
   * @param {boolean} error api:do_approval
   */
  this.open = function(error) {
    var url = Green.API + 'baloon_profile';
    var params = {user_id: entry.user_id, timedelta: Green.TIMEDELTA};
    var json = new JSON();

    /**
     * call back to api:baloon_profile
     * Define as Closure to check error
     * @param {Object} profile
     */
    function _open(profile) {
      for (var k in profile) {
        entry[k] = profile[k];
      }
      var html = new HTML();
      var str = html.markerInfo(entry, error);
      _marker.openInfoWindowHtml(str);
    }
    json.load(url, _open, params);
  }

  /**
   * Close InfoWindow
   */
  this.close = function() {
    _marker.closeInfoWindow();
  }

  /**
   * Delete marker
   */
  this.remove = function() {
    map.removeMarker(_marker);
  }

  /**
   * Get marker position
   * @return {GLatLng} marker position
   */
  this.getPoint = function() {
    return _marker.getPoint();
  }

  _init();
}

