/*
	Test model-view-controller for 
	local-google-local
*/

/*
	MapApp
*/
var RangeMapApp = Class.extend({}, {
	
	initialize: function(options) {
		this.model = new GeoLocalModel(options);
		this.listview = new ListView(this.model);
		this.map = new MapView(this.model);
		this.loading = new LoadingDataView(this.model);
	}
});

/*
	Loading data view
*/
var LoadingDataView = Class.extend(View, {

	initialize: function(model) {
		this.model = model;
		this.model.attach(this);
		
		this._loading_semaphore = 0;
		this.loading_element = this.model.loading_element;
	},
	
	update: function(event) {		
		if (event.type == GeoEvent.LOADSTART) {	
			this._loading_semaphore++;
			Element.show($(this.loading_element));	
		} else if (event.type == GeoEvent.LOADEND) {
			this._loading_semaphore--;
			if (this._loading_semaphore <= 0) {
				Element.hide($(this.loading_element));
				this._loading_semaphore = 0;
			}
		}
	}	
	
});

/*
	ListView
*/
var ListView = Class.extend(View, {

	initialize: function(model) {
		this.model = model;
		
		this.initialize_state();
		
		this.controller = new ListController(this);
		this.model.attach(this);	
	},

	initialize_state: function() {
		this.listelement = $(this.model.list_element);	
	},
	
	update: function(event) {		
		if (event.type == GeoEvent.MOVE) {	
			// do nothing to the list on a map move
		} else if (event.type == GeoEvent.SELECT) {
			if (this.model.lastSelectedObject) {
				Element.removeClassName(this.model.lastSelectedObject.id,'selected');
			}
			Element.addClassName(this.model.selectedObject.id,'selected');
		}
	}	
	
});


/*
	MapView
*/
var MapView = Class.extend(View, {
	
	initialize: function(model) {
		this.model = model;
		
		this.initialize_state();		

		this.controller = new MapController(this);
		this.model.attach(this);	
	},
	
	initialize_state: function() {
		app.use('tools.gmap.kmap');
		this.map = new geobirds_gmap($(this.model.map_element));

		this.map.view = this;
		this.map.setCenter(new GLatLng(this.model.start_lat, this.model.start_lng),
					this.model.start_zoom);	
		this.model.move(this.map.getBounds());
	},
	
	update: function(event) {
		if (event.type == GeoEvent.MOVE) {
			var rangepoints = this.model.objects[this.model.selectedObject];
			for (var i in rangepoints) {
				if (rangepoints[i].marker.added) {
					// do nothing!!
				} else {
					this.map.addOverlay(rangepoints[i].marker);
					rangepoints[i].marker.added = true;
				}
			}
		} else if (event.type == GeoEvent.SELECT) {
			if (this.model.lastSelectedObject) {
				lazy_clear(this.model, this.model.lastSelectedObject, this.map, this.model.clear_interval);
			}
		}
	}	

});


/*
	ListController
*/
var ListController = Class.extend(Controller, {

	initialize: function(view) {
		Controller.prototype.initialize.call(this,view);
		this.rules = null;
		
		this.initialize_state();
	},
	
	initialize_state: function() {
		var model = this.model;
	}
});


/*
	MapController
*/
var MapController = Class.extend(Controller, {

	initialize: function(view) {
		Controller.prototype.initialize.call(this,view);
		
		this.initialize_state();
	},
	
	initialize_state: function() {
		GEvent.addListener(this.view.map, 'moveend', function() {
			this.view.model.move(this.getBounds());
		});
	}
	
});


/*
	GeoModel
*/
var GeoModel = Class.extend(Model, {
	
	initialize: function() {
		$C(Model).initialize.call(this);
		this.selectedObject = null;	
		this.lastSelectedObject = null;
		this.objects = {};
	},
	
	add: function(object) {
		if ( (!(this.objects[object.species])) ||
		     (!(this.objects[object.species][object.id])) ) {

			var icon = new GIcon();
			if (object.colour == '1') {
				icon.image = this.winter_image2;
			} else if (object.colour == '2') {
				icon.image = this.winter_image2;
			} else if (object.colour == '3') {
				icon.image = this.winter_image3;
			} else if (object.colour== '4') {
				icon.image = this.summer_image2;
			} else if (object.colour == '5') {
				icon.image = this.summer_image2;
			} else if (object.colour == '6') {
				icon.image = this.summer_image3;
			} else if (object.colour == '7') {
				icon.image = this.yearround_image2;
			} else if (object.colour == '8') {
				icon.image = this.yearround_image2;
			} else if (object.colour == '9') {
				icon.image = this.yearround_image3;
			}


			//var size = Math.round(10 + Math.pow(object.count, 1/1.6));
			//if (size > 40) { size = 40; }
			var size = 16;
			
			icon.iconSize = new GSize(size, size);
			icon.iconAnchor = new GPoint(size/2, size/2);

			object.marker = new GMarker(new GLatLng(object.latitude,object.longitude), {icon:icon, clickable:false});
			object.marker.added = false;

			if (!this.objects[object.species]) this.objects[object.species] = {};

			this.objects[object.species][object.id] = object;
		}
	},

	append: function(newobjects) {
		for (var i = 0; i < newobjects.length; i++) {
			this.add(newobjects[i]);
		}
	},

	remove: function(object) {
		this.objects[object.species][object.id] = null;
	},

	clear: function() {
		this.objects = {};
	},
	
	move: function(bounds) {
		this.notify(new GeoEvent(GeoEvent.MOVE,bounds));
	},

	select: function(object) {
		this.lastSelectedObject = this.selectedObject;
		this.selectedObject = object;
		this.notify(new GeoEvent(GeoEvent.SELECT,{}));
	},
	
	selectid: function(id) {
		this.select(id);
	}
});

/*
	GeoDBModel
*/
var GeoDBModel = Class.extend(GeoModel, {

	initialize: function(options) {
		$C(GeoModel).initialize.call(this);
		this.loadedBounds = null;
		this.currentBounds = null;
		this.server = options.db_server;
		this.method = options.db_method;
		
		this.load_interval_id = 0;
		this.counter = 0;
		this.split_bounds = [];
	},

	objectsInBounds: function(bounds,species) {
		var ret = [];
		var species_obj = this.objects[species];
		for (var i in species_obj) {
			if (this.currentBounds.contains(species_obj[i].marker.getPoint())) ret.push(species_obj[i]);
		}
		return ret;
	},	
	
	move: function(bounds) {
		this.loadSpeciesData(bounds);
	},
	
	select: function(object) {
		$C(GeoModel).select.call(this,object);
		if (this.selectedObject != this.lastSelectedObject) {
			this.loadedBounds = null;
			this.loadSpeciesData(this.currentBounds);
		}
	},
	
	loadSpeciesData: function(bounds) {

		this.currentBounds = bounds;
		
		if (this.selectedObject) {

			if (!this.loadedBounds) {
				// first load
				this.loadedBounds = bounds;
				this.loadBounds(bounds);

			} else if (this.loadedBounds.containsBounds(bounds)) {
				// load finer detail on zoon in
				// need a zoom in handler...
				this.loadBounds(bounds);

			} else if (bounds.containsBounds(this.loadedBounds)) {
				// load -- zoom out
				this.loadedBounds = bounds;
				this.loadBounds(bounds);

			} else if (!this.loadedBounds.intersects(bounds)) {
				// geocoder discrete move -- expand bounds (for now)
				this.loadedBounds = this.loadedBounds.expand(bounds);
				this.loadBounds(this.loadedBounds);

			} else {
				// pan -- load edges

				var a = this.loadedBounds;
				var b = bounds;
				var bounds1 = new GLatLngBounds();
				var bounds2 = new GLatLngBounds();

				// Find the edges that don't overlap
				if (b.getSouthWest().lat() < a.getSouthWest().lat()) {
					if (b.getSouthWest().lng() < a.getSouthWest().lng()) {
						// south west		
						bounds1 = new GLatLngBounds( new GLatLng(b.getSouthWest().lat(),b.getSouthWest().lng()), new GLatLng(a.getNorthEast().lat(),a.getSouthWest().lng()) );
						bounds2 = new GLatLngBounds( new GLatLng(b.getSouthWest().lat(),a.getSouthWest().lng()), new GLatLng(a.getSouthWest().lat(),a.getNorthEast().lng()) );
					} else {
						// south east
						bounds1 = new GLatLngBounds( new GLatLng(b.getSouthWest().lat(),a.getNorthEast().lng()), new GLatLng(a.getNorthEast().lat(),b.getNorthEast().lng()) );
						bounds2 = new GLatLngBounds( new GLatLng(b.getSouthWest().lat(),a.getSouthWest().lng()), new GLatLng(a.getSouthWest().lat(),a.getNorthEast().lng()) );
					}		

				} else {
					if (b.getSouthWest().lng() < a.getSouthWest().lng()) {
						// north west		
						bounds1 = new GLatLngBounds( new GLatLng(a.getSouthWest().lat(),b.getSouthWest().lng()), new GLatLng(b.getNorthEast().lat(),a.getSouthWest().lng()) );
						bounds2 = new GLatLngBounds( new GLatLng(a.getNorthEast().lat(),a.getSouthWest().lng()), new GLatLng(b.getNorthEast().lat(),a.getNorthEast().lng()) );
					} else {
						// north east		
						bounds1 = new GLatLngBounds( new GLatLng(a.getSouthWest().lat(),a.getNorthEast().lng()), new GLatLng(b.getNorthEast().lat(),b.getNorthEast().lng()) );
						bounds2 = new GLatLngBounds( new GLatLng(a.getNorthEast().lat(),a.getSouthWest().lng()), new GLatLng(b.getNorthEast().lat(),a.getNorthEast().lng()) );
					}	
				}

				this.loadBounds(bounds1);
				this.loadBounds(bounds2);
				this.loadedBounds = this.loadedBounds.expand(bounds);
			}
		}
	},


	loadBounds: function(bounds) {
		
		this.counter = 0;
		this.split_bounds = bounds.split(this.split_num);	
		lazy_loader(this.load_interval,this.split_bounds, this);
		
	},
		
	hitDatabase: function(bounds) {
		if ( (bounds) && (bounds.getSouthWest) ) {
			this.notify(new GeoEvent(GeoEvent.LOADSTART,{}));

			var request = Utils.ajaxRequest(this.server,this.method,
						[bounds.getSouthWest().lng(),bounds.getNorthEast().lng(),
						bounds.getSouthWest().lat(),bounds.getNorthEast().lat(),this.selectedObject,
						this.max_points],
						this.onDatabaseLoad, this);
		}
	},	
	

	onDatabaseLoad: function(request) {	
		var newobjs = Utils.createObjects(request, Object);
		this.append(newobjs);
		this.notify(new GeoEvent(GeoEvent.MOVE,this.currentBounds));
		this.notify(new GeoEvent(GeoEvent.LOADEND,this.currentBounds));		
	}		
});
	
	
/*
	GeoLocalModel
*/
var GeoLocalModel = Class.extend(GeoDBModel, {

	initialize: function(options) {
		$C(GeoDBModel).initialize.call(this,options);
		
		Object.extend(this,options);
	}
});


/*
	GeoEvent
*/
var GeoEvent = Object.extend(MVCEvent, {
	SELECT:	0,
	MOVE: 1,
	LOADSTART: 2,
	LOADEND: 3
});


/******************************************
	Lazy Loader code
	
	HANDLE THIS IN AN OO MANNER!!	
********************************************/
var model, timeout;
var arr = [];
var split_bounds = [];
var lazy_loader = function(p_timeout,p_split_bounds,p_model) {
	split_bounds = split_bounds.concat(p_split_bounds);
	model = p_model;
	timeout = p_timeout;
	
	wait_for_clear();	
}

function wait_for_clear() {
	if (arr.length == 0) {
		run();		
	} else {
		window.setTimeout(wait_for_clear, 20);
	}
}

function run() {
	if (split_bounds.length > 0) {
		var thebounds = split_bounds.shift();	
		window.setTimeout("run()", timeout);	
		model.hitDatabase(thebounds);
	}
}

var species, map, ind;
function lazy_clear(p_model, p_species, p_map, p_timeout) {
	model = p_model;
	
	species = p_species;
	map = p_map;
	timeout = p_timeout;

	$('loadingtext').innerHTML = 'unloading data';
	Element.show($(model.loading_element));	

	clearOverlays(species);
/*	
	for (var i in model.objects[p_species]) {
		arr.push([i,p_species]);
	}
	ind = 0;		
	
	if (arr) {
		if (arr.length < model.clear_limit) {
			clearOverlays(species);
		} else {
			var tmp = arr.pop();
			window.setTimeout("clear('"+tmp[1]+"','"+tmp[0]+"')", timeout);
		}
	}
*/
}

/*
function clear(species,id) {
	map.removeOverlay(model.objects[species][id].marker);
	if (arr) {
		if (arr.length < model.clear_limit) {
			clearOverlays(species);			
		} else {
			var tmp = arr.pop();
			window.setTimeout("clear('"+tmp[1]+"','"+tmp[0]+"')", timeout);
		}
	}
}
*/

/****** removed lazy clear code b/c of api speed upgrades in v2.64 */

function clearOverlays(species) {
	for (var i in model.objects[species]) {
		model.objects[species][i].marker.added = false;	
	}
	map.clearOverlays();
	arr = [];
	Element.hide($(model.loading_element));	
	$('loadingtext').innerHTML = 'loading data';
}

	
