var categoriesLength = categories.length;

var isOpera = false, isIE = false;
if (typeof(window.opera) != 'undefined') {
	isOpera = true;
}
if (!isOpera && navigator.userAgent.indexOf('MSIE') != -1) {
	isIE = true;
}

/**
 * A general helper function for creating html elements. <div> as default element type
 * used for infowindows and sidebar
 */
function createElem(opt_className, opt_html, opt_tagName) {
	var tag = opt_tagName || "div";
	var elem = document.createElement(tag);
	if(opt_html) {
		// Note: the regular expression will only match singular html tags with no embedded tags and only one attribute!!
		var innerTag = opt_html.match(".*?<([a-z][a-z0-9]*)(?:\\s+)([a-z][a-z]+)=(?:(?:\"|')([^\"|']*)(?:\"|'))\\s*>(.*)</.*>");
		if (innerTag !== null) {
			var innerTagElem = document.createElement(innerTag[1]);
			innerTagElem.setAttribute(innerTag[2], innerTag[3]);
			innerTagElem.appendChild(document.createTextNode(innerTag[4]));
			elem.appendChild(innerTagElem);
		} else {
			
			elem.appendChild(document.createTextNode(opt_html));
		}
	}
	if(opt_className) {elem.className = opt_className;}
	return elem;
}

/**
 * Marker icon
 */
function tinyImage(opt_color, opt_preload) {
	var color = opt_color || "undefined";
	var src_ = urlpref + "/binaries/burgerlink/images/markers/" + color.replace(/\./,"-") + ".png";
	if(opt_preload) {
		var preImage = new Image();
		preImage.src = src_;
	}
	return src_;
}

function tinyIcon(opt_color) {
	var tiny = new GIcon();
	tiny.image = tinyImage(opt_color);
	tiny.shadow = "http://labs.google.com/ridefinder/images/mm_20_shadow.png";
	tiny.iconSize = new GSize(12, 20);
	tiny.shadowSize = new GSize(22, 20);
	tiny.iconAnchor = new GPoint(6, 20);
	tiny.infoWindowAnchor = new GPoint(5, 1);
	tiny.imageMap = [4,0,0,4,0,7,3,11,4,19,7,19,8,11,11,7,11,4,7,0];
	tiny.transparent = "http://maps.google.com/mapfiles/transparent.png";
	return tiny;
}

/**
 * sidebar with categories
 */
function SideBar(block_element, opt_options) {
	//var opts = opt_options || {};
	this.division = createElem("sidebar-contents");
	block_element.appendChild(this.division);
	this.show = function() {this.division.style.display = "block";};
	this.hide = function() {this.division.style.display = "none";};
	this.cats = [];
	this.addEntry = function(point,opt_options) {
		var opts = opt_options || {};
		var iLabel = opts.iLabel || 2;
		var label = createElem("sidebar-entry", point.textArray[iLabel], "a");
		label.href = "#";
		label.style.display = "block";
		label.onclick = function() {GEvent.trigger(point.marker,'click'); return false;};//x-browser
		label.onfocus = function() {GEvent.trigger(point.marker,'click'); return false;};
		this.division.appendChild(label);
		GEvent.addListener(point.marker,'click',function() {label.focus(); return false;});
	};
	this.clear = function() {
		while(this.division.firstChild) {
			this.division.removeChild(this.division.firstChild);
		}
	};
}

/**
 * Map
 */
_mPreferMetric = true; //to make size sure for IE too
var map = new GMap2(document.getElementById("map"), {size: new GSize(410, 500)});
// ====== set up marker mouseover tooltip div ======
var tooltip = document.createElement("div");
document.getElementById("map").appendChild(tooltip);
tooltip.style.visibility="hidden";
map.setCenter(new GLatLng( 52.1,5.2), 7);
map.addControl(new GLargeMapControl());
map.addControl(new GMapTypeControl());
map.addControl(new GScaleControl());
map.addMapType(G_PHYSICAL_MAP);
map.setMapType(G_PHYSICAL_MAP);
map.removeMapType(G_HYBRID_MAP);
map.openInfoWindowHtml(map.getCenter(),"Nice to see you.");
map.closeInfoWindow(); //preloading infowindow
map.allMarkers = [];
var myBar = new SideBar(document.getElementById("sidebar"));
var CAT_ICONS = [];
CAT_ICONS.DEFAULT_ICON = tinyIcon("overig");

// ====== This function displays the tooltip ======
// it can be called from an icon mousover or a side_bar mouseover
function showTooltip(markers) {
	var style = "background-color:white; white-space:nowrap; padding:2px;";
	var label;
	// Drop all old contents
	if (!isIE) {
		tooltip.setAttribute("style", "border:1px #006699 solid;");
	} else {
		tooltip.style.setAttribute("cssText", "border:1px #006699 solid;");
	}
	while (tooltip.firstChild) {
		tooltip.removeChild(tooltip.firstChild);
	}
	for(var j = 0, mLength = markers.length; j < mLength; j++) {
		if(markers[j].point.isshown) {
			label = markers[j].point.label;
			var tooltipDiv = document.createElement("div");
			if (!isIE) {
				tooltipDiv.setAttribute("style", style);
			} else {
				tooltipDiv.style.setAttribute("cssText", style);
			}
			tooltipDiv.appendChild(document.createTextNode(label));
			tooltip.appendChild(tooltipDiv);
		}
		var point = map.getCurrentMapType().getProjection().fromLatLngToPixel(map.getBounds().getSouthWest(),map.getZoom());
		var offset = map.getCurrentMapType().getProjection().fromLatLngToPixel(markers[j].getPoint(),map.getZoom());
		var anchor = markers[j].getIcon().iconAnchor;
		var width = markers[j].getIcon().iconSize.width;
		var pos = new GControlPosition(G_ANCHOR_BOTTOM_LEFT, new GSize(offset.x - point.x - anchor.x + width,- offset.y + point.y +anchor.y));
		pos.apply(tooltip);
		tooltip.style.visibility="visible";
	}
}

function getTheCookie() {
	try {
		var cookies = document.cookie.split(";");
		for(var i = 0; i < cookies.length; i++) {
			var oneCookie = cookies[i].split("=");
			if(oneCookie[0] === "savedChecks") {
				return eval("({" + unescape(oneCookie[1]) + "})");
			}
		}
	} catch(err) {}
	return [];
}

function saveTheCookie() {
	//alert('cookiesave');
	var value = [];
	var elem = document.getElementById("selectAll");
	value.push("'selectAll':" + elem.checked);
	for(var i = 0; i < categoriesLength; i++) {
		if(myBar.cats[categories[i].id] !== undefined){
			value.push("'" + categories[i].id + "':" + myBar.cats[categories[i].id].check.checked);
		}
	}
	var d = new Date();
	d.setTime(d.getTime() - 1000000);
	document.cookie = "savedChecks=1;expires=" + d.toGMTString();
	document.cookie = "savedChecks=" + escape(value);
}

function updateSelectAll() {
	var ischecked = true;
	var elem = document.getElementById("selectAll");
	for(var i = 0; i < categoriesLength; i++) {
		if(myBar.cats[categories[i].id] !== undefined){
			ischecked = ischecked && myBar.cats[categories[i].id].check.checked;
		}
	}
	elem.checked = ischecked;
}

function selectAll() {
	var elem = document.getElementById("selectAll");
	var ischecked = elem.checked;
	for(var i = 0; i < categoriesLength; i++) {
		if(myBar.cats[categories[i].id] !== undefined){
			if(myBar.cats[categories[i].id].check.checked !== ischecked) {
				myBar.cats[categories[i].id].check.click();
			}
		}
	}
	saveTheCookie();
}

/**
 * category to a sidebar
 */
function BarCategory(sideBar, catId, catName, opt_options) {
	var me = this;
	var opts = opt_options || {};
	me.division = createElem("sidebar-cat");
	var cssClasses = "sidebar-cat-header cat-header-" + catId;
	var cat = createElem(cssClasses);
	me.pin = createElem("sidebar-cat-image", null, "img");
	me.pin.src = opts.icon.image;
	cat.appendChild(me.pin);
	cat.appendChild(document.createTextNode(" "));
	var check = createElem("sidebar-cat-check", null, "input");
	me.header = cat;
	check.type = "checkbox";
	check.checked = opts.checked || true;
	cat.appendChild(check);
	cat.appendChild(document.createTextNode(" "));
	var checkLabel = createElem("sidebar-cat-label", catName ,"span");
	cat.appendChild(checkLabel);
	me.show = function() {
		me.division.style.display = "block";
		me.pin.style.visibility = "visible";
	};
	me.hide = function() {
		me.division.style.display = "none";
		me.pin.style.visibility = "hidden";
	};
	me.hilight = function() {me.header.className = cssClasses +" hilight-cat-header";};
	me.lolight = function() {me.header.className = cssClasses;};
	sideBar.division.appendChild(cat);
	sideBar.division.appendChild(me.division);
	sideBar.cats[catId] = me;
	var numberOfNodes = sideBar.division.childNodes.length;
	// Because two nodes are added (one with the categorie, and one divider), there is a factor 2 that
	// has to be taken into account
	// Only start when there are nodes to compare
	if (numberOfNodes > 2) {
		// By using a for-loop, nodes will bubble to their proper position.
		var hasSwapped;
		do {
			hasSwapped = false;
			for (var i = 0; i < ((numberOfNodes / 2) - 1); i++) {
				// Strip the classname down to categorie-name
				var name1 = sideBar.division.childNodes[2*i].className.match("cat-header-[^ ]+").toString();
				var name2 = sideBar.division.childNodes[2*(i + 1)].className.match("cat-header-[^ ]+").toString();
				name1 = name1.substring(name1.lastIndexOf("-")+1);
				name2 = name2.substring(name2.lastIndexOf("-")+1);
				// get the required postion of each node
				var position1 = 0;
				var position2 = 0;
				for(var j = 0; j < categoriesLength; j++) {
					if (name1 === categories[j].id) {position1 = j;}
					if (name2 === categories[j].id) {position2 = j;}
				}
				// swap to nodes if the first needs a higher position than the
				// second. Because this is in a for-loop the first node will bubble to
				// its position.
				var swap = false;
				if(position1 > position2) {
					swap = true;
					var node1 = sideBar.division.childNodes[2*i];
					var node2 = sideBar.division.childNodes[2*(i + 1)];
					// Let's not forget the divider
					var divisionNode = sideBar.division.childNodes[2*(i + 1) + 1];
					sideBar.division.insertBefore(node2,node1);
					sideBar.division.insertBefore(divisionNode,node1);
				}
				hasSwapped = hasSwapped || swap; 
			}
		}
		while(hasSwapped);
	}
	me.points = [];
	me.showMarkers = function() {
		map.closeInfoWindow();
		for(var i = 0; i < me.points.length; i++) {
			me.points[i].isshown = true;
			me.points[i].marker.show();
		}
	};
	me.hideMarkers = function() {
		map.closeInfoWindow();
		for(var i = 0, pointsLength = me.points.length; i < pointsLength; i++) {
			me.points[i].isshown = false;
			var hide = true;
			if(me.points[i].marker.point.isshown) {
				hide = false;
			}
			if(hide) {
				me.points[i].marker.hide();
				me.points[i].marker.closeInfoWindow();
			}
		}
	};
	function update() {
		saveTheCookie();
		if(me.check.checked) {
			me.show();
			me.showMarkers();
			me.hilight();
		} else {
			me.hide();
			me.hideMarkers();
			me.lolight();
		}
		updateSelectAll();
	}
	check.onclick = update;
	me.update = update;
	me.check = check;
	me.addEntry = function(point,opt_options) {
		//var opts = opt_options || {};
		me.points.push(point);
	};
	me.clear = function() {
		while(me.division.firstChild) {
			me.division.removeChild(me.division.firstChild);
		}
	};
}

/**
 * GMap2.showBounds() method. Fit bounds to viewport with paddings.
 * @ param bounds_ GLatLngBounds()
 * @ param opt_options Optional options object {top, right, bottom, left, save}
 */
GMap2.prototype.showBounds = function(bounds_, opt_options) {
	var opts = opt_options || {};
	opts.top = opt_options.top*1 || 0;
	opts.left = opt_options.left*1 || 0;
	opts.bottom = opt_options.bottom*1 || 0;
	opts.right = opt_options.right*1 || 0;
	opts.save = opt_options.save || true;
	opts.disableSetCenter = opt_options.disableSetCenter || false;
	var ty = this.getCurrentMapType();
	var port = this.getSize();
	if(!opts.disableSetCenter) {
		var virtualPort = new GSize(port.width - opts.left - opts.right, port.height - opts.top - opts.bottom);
		this.setZoom(ty.getBoundsZoomLevel(bounds_, virtualPort));
		var xOffs = (opts.left - opts.right) / 2;
		var yOffs = (opts.top - opts.bottom) / 2;
		var bPxCenter = this.fromLatLngToDivPixel(bounds_.getCenter());
		var newCenter = this.fromDivPixelToLatLng(new GPoint(bPxCenter.x - xOffs, bPxCenter.y - yOffs));
		this.setCenter(newCenter);
		if(opts.save) {this.savePosition();}
	}
	var portBounds = new GLatLngBounds();
	portBounds.extend(this.fromContainerPixelToLatLng(new GPoint(opts.left, port.height-opts.bottom)));
	portBounds.extend(this.fromContainerPixelToLatLng(new GPoint(port.width-opts.right, opts.top)));
	return portBounds;
};

/**
 * parseCsv()
 * @return an array of GLatLng() objects
 * @param opt_options object {lat, lng} integers defining the csv cells of coordinates (default: {lat:1, lng:0})
 */
String.prototype.parseCsv = function(opt_options) {
	var results = [],
		opts = opt_options || {},
		iLat = opts.lat || 1,
		iLng = opts.lng || 0,
		lines = this.split("\n");
	for(var i = 0, linesLength = lines.length; i < linesLength; i++) {
		var blocks = lines[i].split('"');
		//finding commas inside quotes. Replace them with '::::'
		for(var j = 0, blocksLength = blocks.length; j < blocksLength; j++) {
			if(j%2) {
				blocks[j] = blocks[j].replace(/,/g,'::::');
			}
		}	//@author Esa 2008, keep this note.
		lines[i] = blocks.join("");
		var lineArray = lines[i].split(",");
		var lat = parseFloat(lineArray[iLat]);
		var lng = parseFloat(lineArray[iLng]);
		var point = new GLatLng(lat,lng);
		//after splitting by commas, we put hidden ones back
		for(var cell in lineArray) {
			if (lineArray.hasOwnProperty(cell)) {
				lineArray[cell] = lineArray[cell].replace(/::::/g,',');
			}
		} //corrupted line step-over
		if(!isNaN(lat + lng)) {
			point.textArray = lineArray;
			results.push(point);
		}
	}
	return results;
};

/**
 * Create the DHTML pop-up window which is shown if a point(marker) is clicked.
* @param point  The point to create the DHTML pop-up for.
* @param nearMarkers A array which contains all markers which are near the supplied point
* @param opt_options object {lat, lng} integers defining the csv cells of coordinates (default: {lat:1, lng:0})
*/
function createInfoWindow(point, nearMarkers, opt_options) {
	GEvent.addListener(point.marker, "click", function() {
		var opts = opt_options || {};
		var start = opts.iLabel || 2;
		var iwNode = createElem("info-window");
		var iwRows = [];
		for (var j = 0, nearMarkersLength = nearMarkers.length; j < nearMarkersLength; j++) { //Add for each nearMarker a item to the pop-up
			var currentMarker = nearMarkers[j];
			if (currentMarker.point.isshown) { //Only add the nearMarker if the marker is shown.
				for(var i = start, textArrayLength = currentMarker.point.textArray.length; i < textArrayLength; i++) {
					//alert(currentMarker.point.textArray[i]);
					var row = createElem("iw-cell-" + i, currentMarker.point.textArray[i]);
					iwRows.push(row);
					iwNode.appendChild(row);
				}
			}
		}
		iwRows[0].className += " iw-header";
		map.openInfoWindow(point, iwNode, {maxWidth:300});
	});
}

/**
 * Create the markers, with infowindow.
 * Create sidebar categories and entries.
 */
var bounds = new GLatLngBounds();
GMap2.prototype.populate = function(points, options) {
	var opts = options || {},
		noCat = !(opts.cat || opts.iCat), // no cat is false if either of opts.cat or opts.iCat is set
		catId = opts.cat || "",
		catName = opts.name || "",
		bar = opts.sidebar,
		myCat,
		newCats = [],
		catIdlc;
	for(var i = 0, pointsLength = points.length; i < pointsLength; i++) {
		if(opts.iCat) { // category from file contents
			catId = points[i].textArray[opts.iCat];
		}
		catIdlc = catId.toLowerCase(); //javascript is case sensitive, but the provided categorie files both exist in lower and upper case, not always with content
		var theIcon = opts.icon || CAT_ICONS[catIdlc] || CAT_ICONS.DEFAULT_ICON;
		myCat = bar.cats[catIdlc];
		if(!myCat && !noCat) { // create a category if not found
			myCat = new BarCategory(bar, catId, catName, {icon: theIcon});
			newCats.push(myCat);
		}
		var iLabel = opts.iLabel || 2;
		var label = points[i].textArray[iLabel];
		points[i].label = label;
		points[i].isshown = false;
		// Walk through all already defined markers. Check if the first point of a marker is
		// less then 10m form the current point. If so, we ar going to work with that marker.
		var nearMarkers = [];
		for(var j = 0, allMarkersLength = map.allMarkers.length; j < allMarkersLength; j++) {
			if(map.allMarkers[j].point.distanceFrom(points[i]) < 10) {
				nearMarkers.push(map.allMarkers[j]);
		 	}
		}
		// If this point is not going to be added to an existing marker, create a new marker and add it to the list of markers
		// otherwise associate the marker of this point with the found marker
		var newMarker = new GMarker(points[i],{ABCtitle:label, icon:theIcon});
		points[i].marker = newMarker;
		map.allMarkers.push(newMarker);
		nearMarkers.unshift(newMarker);
		//======The new marker "mouseover" and "mouseout" listeners======
		var binder = function(_binder) { return function() { showTooltip(_binder); }; };
		GEvent.addListener(newMarker, "mouseover", binder(nearMarkers));
		GEvent.addListener(newMarker, "click", function() {tooltip.style.visibility = "hidden";});
		GEvent.addListener(newMarker, "mouseout", function() {tooltip.style.visibility = "hidden";});
		this.addOverlay(newMarker);
		bounds.extend(points[i]); // this must be considered
		createInfoWindow(points[i],nearMarkers,opts);
		// Add the point to the marker that this point references to (so circular reference)
		points[i].marker.point = points[i];
		myCat.addEntry(points[i],opts);
	}
	if(myCat !== undefined) {
		var saved = (opts.theCookie !== undefined) && opts.theCookie[catIdlc]; //Check if the value is set in the cookie
		if (saved !== undefined &&  saved === false) { //If the value is set but false don't set the checkbox.
			myCat.check.checked = false;
		} else { // The value is not set in the cookie or the value is rendered the first time. Show the checkbox checked.
			myCat.check.checked = true;
		}
		myCat.update();
	}
	/*for(var i = 0; i < newCats.length; i++) {
		var saved = opts.theCookie !== undefined && opts.theCookie[catId];
		newCats[i].check.checked = opts.checked || saved || true;
		newCats[i].update();
	}*/
	//var paddings = {top:30, right:10, bottom:10, left:50};
	//this.showBounds(bounds,paddings);
};

/**
 * create infowindow
 */
 // A function to create a tabbed marker and set up the event window
function createTabbedMarker(point,html1,html2,label1,label2) {
	GEvent.addListener(marker, "click", function() {
		marker.openInfoWindowTabsHtml([new GInfoWindowTab(label1,html1), new GInfoWindowTab(label2,html2)]);
	});
	return marker;
}

/**
 * This function triggers the downloading and parsing of a selected text file
 * marker, sidebar and infowindow data is extracted from the file
 */
function ajaxLoad(textFile, opt_options) {
	var opts = opt_options || {};
	opts.sidebar = myBar;
	var process = function(material) {
		var entries = material.parseCsv(material, opts);
		map.populate(entries, opts);
	};
	var result = new GDownloadUrl(textFile, process);
}

//Preload loop
for(var i = 0; i < categoriesLength; i++) {
	tinyImage(categories[i].id, true);
}

window.onload = function() {
	var theCookie = getTheCookie();
	for(var i = 0; i < categoriesLength; i++) {
		CAT_ICONS[categories[i].id] = tinyIcon(categories[i].id);
		ajaxLoad(categories[i].doc, { cat: categories[i].id, name: categories[i].name, 'theCookie': theCookie});
	}
};
