var editor_mode = 0;
var mymap;

var polyconf_show_street_names = 5; // Zoom level for when to start showing street names
var polyconf_show_cities = 5; /* City outlines will be filled on this level and further away */
var polyconf_show_districts = 4; /* Shown from polyconf_show_cities until this*/

var streetLabelsRenderer = new L.StreetLabels({
	collisionFlg: true,
	propertyName: 'name',
	showLabelIf: function (layer) {
		switch (layer.geometry.type) {
		case "LineString":
			if (mymap.getZoom() <= polyconf_show_street_names)
				return false;
			break;
		case "Polygon":
			if (mymap.getZoom() > polyconf_show_street_names)
				return false;
			break;
		default:
			return false;
		}
		
		return true;
	},
	fontStyle: {
		dynamicFontSize: true,
		fontSize: 10,
		fontSizeUnit: "px",
		lineWidth: 3.0,
		fillStyle: "black",
		strokeStyle: "white",
	},
});

streetLabelsRenderer._getDynamicFontSize = function (layer) {
	zoom = mymap.getZoom();

	switch (layer.geometry.type) {
	case "LineString":
		if (zoom <= 8)
			return 11;
		else
			return 2**(zoom - 9) * 11;
		break;
	case "Polygon":
		if (zoom >= 4)
			return 30;
		return 20;
	default:
		break;
	}
	return 10;
}

var style_outlines = {
		radius: 8,
		fillColor: "#ff7800",
		color: "black",
		opacity: 1.0,
		fillOpacity: 0.5
	};

var style_streets = {
	};


// Projection fix from: https://gis.stackexchange.com/questions/200865/leaflet-crs-simple-custom-scale
var factorx = 1 / 256 * 4;
var factory = factorx;
var originx = 7000 + 8 + 0.5;
var originy = 7000 + 8 - 0.5;
var zoom_level_real = 6;

L.CRS.pr = L.extend({}, L.CRS.Simple, {
	projection: L.Projection.LonLat,
	transformation: new L.Transformation(factorx,factorx * originx,-factory,factory * originy),

	scale: function(zoom) {
		return Math.pow(2, zoom);
	},

	zoom: function(scale) {
		return Math.log(scale) / Math.LN2;
	},

	distance: function(latlng1, latlng2) {
		var dx = latlng2.lng - latlng1.lng
		  , dy = latlng2.lat - latlng1.lat;

		return Math.sqrt(dx * dx + dy * dy);
	},
	infinite: true
});

// Init map
mymap = L.map('mapid', {
	renderer: streetLabelsRenderer,
	editable: true,
	crs: L.CRS.pr
}).setView([0, 0], 5);

var mapheight = 16384;
var mapwidth = mapheight;
var sw = mymap.unproject([0, 0], zoom_level_real);
var ne = mymap.unproject([mapwidth, mapheight], zoom_level_real);
var layerbounds = new L.LatLngBounds(sw,ne);

var layers = L.control.layers({}, {}).addTo(mymap);

function load_svg(name, url, active=1) {
	var xhttp_ps = new XMLHttpRequest();
	xhttp_ps.onreadystatechange = function() {
		if (this.readyState == 4) {
			if (this.status == 200) {
				var svgElement = document.createElementNS("http://www.w3.org/2000/svg", "svg");
				svgElement.setAttribute('xmlns', "http://www.w3.org/2000/svg");
				svgElement.setAttribute('viewBox', "0 0 16384 16384");
				svgElement.innerHTML = xhttp_ps.responseText;
				var svgElementBounds = [[0, 0], [1000, 1000]];
				var overlay = L.svgOverlay(svgElement, svgElementBounds);
				layers.addOverlay(overlay, name);
				if (active)
					overlay.addTo(mymap);
				return overlay;
			} else {
				alert("Error: Could not load SVG map layer (" + name + ")");
			}
		}
	}
	;
	xhttp_ps.open("GET", url, true);
	xhttp_ps.send();
}

function load_tiles(name, id) {
	var satellite = L.tileLayer('https://notsyncing.net/maps.linux-forks.de/tiles/{id}/{z}/{y}/{x}.png', {
		maxZoom: 14 /*8*/,
		maxNativeZoom: 6,
		minNativeZoom: 0,
		minZoom: 0,
		noWrap: true,
		attribution: 'Map data &copy; <a href="https://wiki.linux-forks.de/mediawiki/index.php/Maps">Linux-Forks</a>',
		id: id,
		tileSize: 256,
		zoomOffset: 0,
		opacity: 1.0,
		bounds: layerbounds
	});
	layers.addBaseLayer(satellite, name);
	return satellite;
}

var current_location = "";
var current_feature = null;

function load_geojson(name, url, iconname, iconcolor, active=1, style={}) {
	var xhttp_ps = new XMLHttpRequest();
	xhttp_ps.onreadystatechange = function() {
		if (this.readyState == 4) {
			if (this.status == 200) {
				onEachFeature = null;
				pointToLayer = null;
				filter = null;
				
				switch (iconname) {
				case "street":
					onEachFeature = function(feature, layer) {
						layer.on("click", function (e) {
							current_feature = feature;
						});
					};
					break;
				case "outline":
					onEachFeature = function(feature, layer) {
						layer.myTag = iconname;
						layer.myName = name;
						layer.on("click", function (e) {
							current_location = feature.properties.name;
						});
					}
					break;
				default: /* else it is a marker with the specified icon */
					onEachFeature = function(feature, layer) {
							label = String(feature.properties.name)
							layer.bindPopup('<h1><a href="https://wiki.linux-forks.de/mediawiki/index.php/' + feature.properties.name + '">' + feature.properties.name + '</a> (' + feature.geometry.coordinates + ')</h1>' + '<p><img style="width:100%" src="' + feature.properties.image + '"></p>' + '<p>' + feature.properties.description + '</p>');
							layer.bindTooltip(label, {
								permanent: true,
								direction: "center",
								className: "city-names"
							}).openTooltip().on('click', jump_to_marker);
						};
					pointToLayer = function(feature, latlng) {
							label = String(feature.properties.name)
							return new L.marker(latlng,{
								icon: L.AwesomeMarkers.icon({
									icon: iconname,
									markerColor: iconcolor
								})
							}).bindTooltip(label, {
								permanent: false,
								direction: "center",
								opacity: 0.7
							}).openTooltip();
						};
					break;
				}
				var json = JSON.parse(xhttp_ps.responseText);
				geojson = L.geoJSON(json, {
					style: style,
					onEachFeature: onEachFeature,
					pointToLayer: pointToLayer,
					filter: filter
				});
				layers.addOverlay(geojson, name);
				
				if (active)
					geojson.addTo(mymap);
				return geojson;
			} else {
				console.log("Error: Could not load geojson map layer (" + name + ").");
			}
		}
	}
	;
	xhttp_ps.open("GET", url, true);
	xhttp_ps.send();
}

load_tiles("Satellite (2020-04-09)", 'world-2020-04-09').addTo(mymap);
load_tiles("Satellite (2019-05-04, wrong coords)", 'world-2019-05-04');

//load_svg("Test", "./overlay.svg", 0);
load_geojson("Cities", "./geojson/cities.json", "city", "red");
load_geojson("Stations", "./geojson/stations.json", "train", "blue");
load_geojson("Shops", "./geojson/shops.json", "shopping-cart", "green");
load_geojson("Parks", "./geojson/parks.json", "tree", "darkgreen");
load_geojson("Libraries", "./geojson/libraries.json", "book-open", "darkblue");
load_geojson("CW Complexes", "./geojson/cw_complexes.json", "border-all", "black");
load_geojson("Courts", "./geojson/courts.json", "balance-scale", "black");
load_geojson("Waterway", "./geojson/waterway.json", "water", "darkblue");
load_geojson("Train Depots", "./geojson/depots.json", "wrench", "violet");
load_geojson("Streets", "./geojson/streets.json", "street", "blue", 0);

L.control.scale().addTo(mymap);

function resolve_latlng(latlng, recenter = 0) {
	latlng.lng = Math.round(latlng.lng);
	latlng.lat = Math.round(latlng.lat);
	return latlng;
}

function get_current_location_str() {
	var latlng = resolve_latlng(mymap.getCenter());
	return latlng.lng + "," + latlng.lat + "," + mymap.getZoom();
}

/* Important: Do not use mymap.setView, use this function instead */
function jump_to(latlng, zoom = -1) {
	if (zoom == -1)
		zoom = mymap.getZoom();
	if (!editor_mode) {
		pos = resolve_latlng(latlng);
		document.location.hash = "#" + pos.lng + "," + pos.lat + "," + zoom;
	} else {
		mymap.setView(latlng, zoom);
	}
}

function jump_to_marker(e) {
	jump_to(e.target.getLatLng());
}

function prompt_location() {
	var str = prompt("Enter coordinates to jump to:\n\n" +
			"Format: x, y [, zoom]", get_current_location_str());
	if (str) {
		if (!editor_mode)
			document.location.hash = "#" + str;
		else
			onHashChange(null, "#" + str);
	}
}

var search_element;
function toggle_search() {
	if (el = document.getElementById('searchbar')) {
		el.parentNode.removeChild(el);
		return;
	}

	if (!search_element) {
		search_element = document.createElement("div");
		search_element.style.overflow = "scroll";
		search_element.style.padding = "6px";
		search_element.style.height = "100%";
		search_element.innerHTML = '<input style="width:100%;" id="search_query" name="lifo_map_search" type="search" placeholder="Start typing to search..." oninput="search(event)"><div id="search_results"></div>' ;
	}

	td = document.getElementById('windowtr').insertCell(0);
	td.style.width = "400px";
	td.style.borderRight = "1px solid black";
	td.style.verticalAlign = "top";
	td.id = "searchbar";
	td.appendChild(search_element);

	document.getElementById('search_query').select();
}

function htmlEntities(str) {
    return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}

function polyline_get_middle_coords(coords) {
	var ret = [2];
	ret[0] = coords[0][0] + (coords[coords.length - 1][0] - coords[0][0]) / 2;
	ret[1] = coords[0][1] + (coords[coords.length - 1][1] - coords[0][1]) / 2;
	return ret;
}

var highlighted_line;
var default_street_color = "#3388ff";
function search(e) {
	var query = document.getElementById("search_query").value;
	document.getElementById('search_results').innerHTML = "";
	if (true) {
		if (query.length == 0)
			query = "!@#$%^&"; // Cheap workaround to (hopefully) match nothing
		results = document.createElement("ul");
		for (var i = 0; i < layers._layers.length; i++) {
			if (!layers._layers[i].layer._layers)
				continue;

			for (key in layers._layers[i].layer._layers) {
				item = layers._layers[i].layer._layers[key];
				switch (item.feature.geometry.type) {
				case "Point":
					regex = new RegExp(query, 'i');
					if (item.feature.properties.name.match(regex)) {
						el = document.createElement("li");
						el.innerHTML = "[" + layers._layers[i].name + "] " + '<a href="#" onclick="layers._layers[' + i + '].layer._layers[' + item._leaflet_id + '].fire(\'click\'); return false;">' + htmlEntities(item.feature.properties.name) + "</a>";
						results.appendChild(el);
					}
					break;
				case "LineString":
					if (item.options.color != default_street_color) { // De-hilight last search
						item.options.color = default_street_color;
						item.redraw();
					}
					regex = new RegExp(query, 'i');

					if (item.feature.properties.name.match(regex)) {
						item.options.color = "#FF0000";
						item.redraw();
						el = document.createElement("li");
						zpos = polyline_get_middle_coords(item.feature.geometry.coordinates);
						el.innerHTML = "[" + layers._layers[i].name + "] " + '<a href="#" onclick="latLng2 = L.latLng(' + zpos[1] + ',' + zpos[0] + '); jump_to(latLng2); return false;">' + htmlEntities(item.feature.properties.name) + "</a>";
						results.appendChild(el);
					}
					break;
				case "Polygon":
					regex = new RegExp(query, 'i');

					if (item.feature.properties.name.match(regex)) {
						el = document.createElement("li");
						zpos = resolve_latlng(layers._layers[i].layer._layers[item._leaflet_id].getCenter());
						el.innerHTML = "[" + layers._layers[i].name + "] " + '<a href="#" onclick="latLng2 = L.latLng(' + zpos.lat + ',' + zpos.lng + '); jump_to(latLng2, ' + polyconf_show_cities + '); return false;">' + htmlEntities(item.feature.properties.name) + "</a>";
						results.appendChild(el);
					}
					break;
				default:
					break;
				}
			}
		}
		document.getElementById('search_results').appendChild(results);
	}
	return false;
}

L.MyControl = L.Control.extend({
	options: {
		position: 'topleft',
		callback: null,
		kind: '',
		html: ''
	},
	onAdd: function (map) {
		var container = L.DomUtil.create('div', 'leaflet-control leaflet-bar'),
		link = L.DomUtil.create('a', '', container);

		link.href = '#';
		link.title = this.options.title;
		link.innerHTML = this.options.html;
		L.DomEvent.on(link, 'click', L.DomEvent.stop)
		.on(link, 'click', function () {
			window.LAYER = this.options.callback.call();
		}, this);
	return container;
	}
});

L.SearchControl = L.MyControl.extend({
	options: {
		position: 'topleft',
		callback: toggle_search,
		title: 'Search',
		html: '🔍'
	}
});
L.GotoControl = L.MyControl.extend({
	options: {
		position: 'topleft',
		callback: prompt_location,
		title: 'Jump to coordinates',
		html: '⌖'
	}
});
mymap.addControl(new L.SearchControl());
mymap.addControl(new L.GotoControl());

var popup = L.popup();

L.AwesomeMarkers.Icon.prototype.options.prefix = 'fa';
var baseballIcon = L.AwesomeMarkers.icon({
	icon: 'coffee',
	markerColor: 'red'
});

function onMapClick(e) {
	var addinfo = "";
	if (current_location != "")
		addinfo = " (part of " + current_location + ")";
	if (current_feature) {
		popup.setLatLng(e.latlng).setContent("This is " + current_feature.properties.name + addinfo).openOn(mymap);
	} else {
		pos = resolve_latlng(e.latlng);
		popup.setLatLng(e.latlng).setContent("You clicked the map at " + pos.lng + "," + pos.lat + addinfo).openOn(mymap);
	}
	current_feature = null;
	current_location = "";
}

mymap.on('click', onMapClick);

var is_user_drag = 0;

function update_hash_from_position(e) {
	if (is_user_drag)
		is_user_drag = 0;
	else
		return;
	if (!editor_mode)
		document.location.hash = "#" + get_current_location_str();
}

function dragstart(e) {
	is_user_drag = 1;
}

function update_aa_status() {
	if (mymap.getZoom() > zoom_level_real)
		document.getElementById('mapid').classList.add("no-aa");
	else
		document.getElementById('mapid').classList.remove("no-aa");
}

function update_street_width() {
	zoom = mymap.getZoom();
	var w;
	if (zoom <= 6) {
		w = 2;
	} else {
		w = 2**zoom * 0.016 * 2;
	}
	var myStyle = {weight: w, opacity: 0.7};
	for (var i = 0; i < layers._layers.length; i++) {
		if (!layers._layers[i].layer._layers)
			continue;
		layers._layers[i].layer.setStyle(myStyle);
	}
}

function update_outline_visibility() {
	zoom = mymap.getZoom();
	mymap.eachLayer( function(layer) {
		if ( layer.myTag && layer.myTag === "outline") {
			var fillOpacity;
			if (zoom <= polyconf_show_cities)
				fillOpacity = 0.5;
			//else if (zoom == polyconf_show_cities + 1)
			//	opacity = 0.2;
			else
				fillOpacity = 0.0;
				
			layer.setStyle({fillOpacity: fillOpacity});
		}
	});
}

mymap.on('zoomend', function () {is_user_drag = 1; update_hash_from_position();});
mymap.on('zoomend', update_aa_status);
mymap.on('zoomend', update_street_width);
mymap.on('zoomend', update_outline_visibility);
mymap.on('moveend', update_hash_from_position);
mymap.on('dragstart', function () { is_user_drag = 1;});
mymap.on('keydown', function (e) { if (e.originalEvent.code.match(/Arrow.*/)) is_user_drag = 1;});
mymap.on('overlayadd', function (e) { update_street_width(); update_outline_visibility(); });
function onHashChange(e, hash=null) {
	if (!hash)
		hash = document.location.hash;
	if (hash == "#" + get_current_location_str())
		return; // We're already there
	coordstr = hash.slice(1).replace(/%20/g, "").split(",");
	if (coordstr.length < 2)
		coordstr.push(0); // Default y
	if (coordstr.length < 3)
		coordstr.push(mymap.getZoom()); // Default zoom
	var latlng = L.latLng(parseFloat(coordstr[1]), parseFloat(coordstr[0]));
	mymap.setView(latlng, parseInt(coordstr[2]));
}

window.addEventListener("hashchange", onHashChange, false);
onHashChange(null);