/* ----------------------------------------------------------------------
 * Mapify javascripts
 * Copyright 2009, John Tajima
 * All rights reserved.
 * - rewritten to be compatible with jQuery
 * ----------------------------------------------------------------------
 */
var Mapify;
if ((typeof Mapify) == 'undefined') {
    var Mapify = {};
}

/* ----------------------------------------------------------------------
 * Messenger
 * ----------------------------------------------------------------------
 */

var Messenger = {  
	error: function(message, options) { 
    var dflts = { sticky:true, theme: 'error'};
    for (key in options) { dflts[key] = options[key]; };
    jQuery.jGrowl(message, dflts);
	},

	notice: function(message, options) {
    var dflts = { life: 10000, theme: 'success'};
    for (key in options) { dflts[key] = options[key]; };
    jQuery.jGrowl(message, dflts);
	},

	alert: function(message, options) {
    var dflts = { life: 10000, theme: 'error'};
    for (key in options) { dflts[key] = options[key]; };
    jQuery.jGrowl(message, dflts);
	}
};

/* ----------------------------------------------------------------------
 * Mapify.Address
 * ----------------------------------------------------------------------
 */
 
Mapify.Address = function(json) {
  this.lat = null;
  this.lng = null;
  if (json != null) {
    for (property in json) { this[property] = json[property]; }
  }  
  // extended attributes
  this.name           = this.first_name + " " + this.last_name;
  this.street_address = [this.address1, this.address2].join(", ").replace(/\,\s$/, "").replace(/\,\s\,\s/g, ""); // remove ending string and ,\s,\s
  
  this.pcode = (this.province_code) ? this.province_code : this.province;
  this.ccode = (this.country_code) ? this.country_code : this.country;
  this.full_address   = [this.address1, this.address2, this.city, this.pcode, this.ccode, this.zip].join(", ").replace(/\,\s$/, "").replace(/\,\s\,\s/g, ", ");
  this.full_address2  = [this.city, this.pcode, this.ccode, this.zip].join(", ").replace(/\,\s$/, "").replace(/\,\s\,\s/g, ", ");
};

Mapify.Address.prototype.is_geocoded = function() {
  return ((this.lng != null) && (this.lat != null));
};

Mapify.Address.prototype.updateLatLng = function(lat, lng) {
  this.lat = lat;
  this.lng = lng;
  this.save();  
};

// adds address to sidebar
Mapify.Address.prototype.addToList = function(domid) {
  var content = this._buildContent();
  $(domid).append(content);
};

Mapify.Address.prototype.showOrder = function() {
  var address = this;
  jQuery.facebox(function() {
    jQuery.get('/addresses/'+address.id+'/order', function(data) {
      jQuery.facebox(data);
    });
  });
};

Mapify.Address.prototype.save = function() {
  if (this.id == null) { return; }
  var save_path = "/addresses/" + this.id;
  var address = this;
  var obj = { authenticity_token: window._authenticity_token };
  if (address.lat != null) { obj["address[lat]"] = address.lat; }
  if (address.lng != null) { obj["address[lng]"] = address.lng; }
  
  jQuery.ajax( {
    type: "PUT",
    url: save_path,
    data: jQuery.param(obj),
    success: function(msg){},
    error: function(msg){ Messenger.alert("Unable to save geocoded coordinates for "+address.full_address); }
  });
};

Mapify.Address.prototype._buildContent = function() {
  var geocontent = '<li class="address" id="address-#{id}">\
    <p class="name">\
      <img src="/images/icons/silk/house.png" title="View on map">\
      <a href="#" onclick="map.showMarker(\'#{marker_id}\');"> #{name} (#{order_name})</a>\
    </p>\
    <p class="address">#{street_address}</p>\
    <p class="address">#{city}, #{province}</p>\
    <p class="address">#{country} #{zip}</p>\
  </li>';
  var nocontent = '<li class="address" id="address-#{id}">\
    <p class="name">\
      <img src="/images/icons/drf/stop.png" title="Unable to geocode address" width="16px">\
      #{name} (#{order_name})\
    </p>\
    <p class="address">#{street_address}</p>\
    <p class="address">#{city}, #{province}</p>\
    <p class="address">#{country} #{zip}</p>\
  </li>';
  var content = this.is_geocoded() ? geocontent : nocontent;
  for (key in this) {
    var pat = new RegExp("#{"+key+"}", 'g');
    content = content.replace(pat, this[key]);
  }
  return content;
};



/* ----------------------------------------------------------------------
 * Mapify.Map
 * ----------------------------------------------------------------------
 */
 
Mapify.Map = function(domid, listid, loadingid) {
  this.domid       = "#"+domid;
  this.listid      = "#"+listid;    // ul that holds listing
  this.loading     = "#"+loadingid; // loading div
  this.map         = null;          // gmap
  this.markersList = {};            // empty prototype hash
  this.startPt     = new GLatLng(45.435623,-75.684729); // Shopify HQ
  this.geocoder    = new GClientGeocoder();
  this.minZoom     = 3;
  this.maxZoom     = 16;
  this.dfltZoom    = 3;
  this.queue       = [];  // queue of to be geocoded addresses
  this.queueId     = 0;
  this.dflt_interval = 200; // delay per geocoding

  this._initMap();
};

Mapify.Map.prototype.addMarker = function(address) {
  address.is_geocoded() ? this._addMarker(address) : this._geocodeAddress(address);
};

Mapify.Map.prototype.addMarkers = function(addresses) {
  this.showLoading(); // show loading 
  
  if (addresses.length == 0) {
    $(this.loading).fade();
    return; 
  }
  
  // separate into geocoded and non-geocoded.
  var map = this;
  jQuery.each(addresses, function(){
    (this.is_geocoded() == true) ? map._addMarker(this) : map.queue.push(this);
  });
  this._processQueue();
};

Mapify.Map.prototype.removeMarker = function(id) {};
Mapify.Map.prototype.hideMarkers = function(ids) {};
Mapify.Map.prototype.showMarkers = function(ids) {};

Mapify.Map.prototype.showMarker = function(id) {
  this.zoomToMarker(id);
  var marker = this.markersList[id];
  GEvent.trigger(marker,'click');
};

Mapify.Map.prototype.clearMarkers = function() {
  this.map.clearOverlays();
  this.markerList = {};       // reset markers
  this.map.setCenter(this.startPt, this.dfltZoom);
};

Mapify.Map.prototype.zoomToMarker = function(id) {
  var marker = this.markersList[id];
  if (marker) { 
    this.map.panTo(marker.getLatLng());
    this.map.setCenter(marker.getLatLng());
    this.map.setZoom(this.maxZoom);
  };
};

Mapify.Map.prototype.zoomOut = function() { this.map.setZoom(this.dfltZoom); };

Mapify.Map.prototype.centerAndZoomToMarkers = function() {
  var markers = [];
  for (key in this.markersList) {
    markers.push( this.markersList[key] );
  };
  if (markers.length == 0) return;

  var bounds = new GLatLngBounds(markers[0].getPoint(),markers[0].getPoint());
  jQuery.each(markers, function(){ 
    bounds.extend(this.getPoint());
  });
  this.map.setCenter( bounds.getCenter(), this.map.getBoundsZoomLevel(bounds) );  
};

// calls showOrder method in address model
Mapify.Map.prototype.showOrder = function(id) {
  var marker = this.markersList[id];
  marker.address.showOrder();
};

Mapify.Map.prototype.showLoading = function() {
  var message = '\
    <img src="/images/spinner32.gif" class="icon"/>\
    <span style="font-size: 18px; font-weight: bold">Please wait...geocoding addresses. </span>';
  if (this.queue.length > 0) {
    message += '<span style="font-size:18px; font-weight: bold">' + this.queue.length.toString() + ' left to be geocoded.</span>';
  }
  $(this.loading).html(message).show();
};

//---------------------------------------------

Mapify.Map.prototype._processQueue = function() {
  this.showLoading();
  this.queueId = setInterval( "map._process()", this.dflt_interval);
};

// pull from to-be-geocded queue and process
Mapify.Map.prototype._process = function() {
  var address = this.queue.pop();
  var map = this;
  if (address) {
    this._geocodeAddress(address);
    this.showLoading();
  } else {
    // finish processing queue.
    clearInterval(this.queueId);
    $(this.loading).fade();       // hide loading tag
    var count = 0;
    for(var i in this.markersList) { count += 1; } // update display message
    $('#marker-count').html(count + " of ");
    this.centerAndZoomToMarkers();
  }
};

Mapify.Map.prototype._addMarker = function(address) {
  var new_icon = this._newIcon();
  var point    = new GLatLng(address.lat, address.lng);
  var marker   = new GMarker( point, {icon: new_icon} );
  // extend some attributes
  marker.id         = 'address_'+address.id;
  marker.address    = address;             // reference address to marker
  address.marker_id = marker.id;           // assign back to address obj
  var content       = this._buildContent(address);
  marker.bindInfoWindowHtml(content);      // bind window content to marker
  this._addTooltip(marker, address.name);  // add tooltip to marker
  this.map.addOverlay(marker);             // add to map
  this.markersList[marker.id] = marker;    // add to hash
  
  // add to sidebar list
  address.addToList(this.listid);
};

  // create and return new icon
Mapify.Map.prototype._newIcon = function() {
  var icon        = new GIcon();
  icon.image      = "/images/markers/address.png";
  icon.shadow     = "/images/markers/address_shadow.png";
  icon.iconSize   = new GSize(30.0, 30.0);
  icon.shadowSize = new GSize(46.0, 30.0);
  icon.iconAnchor = new GPoint(15.0, 15.0);
  icon.infoWindowAnchor = new GPoint(15.0, 15.0);
  return icon;
};
  
// build the HTML window for marker
Mapify.Map.prototype._buildContent = function(address) {
  var content = '<div class="bubble"> \
          <p class="name_line">#{name}</p> \
          <p class="address_line">#{street_address}</p> \
          <p class="city_line">#{city}, #{province}</p> \
          <p class="country_line">#{country} #{zip}</p> \
          <p class="order_date" style="text-align:right; font-size:10px; font-style:italic">Order #{order_name} #{order_date}</p>\
          <ul class="options-menu"> \
            <li class="first"><a href="#" onclick="map.zoomToMarker(\'#{marker_id}\');" class="link" style="background:url(/images/icons/silk/zoom_in.png) left center no-repeat;"/>Zoom In</a></li> \
            <li><a href="#" onclick="map.zoomOut();" class="link" style="background:url(/images/icons/silk/zoom_out.png) left center no-repeat;"/>Zoom Out</a></li> \
            <li><a href="#" onclick="map.showOrder(\'#{marker_id}\')">View Order</a></li> \
          </ul> \
        </div>';
  for (key in address) {
    var pat = new RegExp("#{"+key+"}", 'g');
    if (key == 'order_date') {
      try {
        address[key]= Date2.parse(address[key]).toDateString();
      } catch(e) {}
    }
    content = content.replace(pat, address[key]);
  }
  return content;
};

Mapify.Map.prototype._addTooltip = function(marker, tooltip) {
 marker.tooltip = "<div class='tooltip'><nobr>" + tooltip + "</nobr></div>";
 var thisMap = this;
 GEvent.addListener(marker, "mouseover", function(){ 
    // show tooltip function
    thisMap.tooltip.innerHTML = marker.tooltip;
    var point  = thisMap.map.getCurrentMapType().getProjection().fromLatLngToPixel(thisMap.map.fromDivPixelToLatLng(new GPoint(0,0),true), thisMap.map.getZoom());
    var offset = thisMap.map.getCurrentMapType().getProjection().fromLatLngToPixel(marker.getPoint(), thisMap.map.getZoom());
    var anchor = marker.getIcon().iconAnchor;
    var width  = marker.getIcon().iconSize.width;
    var height = thisMap.tooltip.clientHeight;
    var pos    = new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(offset.x - point.x - anchor.x + width, offset.y - point.y -anchor.y -height)); 
    pos.apply(thisMap.tooltip);
    thisMap.tooltip.style.visibility="visible";
  });
  GEvent.addListener(marker, "mouseout", function() { thisMap.tooltip.style.visibility = 'hidden'; } );
};

  
Mapify.Map.prototype._geocodeAddress = function(address) {
  var thisMap = this;
  if (address.attempts > 5) {
    return; // don't bother trying to geocode - didn't work the previous 5 times...
  }
  this.geocoder.getLatLng(address.full_address, function(point){
    if (!point) {
      //console.log("Could not map first address, dropping down "+address.full_address);
      // could not geocode this. Try again with address 2
      thisMap.geocoder.getLatLng(address.full_address2, function(point){
          if (!point) {
            Messenger.alert("Unable to geocode "+address.full_address);
            address.save(); // save anyways, increments attempts count
            // add addresses to sidebar list
            address.addToList(thisMap.listid);
          } else {
            address.updateLatLng(point.lat(), point.lng());
            thisMap._addMarker(address);
          }
        });
    } else {
      address.updateLatLng(point.lat(), point.lng());
      thisMap._addMarker(address);
    };
  });
};

// initialize Google maps
Mapify.Map.prototype._initMap = function() {
  if ($(this.domid) == null) {
    Messenger.error("Error: Unable to load Map");
    return;
  };
  // init tooltip
  this.tooltip = document.createElement("div");
  if (GBrowserIsCompatible()) {
    this.map = new GMap2( document.getElementById($(this.domid).attr('id')) );
    this.map.enableDoubleClickZoom();  // by default, allow dblclick to zoom in
    var mapControl = new GHierarchicalMapTypeControl(); 
    this.map.addMapType(G_PHYSICAL_MAP); 
    mapControl.clearRelationships();
    mapControl.addRelationship(G_SATELLITE_MAP, G_HYBRID_MAP, "Labels", false);
    this.map.addControl(mapControl);
    this.map.addControl(new GLargeMapControl());
    // add tooltip
    this.map.getPane(G_MAP_FLOAT_PANE).appendChild(this.tooltip);
    this.tooltip.style.visibility = "hidden";
    this.map.setCenter(this.startPt, this.dfltZoom, G_PHYSICAL_MAP);
  } else {
    Messenger.error("Error: Unable to initialize Google Maps");
  }  
};
//-----------------------------------------------------------------------------------------

/* parse ISO 8601 date */
var Date2 = {
  Months: [ 
    ["January","Jan"],
    ["Febuary","Feb"],
    ["March","Mar"],
    ["April","Apr"],
    ["May","May"],
    ["June","Jun"],
    ["July","Jul"],
    ["August","Aug"],
    ["September","Sep"],
    ["October","Oct"],
    ["November","Nov"],
    ["December","Dec"]
  ],

  parse: function(isoDate) {
    try {
      var match = /(\d{4})-(\d{2})-(\d{2})T(\d{2}:\d{2}:\d{2})([+|-]?\d{2}:\d{2}|Z)/.exec(isoDate);
    } catch(e) {
      var match = /(\d{4})-(\d{2})-(\d{2})T(\d{2}:\d{2}:\d{2})/.exec(isoDate);
    }
    var date = new Date(match[1], match[2]-1, match[3]);
    date = this.Months[date.getMonth()][1] + " " + date.getDate() 
      + " " + date.getFullYear() + " " + match[4] + " GMT ";
    if (match[5] != 'Z') {
      date += match[5].replace(/:/, "");
    }
    date = new Date(Date.parse(date));
    return date;
  },

  toString: function(date) {
    return this.Months[date.getMonth()][0] + " " + date.getDate() + ", " + date.getFullYear();
  }
};
