function spcMapData()
{
	this.mapid   = null;
	this.mapname = null;
	this.iscityselect = false;

	this.types   = [];
	this.items   = [];
	this.accidents = [];		// Different from Items because the accident population can be different after each selection from the database

	this.tracks  = [];			// caches all tracks including trackpoints
	this.tracks_current = [];	// last loaded tracklist (might not contain trackpoints)
	this.trackrequests = [];
	
	this.trackcollections = new itemlist();
	
	this.nexttempid = -1;
	
	this.mapdatarequest = null;
	
	this.mapdataraw = null;		// mapdata as received from the server
	this.mapaccidents = null;
	
	
	this.maploaded = false;
	this.appliedtomap = false;
}

// overwrite me
spcMapData.prototype.onMapLoaded = function()
{
	this.maploaded		= true;
	this.appliedtomap	= false;
}

spcMapData.prototype.load = function(mapname, forcereload)
{
	var self = this;

	var params = { "class":			'map'
				, method:			'getmapdata'
				, map:				mapname
				, showunpublished:	true
				};

    if (forcereload)
		params.random = Math.floor(Math.random()*10000);

	if (this.mapdatarequest)//(typeof this.mapdatarequest.abort == 'function')
		this.mapdatarequest.abort();
		
	this.mapdatarequest =
		requestdoc({ url:				product.url.api
				   , method:            'GET'
					, params:			params
					, callback:          function()
						{
							if (this.req.responseText == '')
							{
								alert('Geen kaartgegevens ontvangen, is er verbinding met internet?');
								return;
							}
						
							//var mapdata = parseResponse(this.req.responseText); FIXME !!!!
							mapdata = parseResponse(this.req.responseText);
							
							if (!mapdata.success)
							{
								console.error("MapData / map loading failed: "+mapdata.reason);
								alert("Kaart kan niet geladen.\n\n"+mapdata.reason);
								return;
							}

							// before trashing all old item data, use it to remove the old items
							if (window.mygmap)
								mygmap.removeAllItems();

							window.mapdata = mapdata; // for .view for old map code // FIXME: don't rely on globals
							self.mapdataraw = mapdata;
							
							self.cancelTrackRequests();
						 
							self.items = mapdata.items;
							self.mapid = mapdata.id;
							self.mapname = mapdata.name;
							self.iscityselect = mapdata.iscityselect;
							
							console.warn("MapData / loaded map #"+self.mapid+":"+self.mapname);

							console.log("MapData / firing onMapLoaded");
							if (mapmeta.onMapLoaded)
								mapmeta.onMapLoaded(forcereload);

							console.log("MapData / firing mapswitch event.");
							product.events.mapswitch.fire(null);
							
							if (product.events.onceaftermaploaded)
							{
								product.events.onceaftermaploaded();
								product.events.onceaftermaploaded = null;
							}
							//product.onaftermapload(); // FIXME: find better way to handle event
						}
				   , callbackError:     function()
						{
							alert('spcMapData.load failed to load mapdata for map '+product.map);
						}
				   });
}

spcMapData.prototype.refreshAccidents = function(query, forcereload)
{
	var self = this;

	var params = { "class":			'map'
				, method:			'getmapdataaccident'
				, query:			query
				, showunpublished:	true
				};

	if (this.mapdatarequest)//(typeof this.mapdatarequest.abort == 'function')
		this.mapdatarequest.abort();
		
	this.mapdatarequest =
		requestdoc({ url:				product.url.api
				   , method:            'GET'
					, params:			params
					, callback:          function()
						{
							if (this.req.responseText == '')
							{
								alert('Geen kaartgegevens ontvangen, is er verbinding met internet?');
								return;
							}
						
							var accidents = parseResponse(this.req.responseText);
							
							/*
							if (!accidents.success)
							{
								console.error("MapData / map refreshAccidents failed: "+accidents.reason);
								alert("Ongevallen data kan niet geladen.\n\n"+accidents.reason);
								return;
							}
							*/

							// before trashing all old item data, use it to remove the old items
							if (window.mygmap) {
								mygmap.removeAllAccidents();
								self.accidents = accidents;
								mygmap.drawAllAccidents();
							}
							//product.onaftermapload(); // FIXME: find better way to handle event
						}
				   , callbackError:     function()
						{
							alert('spcMapData.refreshAccidents failed to load mapdata for map '+product.map);
						}
				   });
}

/** @short get data for a specific point
*/
spcMapData.prototype.fetchItemById = function(id, callback)
{
	var item = this.getItemById(id);

	if (item != null)
		callback(item);

//	this.mapdatarequest =
		requestdoc({ url:				product.url.api
				   , method:            'GET'
					, params:			{ "class":	'map'
										, method:	'getitem'
										, id:		id
										}
					, callback:          function()
						{
							if (this.req.responseText == '')
							{
								alert('?? '+mapname+' is empty?');
								return;
							}

							var mapitem = parseResponse(this.req.responseText);
/*
							if (!mapitem.success)
							{
								if (callback)
									callback(null);
							}
							else
							{
*/
							if (callback)
									callback(mapitem[0]);
//							}
						}
				   , callbackError:     function()
						{
							console.error('Failed to get data for point #'+id);
						}
				   });
}

spcMapData.prototype.defineType = function(id,data)
{
	this.types[id] = data;
}

// convert internal javascript key/ID to the real ID of the item
spcMapData.prototype.getRealID = function(itemID)
{
	return this.items[ itemID ].id;
}

spcMapData.prototype.getCurrentItemAttribute = function(att) {
	return this.items[product.selectedPoint][att];
}

// for internal use
spcMapData.prototype.getItemPosById = function(itemid)
{
	for (var i=0; i < this.items.length; i++)
	{
		if (this.items[i].id == itemid)
			return i;
	}
	
	console.log('itemid #'+itemid+' could not be found.');
	
	return -1;
}

spcMapData.prototype.getAccidentPosById = function(itemid)
{
	for (var i=0; i < this.accidents.length; i++)
	{
		if (this.accidents[i].id == itemid)
			return i;
	}
	
	console.log('getAccidentPosById(itemid) #'+itemid+' could not be found.');
	
	return -1;
}

spcMapData.prototype.getItemById = function(itemid)
{
	//var itempos = this.getItemPosById(itemid);
	//return this.items[ itempos ];

	return this.items[ this.getItemPosById(itemid) ];
}

spcMapData.prototype.getAccidentById = function(itemid)
{
	//var itempos = this.getItemPosById(itemid);
	//return this.items[ itempos ];

	return this.accidents[ this.getAccidentPosById(itemid) ];
}

spcMapData.prototype.getTempId = function()
{
	return this.nexttempid--;
}


// for internal use
/*
spcMapData.prototype.getItemPosById = function(itemid)
{
	for (var i=0; i < this.items.length; i++)
	{
		if (this.items[i].id == itemid)
			return i;
	}
}
*/


/*
f.e. getItemByTypeAndInstance(3,10) will return the item associated with (class 3)Track #10
*/
spcMapData.prototype.getItemByTypeAndInstance = function(typeid, instanceid)
{
	console.log('Looking up item with typeid #'+typeid+' and typeinstanceid #'+instanceid);

	for (var i=0; i < this.items.length; i++)
	{
		if (this.items[i].type == typeid && this.items[i].trackid && this.items[i].trackid == instanceid)
			return this.items[i];
	}
	
	console.warn('Item was not found.');
	
	return null;
}


spcMapData.prototype.removeItemByOwnerId = function()
{
	var removeditems = [];
	for (var i=0; i < this.items.length; i++)
	{
		if (this.items[i].id == itemid)
			removeditems.push( this.items.splice(i,1) );
	}
	return removeditems;
}

// returns item id
spcMapData.prototype.addMapItem = function(newitem)
{
	var newid = this.getTempId();
	newitem.id = newid;
	this.items.push(newitem);

	console.log('Added mapitem with id #'+newid);
	
	return newid;
}

// returns item ref
spcMapData.prototype.addMapItem2 = function(newitem)
{
	var newid = this.getTempId();
	newitem.id = newid;
	this.items.push(newitem);

	console.log('Added mapitem with id #'+newid);
	
	return newitem;
}

spcMapData.prototype.saveItem = function(newitem)
{
	requestdoc({ url:			product.url.api
			   , method:        'GET'
				, params:		{ "class":	'map'
								, method:	'saveitem'
								, id:		id
								}
				, callback:          function()
					{
						var mapitem = parseResponse(this.req.responseText);

						if (!mapitem.success)
						{
							if (callback)
								callback(null);
						}
						else
						{
							if (callback)
								callback(mapitem[0]);
						}
					}
			   , callbackError:     function()
					{
						console.error('saveItem call to server failed');

						if (callback)
							callback(null);
					}
			   });
}

spcMapData.prototype.getItemsByAttribute = function(attributename, isequal, value)
{
	var founditems = [];
	var arrLen = this.items.length;

	for(var item=0 ; item<arrLen; item++)
	{
		if (this.items[item][attributename]!=value ^ isequal)
			founditems.push(this.items[item]);
	}

	return founditems;
}

// get an array with all (referenced) items with the given shape
spcMapData.prototype.getItemsByShape = function(shape)
{
	// 1=point, 2=track
	var items = [];
	for(var tel=0; tel<this.items.length; tel++)
	{
		if(this.items[tel].shape == shape)
			items.push(this.items[tel]);
	}

	//console.log('Items with shape type #'+shape);
	//console.log(items);
	
	return items;
}

spcMapData.prototype.queryPoints = function(type,isNot,varName,alpha)
{
	// 0 true 'ID' false
	var finalList = [];

	//	Do a simple search if we don't need the results sorted

	if (varName == null)
	{
		var fllen = 0;
		var milen = this.items.length;
		for(var item=0 ; item < milen ; item++) {
			if (this.items[item].type==type ^ isNot)
				finalList[ fllen++ ] = item;
		}
		return finalList;
	}

	var   itemList=[]
		, sortList=[]
		, varValue;

	var arrLen = this.items.length;

	for(var item=0 ; item < arrLen ; item++)
	{
		if (this.items[item].type==type ^ isNot)
		{
			varValue = this.items[item][varName];

//			console.log ('Item '+item+' has value '+varValue+' for column '+varName);

//			alert('Item '+item+'/Field '+varName+'='+varValue);

//			if (varValue == undefined) alert('error');

			sortList[item] = varValue;

			itemList[varValue] = item;
		}
	}

	if (alpha==true) {
		if (varName.localeCompare) sortList.sort(sortString); else sortList.sort();
	} else {
		sortList.sort(sortNumber);
	}

	for(var key in sortList)
	{
		varValue = sortList[key];

//		finalList[itemList[varValue]] = varValue;

//		console.log(' finalList [ '+varValue+' ] = '+itemList[varValue]);

//		test=test+ varValue + '=' + itemList[varValue]+"\n";

		// check for undefined as a temporary workaround for Opera,
		// which during the sorting of names (string) created undefineds

		if (varValue != undefined)
			finalList[ varValue ] = itemList[varValue];
	}

	return finalList;
}

spcMapData.prototype.getSortedMapItems = function()
{
	var resultitems = [];
	var itemcount = this.items.length;
	for(var tel=0; tel<itemcount; tel++)
		resultitems.push(this.items[tel]);

	// sort results
	var strvar = "";
	if (strvar.localeCompare)
	{
		resultitems.sort(function(a,b) { return a.name.localeCompare(b.name); });
	}
	else
	{
		resultitems.sort(function(a,b) { return a.name > b.name });
	}
	return resultitems;
}


spcMapData.prototype.getRoutesList = function getRoutesList(mapid, onload)
{
	var self = this;

	requestdoc({ url:			product.url.api
				, method:		'GET'
				, params:		{ "class":		'gpslogmanager'
								, "method":		'gettracklist'
								, "cityid":		mapid
								}
				, callback:
					function()
					{
						var tracklist = parseResponse(this.req.responseText);

						// change content of tracks_current to new list while keeping the current array instance
						// so it doesn't break the pointer the YUI DataTable has to the array.
						self.tracks_current.splice(0, self.tracks_current.length);//, tracklist);
						for(var tel=0; tel<tracklist.length; tel++)
						  self.tracks_current.push(tracklist[tel]);
						
						for(var tel=0; tel<tracklist.length; tel++)
							self.importTrack(tracklist[tel]);
						
						if(onload)
							onload();
					}
				, callbackError:
					function()
					{
						alert('Failed to get a list of routes.');
					}
				}
			);
}

// FIXME: voeg functies samen
spcMapData.prototype.getRoutesListByBounds = function getRoutesList(south, north, west, east, onload)
{
	var self = this;

	requestdoc({ url:			product.url.api
				, method:		'GET'
				, params:		{ "class":		'gpslogmanager'
								, "method":		'gettracklistbybounds'
								, "south":		south
								, "north":		north
								, "west":		west
								, "east":		east
								}
				, callback:
					function()
					{
						var tracklist = parseResponse(this.req.responseText);

						// change content of tracks_current to new list while keeping the current array instance
						// so it doesn't break the pointer the YUI DataTable has to the array.
						self.tracks_current.splice(0, self.tracks_current.length);//, tracklist);
						for(var tel=0; tel<tracklist.length; tel++)
						  self.tracks_current.push(tracklist[tel]);
						
						for(var tel=0; tel<tracklist.length; tel++)
							self.importTrack(tracklist[tel]);
						
						if(onload)
							onload();
					}
				, callbackError:
					function()
					{
						alert('Failed to get a list of routes.');
					}
				}
			);
}

spcMapData.prototype.clearTrackCache = function(track)
{
	this.tracks  = [];			// caches all tracks including trackpoints
}

spcMapData.prototype.importTrack = function(track)
{
	for(var tel=0; tel<this.tracks.length; tel++)
	{
		if(this.tracks[tel].id == track.id)
		{
			this.tracks[tel] = track; // update track definition
			return;
		}
	}

	this.tracks.push(track); // add track definition
}

spcMapData.prototype.importTrackCurrent = function(track)
{
	for(var tel=0; tel<this.tracks_current.length; tel++)
	{
		if(this.tracks_current[tel].id == track.id)
		{
			this.tracks_current[tel] = track; // update track definition
			return;
		}
	}

	this.tracks_current.push(track); // add track definition
}

/** @short get track data as recieved from server (use mygmap.getItemByTrackId to get item instead)
*/
spcMapData.prototype.getTrackDataById = function(trackid)
{
	for(var tel=0; tel<this.tracks.length; tel++)
	{
		if(this.tracks[tel].id == trackid)
			return this.tracks[tel];
	}
	
	return false;
}

spcMapData.prototype.loadTrackWithCallback = function(trackid, callbackfunc)
{
	var self = this;

	console.log('loadTrackWithCallback for track #'+trackid);

	var trackdata = this.getTrackDataById(trackid);
	
	console.log(trackdata);
	
	if (trackdata && trackdata.track)
	{
		callbackfunc(trackdata);
	}
	else
	{
		this.getRoute(trackid
					, function() { var trackdata = self.getTrackDataById(trackid); callbackfunc(trackdata); }
					, function() { console.error('Failure to load route.'); }
					);
	}
}

spcMapData.prototype.cancelTrackRequests = function()
{
	console.warn("Cancelling "+this.trackrequests.length+" track requests.");
	for (var tel=0; tel<this.trackrequests.length; tel++)
	{
		if (this.trackrequests[tel] != null)
			this.trackrequests[tel].abort();
		//removeditems.push( this.trackrequest.splice(tel,1) );
	}
	this.trackrequests = [];
}

// FIXME: maybe don't abort, but cancel drawing of the routes?
spcMapData.prototype.getRoute = function getRoute(routeid, onload, onerror)
{
	var self = this;

	var request = requestdoc({ url:			product.url.api
				, method:		'GET'
				, params:		{ "class":		'gpslogmanager'
								, "method":		'gettrack'
								, "trackid":	routeid
								, "raw":		product.state.devmode
								}
				, callback:
					function()
					{
						var track = parseResponse(this.req.responseText);
						
						if (typeof track == "undefined" || (typeof track.success != "undefined" && !track.success))
						{
							console.log("Failed to get track information for track #"+routeid);
							return;
						}

						//console.log(track);

						self.importTrack(track);
						
						if (onload)
							onload(track);
					}
				, callbackError:
					function()
					{
						console.error('Failed to get route data for track #'+routeid);
						
						if (onerror)
							onerror();
					}
				}
			);

	this.trackrequests.push(request);
}

spcMapData.prototype.getRouteCollectionsForMap = function(mapid, callback)
{
	var self = this;
	dovizservercall({ "class":		"gpslogmanager"
					, "method":		"gettrackcollectionsformap"
					, "params":		{ "map": mapid }
					, "callback":	function(returnvalue)
									{
										self.trackcollections.setItems(returnvalue.trackcollections);
										console.log(self.trackcollections.items);
										callback(returnvalue);
									}
					});
}
