
var _latitude = 40;
var _longitude = -75;
var _zoom = 9;

var cont;
var quikapp;

function newMap() {

	var map = newCreate();
	var myAjax = new Ajax.Request(
				"/geocoder/userloc.php", 
				{
					method: 'get', 
					parameters: '',
					onComplete: function(response) { eval(response.responseText); }
				});					
}

function newCreate() {
	quikapp = new QuikmapsApp();
	quikapp.initCreate();
	return quikapp.cont.model.map;
}

function newEdit() {
	quikapp = new QuikmapsApp();
	quikapp.initEdit();
	return quikapp.cont.model.map;
}


var QuikmapsApp = Class.extend({}, {

	initialize: function() {
		this.cont = null;
	},
	
	initCreate: function() {
		var map = new GMap2($("map"));
		map.setCenter(new GLatLng(_latitude, _longitude), _zoom, G_NORMAL_MAP);
		map.addControl(new GLargeMapControl());
		map.addControl(new GMapTypeControl());	

		this.initEdit(map);
	},

	initEdit: function(map) {
//		if (GBrowserIsCompatible()) {
			if (isIE) correctPNG();

			var oldcontext = document.oncontextmenu;
			$('map').onmouseover = function() {
				document.oncontextmenu = new Function("return false");
			}
			
			$('map').onmouseout = function() {
				document.oncontextmenu = oldcontext;				
			}

			this.cont = new DnDMapController(map);		
			cont = this.cont;

			this.initGeocoder(this.cont.model.map,$('loading'));

			var app = this;
			$('save').onclick = function() { document.forms[0]['gencode'].value = "n"; app.onSave(); }
			$('savecode').onclick = function() { document.forms[0]['gencode'].value = "y"; app.onSave(); }
//		}	
		
	},

	initGeocoder: function(map,loadingElement) {
		thegeocoder = new Geocoder(map,loadingElement);

		$('geocodertext').select();
		$('geocodertext').onkeydown = function(event) {
			if (DnD.keycode(event) == Event.KEY_RETURN) thegeocoder.goGeocoder($('geocodertext').value);
		}
		$('geocoderok').onclick = function() { thegeocoder.goGeocoder($('geocodertext').value); };
	},
	
	onSave: function() {
		var xmlText = quikapp.dataObject2Xml(quikapp.buildDataObject());

		if (document.forms[0].name.match(/create/)) {
			document.forms[0]["qmap[created_at]"].value = DnD.jsdate2mysql(new Date());
			document.forms[0]["qmap[modified_at]"].value = DnD.jsdate2mysql(new Date());		
		} else if (document.forms[0].name.match(/update/)){
			document.forms[0]["qmap[modified_at]"].value = DnD.jsdate2mysql(new Date());			
		}

		document.forms[0]["qmap[data]"].value = xmlText;

		if ( !( xmlText.match(/<script/) || xmlText.match(/\/script>/) ) )
			document.forms[0].submit();
	},

	buildDataObject: function() {

		var obj = {
			overlays:[], 
			center:cont.model.map.getCenter(), 
			zoom:cont.model.map.getZoom(),
			maptype: DnD.maptype2num(cont.model.map.getCurrentMapType())
		};

		this.cont.model.map.overlays().each(function(node) {
			if (node.type == 'text') {
				obj.overlays.push({type:"textmarker",point:node.getPoint(),data:node.tooltipRaw});
			} else if (node.type == 'default') {
				obj.overlays.push({type:"marker",point:node.getPoint(),icon:DnD.iconImage(node).src,
						   width:DnD.iconImage(node).clientWidth, height:DnD.iconImage(node).clientHeight, data:node.info});
			}	
		});

		this.cont.model.data.paths.each(function(path) {
			obj.overlays.push({type:path.type, points:path.points});
		});

		return obj;
	},

	dataObject2Xml: function(obj) {
		var data;
		var text = '<data>';
		
		text += '<meta x="'+obj.center.x+'" y="'+obj.center.y+'" zoom="'+obj.zoom+'" maptype="'+obj.maptype+'" />';
		text += '<overlays>';
		obj.overlays.each(function(o) {
			text += '<o ';
			data = null;
			for (var i in o) {
				if (i == 'data')
					data = o[i];
				else 
					text += i + '="' + o[i] + '" ';
			}
			if (data != null) {
				text += '><cont><![CDATA[' + data + ']]></cont></o>';
			}
			else text += '/>';
		});
		text += '</overlays>';
		text += '</data>';

		return text;
	}
});

/*

DnDMapModel (really functions as both model and view for map)

*/
function DnDMapModel(controller, themap) {

	this.map = themap;
	this.map.controller = controller;
	this.controller = controller;
	this.mapElement = this.map.getContainer();

	this.editMode = DnDMapModel.MARKERS;
	this.scribbling = false;

	this.last_polyline = null;
	this.polyline_counter = 0;

	this.downmarker = null;
	this.dragged = 0;

	this.data = new DnDMapData();
	
	this.showLocation();
}


DnDMapModel.SCRIBBLE = 'scribble';
DnDMapModel.DRAW = 'draw';
DnDMapModel.MARKERS = 'markers';

DnDMapModel.prototype.showLocation = function() {
	var loc = this.map.getCenter();
	
	/* should send update to a view */
	$('area').innerHTML = (Math.floor(loc.y * 10000) / 10000) + ',' + (Math.floor(loc.x * 10000) / 10000);		
}

DnDMapModel.prototype.load = function(route) {}
DnDMapModel.prototype.undo = function() {}
DnDMapModel.prototype.redo = function() {}

DnDMapModel.prototype.drop = function(element) {
	if (element.className == 'maptext') { this.dropText(element); }
	else if (element.className == 'mapicon') { this.dropImage(element); }
	else if (element.className == 'draggablemarker') { this.dropImage(element); }
	else if (element.className == 'draggabletextmarker') { this.dropTextMarker(element); }
}


DnDMapModel.prototype.createTextMarker = function(point,value,info) {
	var marker = new DnDTextMarker(point);

	marker.info = info;
	this.map.addOverlay(marker);

	marker.bind(this.controller, value);
	
	this.downmarker = marker;
}


DnDMapModel.prototype.renderText = function(element,value) {
	var point = DnD.topLeftPixelToLatLng(element,this.mapElement,this.map);

	var info = element.info ? element.info : '';
	this.createTextMarker(point,value, info);

	document.body.removeChild(element);
}

DnDMapModel.prototype.dropTextMarker = function(element) {
	var point = DnD.leftPixelToLatLng(element,this.mapElement,this.map);

	var info = element.info ? element.info : '';
	this.createTextMarker(point,element.firstChild.innerHTML, info);

	document.body.removeChild(element);
}

DnDMapModel.prototype.dropText = function(imageElement) {
	var pos = Position.cumulativeOffset(imageElement);
				    
	var divelem = document.createElement('div');
	divelem.style.left = pos[0] + 'px';
	divelem.style.top = pos[1] + 'px';
	divelem.style.backgroundColor = '#ffffff';
	divelem.style.padding = '1px';
	divelem.style.position = 'absolute';

	var textelem = document.createElement('input');
	textelem.type = 'text';
	textelem.size = '10';
	textelem.controller = this;
	
	/* this should be set in the controller */
	textelem.onkeydown = function(event) {
		if (DnD.keycode(event) == Event.KEY_RETURN) { this.controller.renderText(divelem,this.value); }
	}

	var linkelem = document.createElement('a');
	linkelem.href = '#';
	linkelem.innerHTML = 'OK';
	linkelem.controller = this;

	/* this should be set in the controller */
	linkelem.onclick = function() { this.controller.renderText(divelem,textelem.value); }

	divelem.appendChild(textelem);
	divelem.appendChild(linkelem);
	document.body.appendChild(divelem);
	textelem.focus();
}


DnDMapModel.prototype.dropImage = function(imageElement) {


	var point = DnD.centerPixelToLatLng(imageElement,this.mapElement,this.map);
	
	var myicon = new DnDIcon(imageElement);
	
	var marker = (imageElement.line) ? new DnDPolyLineMarker(point,true) : new DnDMarker(point,myicon);
	
	if (imageElement.info) marker.info = imageElement.info;
	this.map.addOverlay(marker);

	marker.bind(this.controller);

	if (imageElement.line) {
		marker.prevmarker = imageElement.prevmarker;
		marker.nextmarker = imageElement.nextmarker;

		if (marker.prevmarker) {
			marker.prevline = new GPolyline([marker.prevmarker.getPoint(), point]);
			marker.prevmarker.nextmarker = marker;
			marker.prevmarker.nextline = marker.prevline;
			this.map.addOverlay(marker.prevline);
			this.data.addBefore(point,marker.prevmarker.getPoint());
		}
		if (marker.nextmarker) {
			marker.nextline = new GPolyline([point, marker.nextmarker.getPoint()]);
			marker.nextmarker.prevmarker = marker;
			marker.nextmarker.prevline = marker.nextline;
			this.map.addOverlay(marker.nextline);
			if (!marker.prevmarker) this.data.addAfter(point,marker.nextmarker.getPoint());
		}
	}

	if (imageElement.className == 'draggablemarker') {
		document.body.removeChild(imageElement);
	}
	this.downmarker = marker;

	
}

/*

NOT CURRENTLY USED -- FOR TRASHING A MARKER DURING DRAG ACTION

DnDMapModel.prototype.trashImage = function(imageElement) {
	if (   (imageElement.className == 'draggablemarker')
		|| (imageElement.className == 'draggabletextmarker') ) {
		document.body.removeChild(imageElement);
	} else {
		// do nothing!!!
	}
}
*/

DnDMapModel.prototype.dragDeleteMarker = function(event) {
	var marker = Event.element(event).marker || Event.element(event).parentNode.marker;
	
	this.deletePolylines(marker);
	this.doDeleteMarker(marker);
}	

DnDMapModel.prototype.clickDeleteMarker = function(event) {

	var marker = Event.element(event).marker || Event.element(event).parentNode.marker;
	
	if (marker.type == 'poly') {
		this.deletePolylines(marker);
		
		if ((marker.prevmarker) && (marker.nextmarker) && (marker.draggable)) {

			var newline = new GPolyline([marker.prevmarker.getPoint(), marker.nextmarker.getPoint()])
			this.map.addOverlay(newline);
		
			marker.nextmarker.prevline = newline;
			marker.nextmarker.prevmarker = marker.prevmarker;

			marker.prevmarker.nextline = newline;		
			marker.prevmarker.nextmarker = marker.nextmarker;

		} else if ((marker.prevmarker) && (!marker.prevmarker.prevline)) {
			this.doDeleteMarker(marker.prevmarker);
		} else if ((marker.nextmarker) && (!marker.nextmarker.nextline)) {
			this.doDeleteMarker(marker.nextmarker);
		}
	} 
	

	this.doDeleteMarker(marker);
}


DnDMapModel.prototype.deletePolylines = function(marker) {
	if (marker.prevline) {
		this.map.removeOverlay(marker.prevline);
		marker.prevmarker.nextmarker = null;
		marker.prevmarker.nextline = null;
	}
	if (marker.nextline) {
		this.map.removeOverlay(marker.nextline);
		marker.nextmarker.prevmarker = null;
		marker.nextmarker.prevline = null;
	}
	if (marker.nextline || marker.prevline) {
		this.data.removePoint(marker.getPoint());
	}
}


DnDMapModel.prototype.doDeleteMarker = function(marker) {
	this.map.removeOverlay(marker);
}


DnDMapModel.prototype.startDragTextMarker = function(event) {
	var marker = Event.element(event).parentNode.marker;

	var pos = Position.cumulativeOffset(marker.tooltipObject);

	var divElement = marker.tooltipObject.cloneNode(true);
	divElement.style.left = pos[0] + 'px';
	divElement.style.top = pos[1] + 'px';
	divElement.id = new Date().getTime();
	divElement.className =	'draggabletextmarker';

	divElement.info = marker.info;	

	document.body.appendChild(divElement);

	this.controller.invokeDrag(event,divElement);
}


DnDMapModel.prototype.startDragMarker = function(event) {

	var marker = Event.element(event).marker;

	var pos = Position.cumulativeOffset(DnD.iconImage(marker));

	if ((pos[0] == 0) && (pos[1] == 0)) return;
	
	var imgelem = Builder.node('img', {id: new Date().getTime(),
					   className: 'draggablemarker',
					   style: 'position: absolute; left: ' + pos[0] + 'px; top: ' + pos[1] + 'px;', 
					   src: marker.getIcon().image } );
	imgelem.info = marker.info;	
	if (marker.type == 'poly') {
		imgelem.line = true;
		imgelem.prevline = marker.prevline;
		imgelem.prevmarker = marker.prevmarker;
		imgelem.nextline = marker.nextline;
		imgelem.nextmarker = marker.nextmarker;
	}
	document.body.appendChild(imgelem);

	this.controller.invokeDrag(event,imgelem);
}


DnDMapModel.SCRIBBLE_TIMER = 4;
DnDMapModel.DRAW_TIMER = 4;

DnDMapModel.lastmarker = null;

DnDMapModel.prototype.extendPolyLine = function(point,draggable) {
	this.killTempPolyline();
	this.polyline_counter = 0;

	var path = this.data.currentPath();
	
	var marker = new DnDPolyLineMarker(point,draggable);
	this.map.addOverlay(marker);
	marker.bind(this.controller);

	if (path.length > 0) {
		var newline = new GPolyline([path[path.length-1], point])
		this.map.addOverlay(newline);
		
		marker.prevline = newline;
		marker.prevmarker = DnDMapModel.lastmarker;
		
		DnDMapModel.lastmarker.nextmarker = marker;
		DnDMapModel.lastmarker.nextline = newline;
	} 
	
	DnDMapModel.lastmarker = marker;	
	this.data.extendPath(point);	
}

DnDMapModel.prototype.drawPolyline = function(event) {
	var res = this.renderPolyline(event,DnDMapModel.DRAW_TIMER,true);
}


DnDMapModel.prototype.scribblePolyline = function(event) {
	if (this.renderPolyline(event,DnDMapModel.SCRIBBLE_TIMER,false)) {
		var pointer = [Event.pointerX(event), Event.pointerY(event)];
		var point = DnD.pixelToLatLng(pointer,this.mapElement,this.map);
		this.extendPolyLine(point,false);
	}
}

DnDMapModel.prototype.renderPolyline = function(event,toffset,temp) {
	var pointer = [Event.pointerX(event), Event.pointerY(event)];
	if (DnD.insideElement(pointer,Position.cumulativeOffset(this.mapElement),this.mapElement)) {
//		if (this.polyline_counter == toffset) {
			this.polyline_counter = 0;
			var path = this.data.currentPath();

			if (path.length > 0) {
				var point = DnD.pixelToLatLng(pointer,this.mapElement,this.map);
				
				if (temp) {
					this.killTempPolyline();
					this.last_polyline = new GPolyline([path[path.length-1], point]);
					this.map.addOverlay(this.last_polyline);
				}
			}
			
			return true;
//		} else {
//			this.polyline_counter++;
//		}
	} else {
		if (temp) this.killTempPolyline();
	}
	return false;
}


DnDMapModel.prototype.killTempPolyline = function() {
	if (this.last_polyline) {
		this.map.removeOverlay(this.last_polyline);
		this.last_polyline = null;
	}
}

DnDMapModel.prototype.markerOpenInfoWindow = function(marker) {
	var area = Builder.node('div', {}, ['Marker info', 
					   Builder.node('br',{}),
					   Builder.node('textarea', {id:'ta'+marker.infoid,cols:'20',rows:'5'})]);
	marker.openInfoWindow(area);
	$('ta'+marker.infoid).value = marker.info;

	$('ta'+marker.infoid).onchange = function() {
		marker.info = this.value;	
	}

	window.setTimeout("$('ta"+marker.infoid+"').focus()", 50);
}

DnDMapModel.prototype.startDraw = function(mode) {
	this.polyline_counter = 0;
	this.data.newPath(mode);
}

DnDMapModel.prototype.endDraw = function() {
	if (this.editMode == 'draw') {
		this.killTempPolyline();
	}
}

DnDMapModel.prototype.editModeChanged = function(mode) {

	/* THIS SHOULD BE HANDLED VIA AN UPDATE IN A VIEW */
	['markers','draw','scribble'].each(function(node) {
		Element.removeClassName($(node), 'selected');
	});
	Element.addClassName($(mode),'quikeditmode');	
	Element.addClassName($(mode),'selected');

	if (mode != this.editMode) {
		if (mode == DnDMapModel.SCRIBBLE) this.map.disableDragging();
		else this.map.enableDragging();
		
		if (mode == DnDMapModel.DRAW) this.startDraw(mode);
		else this.endDraw();

		this.editMode = mode;
	}
}



/*

DnDMapController class

*/
function DnDMapController(themap) {

	this.model = new DnDMapModel(this,themap);
	this.erasing = false;

	var controller = this;

	GEvent.addListener(this.model.map, "click", function(overlay,point) {
		this.controller.onMapClick(overlay,point);
	});

	GEvent.addListener(this.model.map, "moveend", function(event) {
		this.controller.model.showLocation();
	});

	GEvent.addDomListener(this.model.map.getPane(0).parentNode, "mousedown", function(event) {
		controller.onMouseDown(event);
	});

	GEvent.addDomListener(this.model.map.getPane(0).parentNode, "mouseup", function(event) {
		controller.onMouseUp(event);
	});

	GEvent.addDomListener(document, "mousedown", function(event) {
		controller.onDocumentMouseDown(event);
	});

	GEvent.addDomListener(document, "mousemove", function(event) {
		controller.onMouseMove(event);
	});
/*
	new Resizeable('map', {top: 0, left:0, right:0, bottom:6, 
		resize:function() { controller.onResize(); } } );
*/

	var min_map_size = (isIE) ? $('tools').offsetHeight - $('above').offsetHeight : $('tools').clientHeight - $('above').clientHeight;
	min_map_size -= 30;

	window.onresize = function() {
		var hdr = (isIE) ? $('header').offsetHeight : $('header').clientHeight;
		var ftr = (isIE) ? $('footer').offsetHeight : $('footer').clientHeight;
		var mapctr = (isIE) ? $('mapcontainer').offsetHeight : $('mapcontainer').clientHeight;
		var mapsize = controller.model.map.getContainer().clientHeight;
		var pageht = (isIE) ? document.documentElement.clientHeight : window.innerHeight;		

		var newmapsize = pageht - (70 + hdr + ftr + (mapctr - mapsize));
		newmapsize = (newmapsize < min_map_size) ? min_map_size : newmapsize;
		
		$('map').style.height = newmapsize + "px";
		controller.onResize();

	}

	window.onresize();

	this.initDragDrop();
	this.initBehaviours();
	
	Draggables.addObserver(this);

}

DnDMapController.prototype.initBehaviours = function() {

	var thecontroller = this;
	
	document.getElementsByClassName('quikeditmode').each(function(node) {
		node.onclick = function() {
			thecontroller.model.editModeChanged(node.id);
			return false;
		};
	});

	document.getElementsByClassName('clear').each(function(node) {
		node.onclick = function() {
			thecontroller.model.data.clear();
			thecontroller.model.map.clearOverlays();
		};
	});
}


DnDMapController.prototype.initDragDrop = function() {
	document.getElementsByClassName('mapicon').each(function(node) {
		new Draggable(node.id, {revert: true});
	});
	document.getElementsByClassName('maptext').each(function(node) {
		new Draggable(node.id, {revert: true});
	});
	document.getElementsByClassName('eraser').each(function(node) {
		new Draggable(node.id, {revert: true});
	});

	var thecontroller = this;
	Droppables.add('map', {accept:['maptext','mapicon','draggablemarker','draggabletextmarker'],
							onDrop:function(e) { thecontroller.model.drop(e);} });
}



// on DRAG start
DnDMapController.prototype.onStart = function(event,draggable) {
	this.model.editModeChanged('markers');
	

	if (draggable.element.className == 'eraser') { this.erasing = true; }
}

// on DRAG end
DnDMapController.prototype.onEnd = function(event, draggable) {
	
	if (draggable.element.className == 'eraser') {
		this.erasing = false;
	}
	

	if ((this.model.downmarker) && (this.model.dragged <= 2)) {
		// marker click but not drag -- open info window, but only for non-text markers
		if (this.model.downmarker.type = 'default') {
			this.model.markerOpenInfoWindow(this.model.downmarker);
		}
	}
	this.model.downmarker = null;
	this.model.dragged = 0;
	
}

// on DRAG
DnDMapController.prototype.onDrag = function(event) {
	this.model.dragged++;
}

DnDMapController.prototype.onResize = function() {
	this.model.map.checkResize();
}

DnDMapController.prototype.onMapClick = function(overlay,point) {
	if ( (point) && (this.model.editMode == DnDMapModel.DRAW) ) {
		this.model.extendPolyLine(point,true);
	}
}

DnDMapController.prototype.onDocumentMouseDown = function(event) {
	this.model.editModeChanged('markers');
}

DnDMapController.prototype.onMouseMove = function(event) {
	if (this.model.editMode == DnDMapModel.DRAW) {
		this.model.drawPolyline(event);
	} else if ( (this.model.editMode == DnDMapModel.SCRIBBLE) && (this.model.scribbling) ) {
		this.model.scribblePolyline(event);				
	}
}

DnDMapController.prototype.onMouseDown = function(event) {
	if (DnD.leftclick(event)) {
		if (this.model.editMode == DnDMapModel.SCRIBBLE) {
			this.model.scribbling = true;
			this.model.startDraw(DnDMapModel.SCRIBBLE);
		} 
	}
}

DnDMapController.prototype.onMouseUp = function(event) {
		
	if (this.model.editMode == DnDMapModel.SCRIBBLE) {
		this.model.scribbling = false;
		this.model.endDraw();
	}
}


DnDMapController.prototype.markerMouseDown = function(event) {

	var marker = Event.element(event).marker || Event.element(event).parentNode.marker;
	this.model.downmarker = marker;

	this.model.editModeChanged(DnDMapModel.MARKERS);
	
	if (DnD.leftclick(event)) {
		if ( ! ( (marker.type == 'poly') && (!marker.draggable) ) ) {
			if (Event.element(event).marker) {
				this.model.startDragMarker(event);
			} else {
				this.model.startDragTextMarker(event);
			} 

			if (!DnD.shiftKey(event)) this.model.dragDeleteMarker(event);
		}
		
	} else if (DnD.rightclick(event)) {
		// delete it on right click
		this.model.clickDeleteMarker(event);		
	}
}

DnDMapController.prototype.markerMouseOver = function(event) {
	if (this.erasing) {
		this.model.clickDeleteMarker(event);
	}
}

DnDMapController.prototype.invokeDrag = function(event,element) {
	// register as draggable and start dragging
	new Draggable(element.id, {revert: false}).initDrag(event);
	// HACK
	// Tell the drag and drop handler that this marker has moved, else
	// if the mouse doesn't move, no drop event will be triggered.
	Draggables.updateDrag(event);
}