/** */

var debug_overlay;

function MapManager(mapobj, pagemanager, sesh, mapcontainer, api_version, other_options)
{

    other_options = other_options || {};
    var default_options = {
	'concept': null,
	'api_version': '2.95'
    };

    this.sesh = sesh;
    this.map = mapobj;
    this.mgr = pagemanager;
    this.map_container = mapcontainer;

//     this.options = {
// 	api_version: api_version || '2.95'
//     };
    this.options = $merge(default_options, other_options);

    this.templates = {}; // layerid => tile dir hash
    this.overlays = {}; // tile dir hash => gtilelayer obj

	this.layerid_to_overlay = {}; // arggghh ...

    this.active_overlays = []; // a stack

    this.objects = {}; // layerid => json representing style
    this.order = []; // list of layerids, in order -- first should
		     // appear on top of second & so forth
    this.zoom = 0;
    this.bb = null;

    this.deferred = {}; // layerid => f, fires when overlay has been
			// set for layerid

    this.scrim = getScrimInstance();

    this.loggerName = "mapmanager";
    Log.enable(this.loggerName);

    // fx for the progress bar
    var tx = 200;
    this.fx0 = new Fx.Styles($('_bar_completed'), {
	    duration: tx,
	    wait: false,
	    unit: '%'
	});

    //
    this.refreshBtn = new BlinkButton('_refresher', { basecls: 'rtp', steps: 5 });
    // this.refreshBtn.elt.addEvent('click', function() { bb.stop() });

    this.ajaxObjects = [];
    this.reverted = false;
    this.snapshot = null; /* the object that holds the previous layer state */

    // configure listeners for the map
    var addmoveendlsnr = (function() {
	// // // console.log('map was loaded -- added a moveend callback');
	GEvent.addListener(this.map, 'moveend', (function() {
	    var result = this.checkModels();
	    // // console.log('moveend:', result);
	    if (result) {
		$('_refresher').fireEvent('click');
	    }
	}).bind(this));
    }).bind(this);

    if (this.map.isLoaded()) {
	addmoveendlsnr();
    } else {
	GEvent.addListener(this.map, 'load', (function() {
	    addmoveendlsnr();
	}).bind(this));
    }

    var f = function(mapmgr) {
	var g = function(overlay, pt) {
	    // // console.log("args to click handler: overlay=" + String(overlay) + ", pt=" + String(pt));
	    latlng = [pt.lat(), pt.lng()];
	    mapmgr.atPoint(latlng);
	};
	return g;
    };
    // GEvent.addListener(this.map, 'click', f(this));
};

MapManager.prototype.atPoint = function(pt)
{
    var sesh = this.sesh;
    // // console.log("calling at point");
    var url = '/cbi/maptool/queryLocation';
    var ajax = new Ajax(url, {
	    method: 'post',
	    data: Json.toString([sesh, pt]),
	    onSuccess: function(txt, code) {
		// // console.log("recvd at point response: " + String(txt));
	    }
	});
    ajax.request();
};

MapManager.prototype.compareObjects = function() {
    // console.log('comparing objects');
    var newObjects = {};
    var oldObjects = {};
    var group, j, key, bChanged;
    /** compute new objects */
    this.mgr.layerGroups.each(function(group) {
	group.layers.each(function(layer) {
	    key = layer.id;
	    newObjects[key] = layer.foo();
	});
    });
    /** compute old objects */
    oldObjects = this.objects;
    var bChanged = !eq(newObjects, oldObjects);
    // console.log('bChanged:', bChanged);
    if (bChanged) {
	this.enableRefreshButton();
	this.refreshBtn.start();
    } else {
	this.disableRefreshButton();
	this.refreshBtn.stop();
    }
    return bChanged;
};
MapManager.prototype.compareZoom = function() {
    var newZoom, oldZoom, bChanged;
    /** compute new zoom */
    newZoom = this.map.getZoom();
    /** compute old zoom */
    oldZoom = this.zoom;
    if (typeof console != 'undefined') {
	// console.log('comparing zooms; new', newZoom, ', old', oldZoom);
    }
    var bChanged = !eq(newZoom, oldZoom);
    if (bChanged) {
	this.enableRefreshButton();
	this.refreshBtn.start();
    } else {
	this.disableRefreshButton();
	this.refreshBtn.stop();
    }
    return bChanged;
};

var callbank_number = 0;
/** */
MapManager.prototype._computeUpdate = function()
{
    var e, g, i, j;
    var k;
    var newObjects = {};
    var newOrder = [];
    var newZoom;
    var newBB;

    var mgr = this.mgr;
    for (i = 0; i < mgr.layerGroups.length; i++) {
	e = mgr.layerGroups[i];
	for (j = 0; j < e.layers.length; j++) {
	    k = e.layers[j].id;
	    newOrder.push(e.layers[j]);
	    newObjects[k] = e.layers[j].foo();
	}
    }
    newZoom = this.map.getZoom();

    var newBounds = gmap.getBounds();
    var newBB = [newBounds.getSouthWest().toUrlValue(),
		 newBounds.getNorthEast().toUrlValue()];
    return [newOrder, newObjects, newZoom, newBB];
};

/**
 * @returns list
 * (bool: did list change,
 *  num or undefined: new zoom level or undefined,
 *  bb or undefined: new bb or undefined,
 *  list or undefined: new order or undefined,
 *  list: added objects, removed objects, changed objects)
 */
MapManager.prototype._checkModel = function(update)
{
    var result = false;

    var order;
    var lst = map(function(obj) { return obj.id; }, update[0]);
    if (!eq(lst, this.order)) {
	// // console.log("order was different");

	order = lst;
	result = true;
    }

    var removed = [];
    var changed = [];
    var added = [];
    var objects_delta;
    if (!eq(update[1], this.objects)) {
	// // console.log("objects were different: update=" + Json.toString(update[1]) + ", old=" + Json.toString(this.objects));

	// compute additions
	for (k in update[1]) {
	    if (this.objects[k] === undefined) {
		if (typeof(k) == "string") {
		    k = parseInt(k);
		}
		added.push(k);
	    }
	}

	// compute removals
	for (k in this.objects) {
	    if (update[1] === undefined) {
		if (typeof(k) == "string") {
		    k = parseInt(k);
		}
		removed.push(k);
	    }
	}

	// compute changes
	var _k;
	for (_k in update[1]) {
	    if (_k in this.objects) {
		// // console.log("comparing: k=" + String(_k));
		// // console.log("o=" + Json.toString(update[1][_k]) + ", m=" + Json.toString(this.objects[_k]));
		var obj = this.objects[_k];
		if ("z-layer" in obj) {
		    delete obj["z-layer"];
		}
		if (typeof(_k) == "string") {
		    _k = parseInt(_k);
		}
		if (!eq(update[1][_k], obj)) { // this.objects[_k])) {
		    changed.push(_k);
		}
		// // console.log("changed: " + Json.toString(changed));

	    }
	}

	objects_delta = [ added, removed, changed ];
	result = true;
    }

    var zoom;
    if (!eq(update[2], this.zoom)) {
	// // console.log("zoom was different");

	zoom = update[2];
	result = true;
    }

    var bb;
    if (!eq(update[3], this.bb)) {
	// // console.log("bb was different");

	bb = update[3];
	result = true;
    }

    return [ result, zoom, bb, order, objects_delta ];
};

MapManager.prototype.getPane = function(version, overlay)
{
    // console.warn("you are no longer allowed to get pane");
    switch (version)
    {
	// case '2.193':
	// return overlay.
	case '2.193':
	  try {
	    return overlay.Ea.A;
	  } catch (e) {
	    debug_overlay = overlay;
	    alert('Detected inconsistency in Google Maps API version.  Please notify support@rhizalabs.com.');
	  }

	    break;
	case '2.164':
		return overlay.Za.k;
		break;
    case '2.95':
		return overlay.ee.Na[0].pane;
    case '2.111':
		try {
			return  overlay.qc.$a[0].pane;
		} catch (e) {
			// console.log('BUMMER!');
			// console.error(e);
		}
		return null;
    default:
		console.assert(false, 'this is not a supported version of the gmaps api');
		return null;
    };
	return null;
};

var MAX_NUMBER_LAYERS = 1000;
MapManager.prototype.getZPriority = function(layerId)
{
    var currentOrder = [];
    var i, j, k, e, n, hsh, overlay, m;
    var mgr = this.mgr;
    for (i = 0; i < mgr.layerGroups.length; i++) {
	e = mgr.layerGroups[i];
	for (j = 0; j < e.layers.length; j++) {
	    k = e.layers[j].id;
	    currentOrder.push(k);
	}
    }

    if (currentOrder.length > MAX_NUMBER_LAYERS) {
	alert("something bad happened with the number of layers");
    }

    //
    var idx = currentOrder.indexOf(layerId);
    if (idx === -1) {
	alert("something bad happened with the layer ids");
    } else {
	return MAX_NUMBER_LAYERS - idx;
    }
	return null;
};

/** make the map match the proper order in the stack */
MapManager.prototype.sortLayers = function()
{
    alert("this method is deprecated");
};

MapManager.prototype.cancelRefresh = function()
{
    var i = 0;
    // // console.log("canceling");
    for (var j = 0; j < this.ajaxObjects.length; j++) {
	if (this.ajaxObjects[j].running) {
	    i++;
	    this.ajaxObjects[j].cancel();
	}
    }
    this.hideProgressBar();
    // // console.log("canceled " + String(i) + " ajax requests");
    this.revertToSnapshot();
    // // console.log("reverted to snapshot");

    // stop all the updating layers
    var mgr = this.mgr;
    var e;
    for (i = 0; i < mgr.layerGroups.length; i++) {
	e = mgr.layerGroups[i];
	for (j = 0; j < e.layers.length; j++) {
	    e.layers[j].setRefreshing(false);
	    e.layers[j].renderHTML();
	}
    }

    this.checkModels();
};

/* take a check point of the current layer state */
MapManager.prototype.takeSnapshot = function()
{

    if (1 == 1) {
	return;
    }

    var currentTemplates = copyObj(this.templates);

    var currentOverlays = {};
    for (var j in this.overlays) {
	if ($type(this.overlays[j]) == 'object') {
	    currentOverlays[j] = this.overlays[j];
	}
    }

    // // console.log('copying self.objects');
    var a = copyObj(this.objects);
    var b = copyObj(this.order);
    var c = copyObj(this.zoom);
    var d = copyObj(this.bb);

    // // // console.log('copied objs: ' + Json.toString(a));

    // assign snapshot
    this.snapshot = {
	templates: currentTemplates,
	overlays: currentOverlays,
	objects: a,
	order: b,
	zoom: c,
	bb: d
    };

    var url = "/cbi/maptool/backup_session";
    var contents = [this.sesh];
    var ajax = new Ajax(url, {
	    method: 'post',
	    data: Json.toString(contents)
	});
    ajax.request();
};

/* revert to previous check point */
MapManager.prototype.revertToSnapshot = function()
{

    if (1 == 1) {
	return;
    }

    // console.log("reverting to snapshot");
    if (this.snapshot == null) {
	// // console.log('snapshot was null');
	return;
    }


    // // console.log("reverting to snapshot");

    // clear the map
    this.map.clearOverlays();

    var templates = this.snapshot.templates;
    var overlays = this.snapshot.overlays;

    var oldobjects = this.snapshot.objects;
    var oldorder = this.snapshot.order;
    var oldzoom = this.snapshot.zoom;
    var oldbb = this.snapshot.bb;

    // add the overlays
    for (var k in overlays) {
	// // console.log('overlay keys: ' + k + ", type=" + $type(k));
	// // console.log('value: ' + $type(overlays[k]));
    }

    // // console.log('hello');
    for (var k in overlays) {
	if ($type(overlays[k]) != 'object') {
	    continue;
	}
	this.addOverlay(overlays[k]);
    }

    // update the mgr fields
    this.templates = templates;
    this.overlays = overlays;

    // // console.log('cur objs: ' + Json.toString(this.objects));
    // // console.log('new objs: ' + Json.toString(oldobjects));

    this.objects = oldobjects;
    this.order = oldorder;
    this.zoom = oldzoom;
    this.bb = oldbb;

    // sort the layers
    this.sortLayers();
    this.snapshot = null;
    this.reverted = true;

    var url = "/cbi/maptool/restore_session";
    var contents = [this.sesh];
    var ajax = new Ajax(url, {
	    method: 'post',
	    data: Json.toString(contents)
	});
    ajax.request();
};

MapManager.prototype.checkModels = function()
{
//     if (this.compareObjects()) {
// 	return true;
//     }
//     if (this.compareZoom()) {
// 	return true;
//     }

    if (typeof console != 'undefined') {
	// console.log('checking models');
    }

    // // console.log('checking models');
    for (var j = 0; j < this.mgr.layerGroups.length; j++) {
	if (!this.mgr.layerGroups[j].isComplete()) {
	    this.disableRefreshButton();
	    this.refreshBtn.stop();
	    // console.log('found incomplete layer group', j)
	    return false;
	}
    }
    // // console.log('checked models; all were complete');

    var update = this._computeUpdate();
    var result = this._checkModel(update);

    // // console.log('result:', Json.toString(result));

    if (result[0]) {
	this.enableRefreshButton();
	this.refreshBtn.start();
    } else {
	this.disableRefreshButton();
	this.refreshBtn.stop();
    }
    return result[0];
};

MapManager.prototype.enableRefreshButton = function() {
    $('_refresher').setStyle('background-position', '');
    $('_refresher').removeEvents('click');
    $('_refresher').addEvents({
	    mousedown: function()
		{
		    this.setStyle('background-position', '0px -231px');
		},
	    mouseup: function()
		{
		    this.setStyle('background-position', '');
		},
	    click: function()
		{
		    mapManager.refresh('rw');
		}
	});

  if ($('refresh_scrim')) {
    $('refresh_scrim').setStyle('height',
				$('map_canvas').getSize()['size']['y']);
    $('refresh_scrim').setStyle('display', 'block');
    $('refresh_scrim').removeEvents('click');
    $('refresh_scrim').addEvents({'click': function () {
				    mapManager.refresh('rw');
				  }
				 });
  }
};

MapManager.prototype.disableRefreshButton = function() {
    $('_refresher').setStyle('background-position', '0px -264px');
    $('_refresher').removeEvents('click');

  if ($('refresh_scrim')) {
    $('refresh_scrim').setStyle('display', 'none');
  }
};


/** */
MapManager.prototype.refresh = function(/* str */ mode)
{
  /*
   * THIS CODE SHOULD BE REFACTORED TO SHARE CODE WITH
   * update_session_toggles() BELOW
   */

    if (typeof console != 'undefined') {
	// console.log('refreshing??')
    }

    mode = mode || 'rw';

    // // console.log("[MapManager] refreshing");
    var update = this._computeUpdate();
    var result = this._checkModel(update);
    if (!result[0]) {
	// // console.log("[MapManager] The model did not change");
	return;
    } else {
	// // console.log("computed differences: " + Json.toString(result));
    }

    // TODO: rewrite me using bind
    f = function(mgr, total, completed) {
		var g = function(layerobj, j) {
			var h = function(text, code) {
				if (typeof console !== 'undefined') {
					// console.log('text:', text);
				}
				completed += 1;

				mgr.updateProgressBar(100 * (completed / total));
				mgr.updateCompleted(completed, total);
				var o = Json.evaluate(text)[0][0];
				var overlay = mgr._getOverlay(layerobj);
				if (overlay) {
					if (mgr.containsOverlay(layerobj, overlay)) {
						mgr.removeOverlay(layerobj, overlay);
					} else {
						console.warn("the old overlay does not match up with the layer object");
					}
				}
				// add new overlay
				overlay = mgr._makeOverlay(o, mgr.getZPriority(layerobj.id));
				mgr.templates[layerobj.id] = o;
				mgr.overlays[o] = overlay;
				mgr.layerid_to_overlay[layerobj.id] = overlay;

				mgr.addOverlay(layerobj, overlay);
				if (!layerobj.getEnabledMask()) {
					mgr.hide(layerobj);
				} else {
					mgr.show(layerobj);
				}
				var e;
				// we're done -- turn off the spinner & turn on toggle
				layerobj.setRefreshing(false);
				if (layerobj.renderReady) {
					layerobj.renderHTML();
				}

				// are we done?
				if (completed == total) {
					mgr.hideProgressBar();
					mgr.snapshot = null;
					mgr.fireEvent('onRefresh');
				}
				mgr.checkModels();
			};
			return h;
		};
		return g;
    };

    this.takeSnapshot();

    this.objects = update[1];
    this.order = map(function(obj) { return obj.id; }, update[0]);
    this.zoom = update[2];
    this.bb = update[3];

    var d, e, j, m, o, k;
    var need_update = []; // ids that need to be updated
    if ((result[1] !== undefined) || (result[2] !== undefined) || (result[4] !== undefined) || (result[3] !== undefined)) {
	need_update.extend(this.order);
    }

    /* show the progress bar */
    this.resetCompleted(need_update.length);
    this.resetProgressBar();
    this.showProgressBar();

    var bundleId = new Date().getTime();
    var bounds = update[3];
    var zoom = update[2];

    var generator = f(this, need_update.length, 0.0);

    for (var j = update[0].length - 1; j >= 0; j--) {
	e = update[0][j];
	if (!need_update.contains(e.id)) {
	    // console.debug(String(e.id) + " did not need update");
	    continue;
	}
	m = update[1][e.id];
	m["z-layer"] = j;
	k = e.parent.u + "_" + String(j + 10);
	m["zoom_levels"] = get_zoom_levels_state_by_index_two(e.parent.u, e.id);
	// console.log('ZOOM LEVELS: ' + k + ' : ' + m['zoom_levels']);
	o = { };
	o[k] = m;
	e.setRenderedId(k);


	e.setRefreshing(true);
	if (e.renderReady) {
	    e.renderHTML();
	}

	var url = "/cbi/maptool/sesh2";
	var maph = this.map_container.getCoordinates().height;
	var mapCenter = [this.map.getCenter().lat(), this.map.getCenter().lng()];

	var ajax = new Ajax(url, {
	    method: 'post',
	    data: Json.toString([bundleId, this.sesh, zoom, bounds, mapCenter, maph, o, mode]),
	    onComplete: generator(e, j)
	});
	this.ajaxObjects.push(ajax);
	ajax.request();
	// // // console.log("sent req: " + String(j), this.loggerName);
    }
    return;
};

MapManager.prototype.update_session_toggles = function() {
  /*
   * THIS CODE SHOULD BE REFACTORED TO SHARE CODE WITH refresh() ABOVE
   *
   * The major differences between the two is that this one does not use
   * rw mode, so no tiles are built, and this does not show the progress
   * bar screen or force a redisplay of the map.
   */


  var mode = 'w'; // do not construct new tiles on server

  var update = this._computeUpdate();
  var result = this._checkModel(update);
  if (!result[0]) {
    return;
  }

  this.objects = update[1];
  this.order = map(function(obj) { return obj.id; }, update[0]);
  this.zoom = update[2];
  this.bb = update[3];

  var e, j, m, o, k;
  var need_update = []; // ids that need to be updated
  if ((result[1] !== undefined) || (result[2] !== undefined) || (result[4] !== undefined) || (result[3] !== undefined)) {
    need_update.extend(this.order);
  }

  var bundleId = new Date().getTime();
  var bounds = update[3];
  var zoom = update[2];

  function f() {
    // console.log('Set toggle on server!');
  }

  for (j = update[0].length - 1; j >= 0; j--) {
    e = update[0][j];
    if (!need_update.contains(e.id)) {
      continue;
    }
    m = update[1][e.id];
    m["z-layer"] = j;
    k = e.parent.u + "_" + String(j + 10);
    o = { };
    o[k] = m;
    e.setRenderedId(k);

    var url = "/cbi/maptool/sesh2";
    var maph = this.map_container.getCoordinates().height;
    var mapCenter = [this.map.getCenter().lat(), this.map.getCenter().lng()];

    var data = Json.toString([bundleId, this.sesh, zoom, bounds, mapCenter,
			      maph, o, mode]);
    var ajax = new Ajax(url, {
      method: 'post',
      data: data,
      onComplete: f
    });
    ajax.request();
  }

  return;
};

/** remove the layerobj from the mapmanager */
MapManager.prototype.remove = function(layerobj)
{
    var hsh = null;
    // remove from templates
    if (layerobj.id in this.templates) {
	hsh = this.templates[layerobj.id];
	delete this.templates[layerobj.id];
    }

    // remove from order
    $A(this.order).remove(layerobj.id);

    // remove from objects
    if (layerobj.id in this.objects) {
	delete this.objects[layerobj.id];
    }

    // remove from overlays
    var overlay = null;
    if (hsh && (hsh in this.overlays)) {
	overlay = this.overlays[hsh];
	delete this.overlays[hsh];
    }

    // remove from map
    // console.log('removing overlay');
    if (overlay) {
	// this.map.removeOverlay(overlay);
	this.removeOverlay(layerobj, overlay);
    }
};

var __layer_objects = [];
var __layer_overlays = [];

/** if the layerobj is in the layer_state make sure it has this overlay, else throw an exception */

MapManager.prototype.overlayIndexOf = function(layerObj) {
    var index = __layer_objects.map(function(item) {
	return item.id;
    }).indexOf(layerObj.id);
    return index;
};

MapManager.prototype.checkOverlayAssociation = function(layerObj, overlay) {
    var index = this.overlayIndexOf(layerObj);
    if (index !== -1) {
		console.assert(__layer_overlays[index] === overlay, 'the overlays do not match');
    }
};

MapManager.prototype.containsOverlay = function(layerObj, overlay) {
    this.checkOverlayAssociation(layerObj, overlay);

    var index = this.overlayIndexOf(layerObj);
    return (index !== -1);
};

MapManager.prototype.removeOverlayForLayer = function(layerObj) {
    var index = this.overlayIndexOf(layerObj);
    if (index !== -1) {
	this.removeOverlay(layerObj, __layer_overlays[index]);
    }
};

MapManager.prototype.removeOverlay = function(layerObj, overlay) {
    this.checkOverlayAssociation(layerObj, overlay);

    var index = this.overlayIndexOf(layerObj);
    // console.log('index:', index);
    if (index !== -1) {
	// console.log('[rem] layerobj is in layer state');
	this.map.removeOverlay(overlay);

	__layer_objects.splice(index, 1);
	__layer_overlays.splice(index, 1);
    } else {
	// console.log('[rem] layerobj is not in layer state.');
	console.assert(false);
    }
};

MapManager.prototype.addOverlay = function(layerObj, overlay) {
    this.checkOverlayAssociation(layerObj, overlay);

    var index = this.overlayIndexOf(layerObj);
    if (index !== -1) {
	// console.log('[add] layerobj in layer state');
	console.assert(false);
    } else {
	// console.log('[add] layerobj not in layer state');

	__layer_objects.push(layerObj);
	__layer_overlays.push(overlay);
	this.map.addOverlay(overlay);
    }
};

MapManager.prototype.setBG = function(mt)
{
    if (mt == this.map.getCurrentMapType()) {
	return;
    }

    var types = this.map.getMapTypes();
    var i;
    for (i = 0; i < types.length; i++) {
	if (types[i] == mt) {
	    this.map.setMapType(mt);
	    return;
	}
    }
    Log.logError("Tried to set invalid map type");
};

/** */
MapManager.prototype._getOverlay = function(obj)
{
    assert(this._isLayer(obj), "getoverlay takes a layer object");
    if (!(obj.id in this.templates)) {
	return null;
    }
    var hsh = this.templates[obj.id];
    if (!(hsh in this.overlays)) {
	return null;
    }
    var overlay = this.overlays[hsh];
    return overlay;
};

/** */
MapManager.prototype.hide = function(obj)
{
    // console.log('calling hide on mapmanager:', obj.id);
    var overlay = this._getOverlay(obj);
    this.checkOverlayAssociation(obj, overlay);

  /* We used to do this because we were having trouble showing and hiding overlays immediately after certain map operations were performed.  It no longer seems necessary.  -higgins 2011-03-11
    var pane = this.getPane(this.options.api_version, overlay);
    if (pane) {
      // seems to hide all overlays as a side-effect!  why are we doing this at all
      $(pane).setStyle('display', 'none');
    }
   */

    if (overlay) {
	// console.log("overlay was not null");
     // console.log('CALLING OVERLAY HIDE ' + overlay, overlay);
	overlay.hide();
	this.update_session_toggles();
    } else {
	console.warn("overlay was null");
    }
};

/** */
MapManager.prototype.show = function(obj)
{
  // console.log('SHOW');
    var overlay = this._getOverlay(obj);
    this.checkOverlayAssociation(obj, overlay);

  /* We used to do this because we were having trouble showing and hiding overlays immediately after certain map operations were performed.  It no longer seems necessary.  -higgins 2011-03-11
    var pane = this.getPane(this.options.api_version, overlay);
    if (pane) {
      // console.log('SETTING PANE DISPLAY');
	$(pane).setStyle('display', 'block');
    }
*/

    if (overlay) {
     // console.log('CALLING OVERLAY SHOW ' + overlay, overlay);
      overlay.show();
      this.update_session_toggles();
    }
  // console.log('DONE SHOW');
};


MapManager.prototype._isLayer = function(obj)
{
    return (obj instanceof Layer);
};

MapManager.prototype._isLayerGroup = function(obj)
{
    return (obj instanceof LayerGroup);
};

/**   */
MapManager.prototype._isSupportedStyle = function(style)
{
};

/** */
MapManager.prototype._hasNecessaryOptions = function(style, options)
{
};

/** */
var MAX_RESOLUTION = MAX_RESOLUTION || 20;
MapManager.prototype._makeOverlay = function(hsh, zPriority, bounds) {
	bounds = bounds || [0, MAX_RESOLUTION];
    var t = (new Date()).getTime();
    var tilelayer = new GTileLayer(null, bounds[0], bounds[1], {
		isPng: true,
		opacity: 1.0
    });
	tilelayer.getTileUrl = function(p, z) {
		if ((z >= this.minResolution()) && (z <= this.maxResolution())) {
			var t = (new Date()).getTime();
			return "/tiles_data/"+hsh+"/z"+z+"x"+p.x+"y"+p.y+".png?t="+t;
		} else {
			return "";
		}
	};

    if ($defined(zPriority)) {
		return new GTileLayerOverlay(tilelayer, {"zPriority": zPriority});
    } else {
		return new GTileLayerOverlay(tilelayer);
    }
};

MapManager.prototype.redraw = function()
{
    // do nothing
}

MapManager.prototype.computeAllPenalties = function()
{
    var mgr = this.mgr;
    for (i = 0; i < mgr.layerGroups.length; i++) {
	this.computePenalty(mgr.layerGroups[i]);
    }
};

MapManager.prototype.computePenalty = function(layergroupobj)
{
    // do nothing
    var url = "/cbi/penaltybox/compute";
    var bounds = gmap.getBounds();
    bounds = [bounds.getSouthWest().toUrlValue(),
	      bounds.getNorthEast().toUrlValue()];
    var f = function(obj)
    {
	var g = function(text)
	{
	    var numOmitted = Json.evaluate(text)[0];
	    obj.penalty = numOmitted;
	    obj.viewDirty = true;
	    obj.renderHTML();
	};
	return g;
    };
    var ajax = new Ajax(url, {
	    data: Object.toQueryString({ uuid: layergroupobj.u, bb: bounds }),
	    method: 'get',
	    onComplete: f(layergroupobj)
	}); // .request();
    ajax.request();
}

/** progress bar stuff */
MapManager.prototype.showProgressBar = function()
{
    var coords = $('contentFrameinner').getCoordinates();
    // // // console.log("content frame dimensions: " + Json.toString($('contentFrameinner').getCoordinates()));

    this.scrim.showUnder('_progress_bar');

    $("_progress_bar").setStyles(coords);


    // // // console.log("fg: " + Json.toString($('_progress_bar_fg').getCoordinates()));
    var styles = {
	top: ((coords.height - 200) / 2),
	left: ((coords.width - 260) / 2)
    };
    var element = $('_progress_bar_fg');
    element.setStyles(styles);
    $('_progress_bar').setStyle("display", "block");
    // // // console.log("fg: " + Json.toString($('_progress_bar_fg').getCoordinates()));

    // $('_progress_bar_bg').setOpacity(0);
//     var fx = new Fx.Styles($('_progress_bar_bg'), {
// 	    duration: 200,
// 	    wait: false,
// 	    transition: Fx.Transitions.Sine.easeInOut
// 	});
//     fx.start({ 'opacity' : 0.5 });

    // // // console.log("covered with scrim");
}

MapManager.prototype.hideProgressBar = function()
{
//     var fx = new Fx.Styles($('_progress_bar_bg'), {
// 	    duration: 1000,
// 	    wait: false,
// 	    transition: Fx.Transitions.Sine.easeInOut,
// 	    onComplete: function() {
// 		$("_progress_bar").setStyle('display', 'none');
// 	    }
// 	});
//     fx.start({ 'opacity': 0 });
    $("_progress_bar").setStyle('display', 'none');
    this.scrim.hide();
}

MapManager.prototype.resetProgressBar = function()
{
    $('_bar_completed').setStyle('width',   '0%' );
}



MapManager.prototype.resetCompleted = function(tot)
{
    $('_progress_txt').setHTML('0 of ' + tot + ' rules.');

};
MapManager.prototype.updateCompleted = function(completed, tot)
{
    $('_progress_txt').setHTML(completed + ' of ' + tot + ' rules.');
};
/* pct must be a number between 0 & 100 */
MapManager.prototype.updateProgressBar = function(pct)
{
    assert ((pct >= 0) && (pct <= 100), "pct must be between 0 & 100");
    // // // console.log("updating to: " + pct);
    this.fx0.start({ 'width':       pct });
};

MapManager.prototype.test_progress = function() {
  debugger;
    var self = this;

    self.resetProgressBar();
    self.showProgressBar();

    var tx = 2000;
    var fx0 = new Fx.Styles($('_bar_completed'), {
	    duration: tx,
	    wait: false,
	    unit: '%',
	    onComplete: self.hideProgressBar
	});
   fx0.start({ 'width': 100 });
};

MapManager.prototype.addEvent = function(eventName, func) {
    if (typeof(this.registered_events) == 'undefined') {
	this.registered_events = { };
    }
    if (!(eventName in this.registered_events)) {
	this.registered_events[eventName] = [];
    }
    this.registered_events[eventName].push(func);
};

MapManager.prototype.fireEvent = function(eventName) {
    if (typeof(this.registered_events) == 'undefined') {
	return;
    }
    if (!(eventName in this.registered_events)) {
	return;
    }
    var evts = this.registered_events[eventName];
    evts.each(function(func) {
	func();
    });
};

MapManager.prototype.clearEvents = function(eventName) {
    if (typeof(this.registered_events) == 'undefined') {
	return;
    }
    this.registered_events[eventName] = [];
};

MapManager.prototype.setMapType = function(map_type_name) {
    // valid names: physical, normal, satellite
    var table = {
	'satellite': G_SATELLITE_MAP,
	'physical': G_PHYSICAL_MAP,
	'normal': G_NORMAL_MAP
    };
    this.setBG(table[map_type_name]);
    update_map_bounds_with_current(this.options.concept);
};

/*
function send_reference_query()
{
    var bounds = gmap.getBounds();
    var bounds = [bounds.getSouthWest().toUrlValue(),
		  bounds.getNorthEast().toUrlValue()];

    var bundle_id = new Date().getTime();
    var obj = {
	"~01b4e47406ac1b11dcbc5c061475115ff1_10" : {
	    "attribute" : "name",
	    "style" : "random",
	    "options" : {},
	    "z-layer" : 0
	},
    };

    var payload = [bundle_id, "$sesh_blueprint", gmap.getZoom(), bounds, obj];
    var f = function(text, code)
    {
	// // // console.log("response: " + text);

	var layer = eval(text)[0][0];
	var tilelayer = new GTileLayer(null, null, null, {
		tileUrlTemplate: "/tiles_data/" + layer + "/z{Z}x{X}y{Y}.png",
		isPng: true,
		opacity: 1.0
	    });
	var overlay = new GTileLayerOverlay(tilelayer);
	gmap.addOverlay(overlay);
	if (oldoverlay) {
	    gmap.removeOverlay(oldoverlay);
	    oldoverlay = overlay;
	}
    };
    var jsonified = Json.toString(payload);
    GDownloadUrl("/cbi/maptool/sesh2", f, jsonified, "text/json");
}
*/



var MAX_RESOLUTION = 20; // the largest possible resolution supported by google maps
var MapTiles = new Class({
    initialize: function(opts) {
    },
    makeoverlay: function(hsh) {
		return this.make_overlay_with_bounds(hsh);
    },
	make_overlay: function(hsh) {
		return this.make_overlay_with_bounds(hsh);
	},
	make_overlay_with_bounds: function(hsh, bounds) {
		bounds = bounds || [0, 20];
		var tl = new GTileLayer(null, bounds[0], bounds[1], {
			isPng:true,
			opacity:1
		});
		/* this should have just worked */
		tl.getTileUrl = function(p, z) {
			if ((z >= this.minResolution()) && (z <= this.maxResolution())) {
				var t = (new Date()).getTime();
				return "/tiles_data/"+hsh+"/z"+z+"x"+p.x+"y"+p.y+".png?t="+t;
			} else {
				return "";
			}
		};
		return new GTileLayerOverlay(tl);
	}
});
MapTiles.implement(new Options);


/**
 * Function: update_map_bounds
 *
 * Arguments:
 *    concept -
 *    center -
 *    bounds -
 *    zoom -
 *
 */

var update_map_bounds = function(concept, center, bounds, zoom, height, maptype) {
    // console.log('updating map bounds');
    var update_bounds_proxy = new Ajax("/cbi/maptool/update_map_bounds", {
	async: false,
	method: "post"
    });
    var payload = {
      "concept": concept,
      "center": center,
      "bounds": bounds,
      "zoom": zoom,
      'map_type': maptype,
      "map_height": height
    };
    // console.log('send request to proxy:', Json.toString(payload));
    update_bounds_proxy.request(Json.toString(payload));

};

function get_map_type_name(gmap_type) {
    var result;
    if (gmap_type == G_SATELLITE_MAP) {
	result = 'satellite';
    } else if (gmap_type == G_NORMAL_MAP) {
	result = 'normal';
    } else if (gmap_type == G_PHYSICAL_MAP) {
	result = 'physical';
    }
    // console.log('result:', result);
    return result;
}

function update_map_bounds_with_current(concept) {
  var center = [ mapManager.map.getCenter().lat(),
		 mapManager.map.getCenter().lng() ];
  var bounds = [[mapManager.map.getBounds().getSouthWest().lat(),
		 mapManager.map.getBounds().getSouthWest().lng()],
		[mapManager.map.getBounds().getNorthEast().lat(),
		 mapManager.map.getBounds().getNorthEast().lng()]];
  var zoom = mapManager.zoom;
  var height = gmap.getSize().height + 40;
  var maptype = get_map_type_name(gmap.getCurrentMapType());
  update_map_bounds(concept, center, bounds, zoom, height, maptype);
}


