/*
	========================================================================
	The 2022 /r/place Atlas

	An Atlas of Reddit's 2022 /r/place, with information to each
	artwork	of the canvas provided by the community.

	Copyright (c) 2017 Roland Rytz <roland@draemm.li>
	Copyright (c) 2022 r/placeAtlas2 contributors

	Licensed under the GNU Affero General Public License Version 3
	https://place-atlas.stefanocoding.me/license.txt
	========================================================================
*/

const prodDomain = "place-atlas.stefanocoding.me"

const innerContainer = document.getElementById("innerContainer");
const container = document.getElementById("container");
const canvas = document.getElementById("highlightCanvas");
const context = canvas.getContext("2d");

let zoom = 1;

if (window.devicePixelRatio) {
	zoom = 1 / window.devicePixelRatio;
}

const maxZoom = 128;
const minZoom = 0.1;

let zoomOrigin = [0, 0];
let scaleZoomOrigin = [0, 0];

let dragging = false;
let lastPosition = [0, 0];

const viewportSize = [0, 0];

document.getElementById("entriesListDonate").addEventListener("click", function (e) {
	document.getElementById("donateOverlay").style.display = "flex";
});

document.getElementById("closeDonateButton").addEventListener("click", function (e) {
	document.getElementById("donateOverlay").style.display = "none";
});

function applyView() {

	//console.log(zoomOrigin, scaleZoomOrigin);
	//console.log(scaleZoomOrigin[0]);

	scaleZoomOrigin[0] = Math.max(-1000, Math.min(1000, scaleZoomOrigin[0]));
	scaleZoomOrigin[1] = Math.max(-1000, Math.min(1000, scaleZoomOrigin[1]));

	zoomOrigin = [scaleZoomOrigin[0] * zoom, scaleZoomOrigin[1] * zoom];

	innerContainer.style.height = (~~(zoom * 2000)) + "px";
	innerContainer.style.width = (~~(zoom * 2000)) + "px";

	innerContainer.style.left = ~~(container.clientWidth / 2 - innerContainer.clientWidth / 2 + zoomOrigin[0] + container.offsetLeft) + "px";
	innerContainer.style.top = ~~(container.clientHeight / 2 - innerContainer.clientHeight / 2 + zoomOrigin[1] + container.offsetTop) + "px";

}

let atlas = null;
window.atlas = atlas
let atlasAll = null
window.atlasAll = atlasAll

if (document.location.host !== prodDomain) document.body.dataset.dev = ""

init();

async function init() {
	// For Reviewing Reddit Changes
	//let resp = await fetch("../tools/temp_atlas.json");
	const resp = await fetch("./atlas.json");
	atlas = await resp.json();
	atlas.sort(function (a, b) {
		if (a.center[1] < b.center[1]) {
			return -1;
		}
		if (a.center[1] > b.center[1]) {
			return 1;
		}
		// a must be equal to b
		return 0;
	});

	atlasAll = updateAtlasAll(atlas);

	await updateTime(currentPeriod, currentVariation)

	//console.log(document.documentElement.clientWidth, document.documentElement.clientHeight);

	zoomOrigin = [0, 0];
	applyView();

	let initialPinchDistance = 0;
	let initialPinchZoom = 0;
	let initialPinchZoomOrigin = [0, 0];

	let desiredZoom;
	let zoomAnimationFrame;

	let mode = "view";

	const args = window.location.search;
	const params = new URLSearchParams(args)
	if (args) {
		mode = params.get("mode")
		if (!mode) {
			mode = "view";
		}

		// Backwards compatibility for old links using "search" id arg
		if (params.has('id') && params.get('mode') !== 'draw') {
			const id = params.get('id')
			params.delete('id')
			const newLocation = new URL(window.location)
			newLocation.hash = id
			newLocation.search = params
			window.history.replaceState({}, '', newLocation)
		}
	}

	document.body.dataset.mode = mode

	initGlobal()
	if (mode !== "draw") initViewGlobal()

	if (mode === "draw") {
		initDraw();
	} else if (mode === "about") {
		window.location = "./about.html";
	} else if (mode === "overlap") {
		if (initOverlap) {
			initOverlap();
		}
	} else if (mode.startsWith("diff")) {
		try {
			const liveResp = await fetch("https://place-atlas.stefanocoding.me/atlas.json");
			let liveJson = await liveResp.json();
			liveJson = updateAtlasAll(liveJson)
			// console.log(liveJson)

			const liveAtlasReduced = liveJson.reduce(function (a, c) {
				a[c.id] = c;
				return a;
			}, {});
			// Mark added/edited entries
			atlasAll = atlasAll.map(function (entry) {
				if (liveAtlasReduced[entry.id] === undefined) {
					entry.diff = "add";
				} else if (JSON.stringify(entry) !== JSON.stringify(liveAtlasReduced[entry.id])) {
					entry.diff = "edit";
				}
				return entry;
			});

			// Mark removed entries
			const atlasReduced = atlasAll.reduce(function (a, c) {
				a[c.id] = c;
				return a;
			}, {});
			const removedEntries = liveJson.filter(entry =>
				atlasReduced[entry.id] === undefined
			).map(entry => {
				entry.diff = "delete"
				return entry
			})
			atlasAll.push(...removedEntries)

			if (mode.includes("only")) {
				atlasAll = atlasAll.filter(function (entry) {
					return typeof entry.diff == "string"
				});
			}

		} catch (error) {
			console.warn("Diff mode failed to load, reverting to normal view.", error);
		} finally {
			await updateTime()
			if (initOverlap && mode.includes("overlap")) {
				initOverlap();
			} else {
				initView();
			}
		}
	} else if (mode === "explore") {
		initExplore();
	} else {
		initView();
	}

	document.getElementById("zoomInButton").addEventListener("click", function (e) {

		/*if(zoomAnimationFrame){
			window.cancelAnimationFrame(zoomAnimationFrame);
		}*/

		const x = container.clientWidth / 2;
		const y = container.clientHeight / 2;

		initialPinchZoomOrigin = [
			scaleZoomOrigin[0],
			scaleZoomOrigin[1]
		];

		initialPinchZoom = zoom;

		lastPosition = [x, y];
		zoom = zoom * 2;
		zoom = Math.max(minZoom, Math.min(maxZoom, zoom));

		applyZoom(x, y, zoom);

	});

	document.getElementById("zoomOutButton").addEventListener("click", function (e) {

		/*if(zoomAnimationFrame){
			window.cancelAnimationFrame(zoomAnimationFrame);
		}*/

		const x = container.clientWidth / 2;
		const y = container.clientHeight / 2;

		initialPinchZoomOrigin = [
			scaleZoomOrigin[0],
			scaleZoomOrigin[1]
		];

		initialPinchZoom = zoom;

		lastPosition = [x, y];
		zoom = zoom / 2;
		zoom = Math.max(minZoom, Math.min(maxZoom, zoom));

		applyZoom(x, y, zoom);
	});

	document.getElementById("zoomResetButton").addEventListener("click", function (e) {
		zoom = 1;
		zoomOrigin = [0, 0];
		scaleZoomOrigin = [0, 0];
		updateLines();
		applyView();
	});

	container.addEventListener("dblclick", function (e) {
		/*if(zoomAnimationFrame){
			window.cancelAnimationFrame(zoomAnimationFrame);
		}*/

		const x = e.clientX - container.offsetLeft;
		const y = e.clientY - container.offsetTop;

		initialPinchZoomOrigin = [
			scaleZoomOrigin[0],
			scaleZoomOrigin[1]
		];

		initialPinchZoom = zoom;

		lastPosition = [x, y];

		if (e.ctrlKey) {

			zoom = zoom / 2;

		} else {

			zoom = zoom * 2;
		}

		zoom = Math.max(minZoom, Math.min(maxZoom, zoom));
		applyZoom(x, y, zoom);

		e.preventDefault();
	});


	container.addEventListener("wheel", function (e) {

		/*if(zoomAnimationFrame){
			window.cancelAnimationFrame(zoomAnimationFrame);
		}*/

		const x = e.clientX - container.offsetLeft;
		const y = e.clientY - container.offsetTop;

		initialPinchZoomOrigin = [
			scaleZoomOrigin[0],
			scaleZoomOrigin[1]
		];

		initialPinchZoom = zoom;

		lastPosition = [x, y];

		// Check if we are zooming by pixels
		// https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/deltaMode
		if (e.deltaMode === 0) {
			// Scale the pixel delta by the current zoom factor
			// We want to zoom faster when closer, and slower when further
			// This creates a smoother experience
			zoom -= e.deltaY * (0.001 * zoom);
		} else {
			if (e.deltaY > 0) {

				zoom = zoom / 2;

			} else if (e.deltaY < 0) {

				zoom = zoom * 2;
			}
		}

		zoom = Math.max(minZoom, Math.min(maxZoom, zoom));
		applyZoom(x, y, zoom);

		e.preventDefault();
	}, { passive: true });

	/*function setDesiredZoom(x, y, target){
		zoom = (zoom*2 + target)/3;
		//console.log(zoom);
		if(Math.abs(1 - zoom/target) <= 0.01){
			zoom = target;
		}
		applyZoom(x, y, zoom);
		if(zoom != target){
			zoomAnimationFrame = window.requestAnimationFrame(function(){
				setDesiredZoom(x, y, target);
			});
		}
	}*/

	container.addEventListener("mousedown", function (e) {
		mousedown(e.clientX, e.clientY);
		e.preventDefault();
	});

	container.addEventListener("touchstart", function (e) {

		if (e.touches.length == 2) {
			e.preventDefault();
		}

		touchstart(e);

	}, { passive: true });

	function mousedown(x, y) {
		lastPosition = [x, y];
		dragging = true;
	}

	function touchstart(e) {

		if (e.touches.length == 1) {

			mousedown(e.touches[0].clientX, e.touches[0].clientY);

		} else if (e.touches.length == 2) {

			initialPinchDistance = Math.sqrt(
				Math.pow(e.touches[0].clientX - e.touches[1].clientX, 2)
				+ Math.pow(e.touches[0].clientY - e.touches[1].clientY, 2)
			);

			initialPinchZoom = zoom;
			initialPinchZoomOrigin = [
				scaleZoomOrigin[0],
				scaleZoomOrigin[1]
			];

			mousedown(
				(e.touches[0].clientX + e.touches[1].clientX) / 2,
				(e.touches[0].clientY + e.touches[1].clientY) / 2
			);

		}

	}

	window.addEventListener("mousemove", function (e) {
		updateLines();
		mousemove(e.clientX, e.clientY);
		if (dragging) {
			e.preventDefault();
		}
	});
	window.addEventListener("touchmove", function (e) {

		if (e.touches.length == 2 || e.scale > 1) {
			e.preventDefault();
		}

		touchmove(e);

	},
		{ passive: false }
	);

	function mousemove(x, y) {
		if (dragging) {
			const deltaX = x - lastPosition[0];
			const deltaY = y - lastPosition[1];
			lastPosition = [x, y];

			zoomOrigin[0] += deltaX;
			zoomOrigin[1] += deltaY;

			scaleZoomOrigin[0] += deltaX / zoom;
			scaleZoomOrigin[1] += deltaY / zoom;

			previousZoomOrigin = [zoomOrigin[0], zoomOrigin[1]];
			previousScaleZoomOrigin = [scaleZoomOrigin[0], scaleZoomOrigin[1]];

			updateLines();
			applyView();
		}
	}

	function touchmove(e) {

		updateLines();

		if (e.touches.length == 1) {

			mousemove(e.touches[0].clientX, e.touches[0].clientY);

		} else if (e.touches.length == 2) {

			const newPinchDistance = Math.sqrt(
				Math.pow(e.touches[0].clientX - e.touches[1].clientX, 2)
				+ Math.pow(e.touches[0].clientY - e.touches[1].clientY, 2)
			);

			zoom = initialPinchZoom * newPinchDistance / initialPinchDistance;

			const x = (e.touches[0].clientX + e.touches[1].clientX) / 2 - container.offsetLeft;
			const y = (e.touches[0].clientY + e.touches[1].clientY) / 2 - container.offsetTop;

			applyZoom(x, y, zoom);

		}

	}

	function applyZoom(x, y, zoom) {

		const deltaX = x - lastPosition[0];
		const deltaY = y - lastPosition[1];

		const pinchTranslateX = (x - container.clientWidth / 2 - deltaX);
		const pinchTranslateY = (y - container.clientHeight / 2 - deltaY);

		scaleZoomOrigin[0] = initialPinchZoomOrigin[0] + deltaX / zoom + pinchTranslateX / zoom - pinchTranslateX / initialPinchZoom;
		scaleZoomOrigin[1] = initialPinchZoomOrigin[1] + deltaY / zoom + pinchTranslateY / zoom - pinchTranslateY / initialPinchZoom;

		zoomOrigin[0] = scaleZoomOrigin[0] * zoom;
		zoomOrigin[1] = scaleZoomOrigin[1] * zoom;

		applyView();
		updateLines();
	}

	window.addEventListener("mouseup", function (e) {
		if (dragging) {
			e.preventDefault();
		}
		mouseup(e.clientX, e.clientY);
	});
	window.addEventListener("touchend", touchend);

	function mouseup(x, y) {
		if (dragging) {
			dragging = false;
		}
	}

	function touchend(e) {

		if (e.touches.length == 0) {

			mouseup();

		} else if (e.touches.length == 1) {
			initialPinchZoom = zoom;
			lastPosition = [e.touches[0].clientX, e.touches[0].clientY];
		}

	}

	window.addEventListener("resize", function () {
		//console.log(document.documentElement.clientWidth, document.documentElement.clientHeight);

		applyView();
	});

	document.body.dataset.initDone = ''

}

function updateAtlasAll(atlas) {
	if (!atlas) atlas = atlasAll
	for (const atlasIndex in atlas) {
		if (Array.isArray(atlas[atlasIndex].path)) {
			const currentPath = atlas[atlasIndex].path
			atlas[atlasIndex].path = {}
			atlas[atlasIndex].path[defaultPeriod] = currentPath
		}
		if (Array.isArray(atlas[atlasIndex].center)) {
			const currentCenter = atlas[atlasIndex].center
			atlas[atlasIndex].center = {}
			atlas[atlasIndex].center[defaultPeriod] = currentCenter
		}
		if (atlas[atlasIndex].links) {
			const currentLinks = atlas[atlasIndex].links
			atlas[atlasIndex].links = {
				website: [],
				subreddit: [],
				discord: [],
				wiki: [],
				...currentLinks
			}
		} else {
			atlas[atlasIndex].links = {
				website: [],
				subreddit: [],
				discord: [],
				wiki: []
			}

			if (atlas[atlasIndex].website) atlas[atlasIndex].links.website = [atlas[atlasIndex].website]
			if (atlas[atlasIndex].subreddit) atlas[atlasIndex].links.subreddit = atlas[atlasIndex].subreddit.split(',').map(subreddit => subreddit.trim().replace(/^\/r\//, ''))

			delete atlas[atlasIndex].website
			delete atlas[atlasIndex].subreddit
		}
	}
	return atlas
}