import translate from "./translate-hints.js";
import update from "./fetch-updates.js";

// Global state for overview
var current_scene = "";
var current_seed = Number.MAX_VALUE;
var current_checks = 0;
var current_entrances = 0;
var current_codes = Number.MAX_VALUE;
var current_hints = 0;

// Global state internal
var server_address = "localhost:51111/";
var can_access_api_server = true;
var is_timeout = false;
var hide_completed_areas = false;
var cross_codes = {};
var total_checks = 0;
var total_entrances = 0;
var all_scenes = [];

window.onload = async () => {
	await get_updated_server_address();
	await parse_cross_codes();
	await initialize_elements();
	await refresh_elements();
};

async function parse_cross_codes() {
	fetch(`${document.URL}static/tracker/data/holy_cross_codes.json`)
		.then((response) => response.json())
		.then(
			(data) => {
				cross_codes = JSON.parse(JSON.stringify(data));
			},
			(error) => {
				console.error(error);
			}
		);
}

async function get_updated_server_address() {
	fetch(`${document.URL}get/address`)
		.then(
			(response) => response.json(),
			(error) => {
				console.error(error);
			}
		)
		.then(
			(data) => {
				const parsed_data = JSON.parse(JSON.stringify(data));
				server_address = parsed_data["listen_address"];
			},
			(error) => {
				console.error(error);
			}
		);
}

// A function to call when the page loads to ensure that we have the correct data when we connect to the server.
async function initialize_elements() {
	// Grab all updates from the backend.
	let overview = await update.refresh_overview(server_address);
	if (!overview.overview) {
		return;
	}
	const checks = await update.refresh_checks(server_address);
	const entrances = await update.refresh_entrances(server_address);
	const hints = await update.refresh_hints(server_address);
	overview = overview.overview;

	// Initialize global state
	current_checks = checks.collected;
	current_entrances = entrances.found;
	total_checks = checks.total;
	total_entrances = entrances.total;
	all_scenes = Array.from(Object.keys(checks.scenes));
	await update_overview(overview, false);

	// Initialize breakdown list
	let breakdown_list = document
		.getElementById("breakdown-list")
		.cloneNode(true);
	let summary_list = document.getElementById("summary-list").cloneNode(true);

	// Initialize the breakdown and summary lists
	breakdown_list = await initialize_breakdown_list(
		breakdown_list,
		checks,
		entrances
	);
	summary_list = await initialize_summary_list(summary_list, checks, entrances);

	// Replace breakdown list
	document
		.getElementById("breakdown-list")
		.replaceWith(breakdown_list.cloneNode(true));

	// Replace summary list
	document
		.getElementById("summary-list")
		.replaceWith(summary_list.cloneNode(true));

	// Update codes list
	update_codes(overview.codes);

	// Update hints list
	update_hints(hints);
	return;
}

async function refresh_elements() {
	const response = await update.refresh_overview(server_address);

	// Check if the "Hide completed areas" option is checked
	hide_completed_areas = document.getElementById("hideDone").checked;

	if (response.overview) {
		if (!can_access_api_server) {
			console.info("I found the server!");
			document.getElementById("status-block").classList.add("hidden");
			can_access_api_server = true;
			await initialize_elements();
		}
		is_timeout = false;
		await update_if_changes(response.overview);
	} else if (response.error.error) {
		if (!is_timeout) {
			is_timeout = true;
			console.debug("Received timeout from API server.");
		}
		if (!can_access_api_server) {
			console.info("I found the server!");
			document.getElementById("status-block").classList.add("hidden");
			can_access_api_server = true;
		}
	} else {
		if (can_access_api_server) {
			console.debug("Could not access the API server.");
			document.getElementById("status-block").classList.remove("hidden");
		}
		can_access_api_server = false;
	}
	setTimeout(refresh_elements, 500);
}

async function update_if_changes(overview) {
	const changed_seed = overview.seed != current_seed;
	const changed_scene = overview.scene != current_scene;
	const changed_checks = overview.items != current_checks;
	const changed_entrances = overview.entrances != current_entrances;
	const changed_hints = overview.hints != current_hints;
	const any_change =
		changed_seed ||
		changed_scene ||
		changed_checks ||
		changed_entrances ||
		changed_hints;

	if (any_change) {
		await perform_updates(
			changed_seed,
			changed_checks,
			changed_entrances,
			changed_hints
		);
		await update_overview(overview, changed_scene);
	}
	await update_codes(overview.codes);
}

async function perform_updates(
	changed_seed,
	changed_checks,
	changed_entrances,
	changed_hints
) {
	if (changed_seed) {
		initialize_elements();
		console.info(`Seed changed to: ${current_seed}`);
	} else {
		if (changed_checks) {
			update.refresh_checks(server_address).then((data) => {
				update_breakdown_list(data, { entrances: false, checks: true });
				update_summary_list(data, { entrances: false, checks: true });
			});
		}
		if (changed_entrances) {
			update.refresh_entrances(server_address).then((data) => {
				update_breakdown_list(data, { entrances: true, checks: false });
				update_summary_list(data, { entrances: true, checks: false });
			});
		}
		if (changed_hints) {
			update.refresh_hints(server_address).then((data) => update_hints(data));
		}
	}
}

async function update_overview(overview, changed_scene) {
	let overview_checks_title = document
		.getElementById("overview-totals")
		.querySelector(".overview-checks");
	let overview_entrances_title = document
		.getElementById("overview-totals")
		.querySelector(".overview-entrances");

	// Set content to updated data.
	overview_checks_title.textContent = `${overview.items}/${total_checks} (${
		total_checks - overview.items
	} left)`;
	overview_entrances_title.textContent = `${
		overview.entrances * 2
	}/${total_entrances} (${total_entrances - overview.entrances * 2} left)`;

	// Set global state
	current_seed = overview.seed;
	current_scene = overview.scene;
	current_checks = overview.items;
	current_entrances = overview.entrances;
	current_hints = overview.hints;

	if (changed_scene && all_scenes.some((scene) => scene == overview.scene)) {
		await update_scene(overview.scene);
	}
}

async function update_codes(codes) {
	// Codes that are always active
	const default_cross_codes = cross_codes.Default;

	let new_cross_codes_block_list = document
		.getElementById("codes-list")
		.cloneNode(true);
	let cross_codes_block_list_item = document
		.getElementById("codes-list")
		.firstElementChild.cloneNode(true);
	new_cross_codes_block_list.innerHTML = "";
	new_cross_codes_block_list.appendChild(
		cross_codes_block_list_item.cloneNode(true)
	);
	cross_codes_block_list_item.classList.remove("hidden");
	const codes_length = Array.from(Object.keys(codes)).length;

	if (current_codes != Array.from(Object.keys(codes)).length) {
		Object.keys(codes)
			.sort((i, j) => {
				return codes[i].Distance - codes[j].Distance;
			})
			.forEach((codename, index) => {
				cross_codes_block_list_item.querySelector(
					".codes-list-item-title"
				).textContent = codename;
				if (codes[codename].Global) {
					cross_codes_block_list_item.querySelector(
						".codes-list-item-code"
					).firstElementChild.alt = cross_codes["Global"][codename]
						.replace(/U/g, "⬆️")
						.replace(/R/g, "➡️")
						.replace(/D/g, "⬇️")
						.replace(/L/g, "⬅️");
					cross_codes_block_list_item.querySelector(
						".codes-list-item-code"
					).firstElementChild.src = `/static/tracker/images/cross_codes/Global/${codename}.png`;
				} else {
					cross_codes_block_list_item.querySelector(
						".codes-list-item-code"
					).firstElementChild.alt = cross_codes[current_scene][codename]
						.replace(/U/g, "⬆️")
						.replace(/R/g, "➡️")
						.replace(/D/g, "⬇️")
						.replace(/L/g, "⬅️");
					cross_codes_block_list_item.querySelector(
						".codes-list-item-code"
					).firstElementChild.src = `/static/tracker/images/cross_codes/${current_scene}/${codename}.png`;
				}
				cross_codes_block_list_item.dataset.codename = codename;
				cross_codes_block_list_item.dataset.order = index;
				new_cross_codes_block_list.appendChild(
					cross_codes_block_list_item.cloneNode(true)
				);
			});
		Object.keys(default_cross_codes).forEach((code) => {
			cross_codes_block_list_item.querySelector(
				".codes-list-item-title"
			).textContent = code;
			cross_codes_block_list_item.querySelector(
				".codes-list-item-code"
			).firstElementChild.alt = default_cross_codes[code]
				.replace(/U/g, "⬆️")
				.replace(/R/g, "➡️")
				.replace(/D/g, "⬇️")
				.replace(/L/g, "⬅️");
			cross_codes_block_list_item.querySelector(
				".codes-list-item-code"
			).firstElementChild.src = `/static/tracker/images/cross_codes/Default/${code}.png`;
			new_cross_codes_block_list.appendChild(
				cross_codes_block_list_item.cloneNode(true)
			);
		});
		document
			.getElementById("codes-list")
			.replaceWith(new_cross_codes_block_list);

		// Change the number of current codes if it's different
		current_codes = codes_length;
	} else {
		Object.keys(codes)
			.sort((i, j) => {
				return codes[i].Distance - codes[j].Distance;
			})
			.forEach((code, index) => {
				const classes = Array.from(
					document
						.getElementById("codes-list")
						.querySelector(`[data-codename="${code}"]`).classList
				).filter((classname) => classname.startsWith("order-"));
				if (classes.length > 0) {
					document
						.getElementById("codes-list")
						.querySelector(`[data-codename="${code}"]`)
						.classList.remove(classes);
				}
				if (codes[code].Global) {
					document
						.getElementById("codes-list")
						.querySelector(`[data-codename="${code}"]`)
						.classList.add(`order-last`);
				} else {
					document
						.getElementById("codes-list")
						.querySelector(`[data-codename="${code}"]`)
						.classList.add(`order-${index + 1}`);
				}
				if (codes[code].InRange) {
					document
						.getElementById("codes-list")
						.querySelector(`[data-codename="${code}"]`)
						.classList.add(`bg-[#ffe28558]`);
				} else {
					document
						.getElementById("codes-list")
						.querySelector(`[data-codename="${code}"]`)
						.classList.remove(`bg-[#ffe28558]`);
				}
				document
					.getElementById("codes-list")
					.querySelector(`[data-codename="${code}"]`).dataset.order = index + 1;
			});
	}
}

async function apply_summary_colors(summary) {
	// Apply color coding to summary block
	summary.element.classList.remove(
		"from-highlight-both-light",
		"from-highlight-checks-light",
		"from-highlight-entrances-light",
		"from-highlight-empty-light",
		"from-highlight-undiscovered-light",
		"to-highlight-both-dark",
		"to-highlight-checks-dark",
		"to-highlight-entrances-dark",
		"to-highlight-empty-dark",
		"to-highlight-undiscovered-dark",
		"text-highlight-both-text",
		"text-highlight-checks-text",
		"text-highlight-entrances-text",
		"text-highlight-empty-text",
		"text-highlight-undiscovered-text"
	);
	if (summary.entrances_total > 0) {
		if (summary.checks_total > 0) {
			if (summary.checks_remaining > 0 || summary.entrances_remaining > 0) {
				if (summary.checks_remaining <= 0 || summary.entrances_remaining <= 0) {
					if (summary.checks_remaining == 0) {
						summary.element.classList.add(
							"from-highlight-entrances-light",
							"to-highlight-entrances-dark",
							"text-highlight-entrances-text"
						);
					} else {
						summary.element.classList.add(
							"from-highlight-checks-light",
							"to-highlight-checks-dark",
							"text-highlight-checks-text"
						);
					}
				} else {
					if (summary.entrances_found <= 0) {
						summary.element.classList.add(
							"from-highlight-undiscovered-light",
							"to-highlight-undiscovered-dark",
							"text-highlight-undiscovered-text"
						);
					}
					summary.element.classList.add(
						"from-highlight-both-light",
						"to-highlight-both-dark",
						"text-highlight-both-text"
					);
				}
			} else {
				summary.element.classList.add(
					"from-highlight-empty-light",
					"to-highlight-empty-dark",
					"text-highlight-empty-text"
				);
			}
		} else {
			if (summary.entrances_remaining > 0) {
				if (summary.entrances_found <= 0) {
					summary.element.classList.add(
						"from-highlight-undiscovered-light",
						"to-highlight-undiscovered-dark",
						"text-highlight-undiscovered-text"
					);
				} else {
					summary.element.classList.add(
						"from-highlight-entrances-light",
						"to-highlight-entrances-dark",
						"text-highlight-entrances-text"
					);
				}
			} else {
				summary.element.classList.add(
					"from-highlight-empty-light",
					"to-highlight-empty-dark",
					"text-highlight-empty-text"
				);
			}
		}
	} else {
		if (summary.checks_total > 0 && summary.checks_remaining > 0) {
			summary.element.classList.add(
				"from-highlight-checks-light",
				"to-highlight-checks-dark",
				"text-highlight-checks-text"
			);
		} else {
			summary.element.classList.add(
				"from-highlight-empty-light",
				"to-highlight-empty-dark",
				"text-highlight-empty-text"
			);
		}
	}

	return summary.element;
}

async function initialize_breakdown_list(breakdown_list, checks, entrances) {
	// Initialize breakdown list
	let breakdown_list_item = breakdown_list.firstElementChild.cloneNode(true);
	breakdown_list.innerHTML = "";
	breakdown_list.appendChild(breakdown_list_item.cloneNode(true));
	breakdown_list_item.classList.remove("hidden");

	for (const scene of Object.keys(checks.scenes)) {
		// Initialize breakdown element
		breakdown_list_item = await initialize_breakdown(
			breakdown_list_item,
			scene,
			checks.scenes[scene],
			entrances.scenes[scene]
		);

		// Add breakdown element to breakdown list
		breakdown_list.appendChild(breakdown_list_item.cloneNode(true));
	}

	// Return initialized breakdown list
	return breakdown_list;
}

async function initialize_breakdown(
	breakdown_element,
	scene,
	checks,
	entrances
) {
	// Initialize breakdown data
	breakdown_element.dataset.scene = scene;
	breakdown_element.querySelector(".breakdown-block-title").textContent = scene;

	// Populate breakdown lists
	const breakdown_checks_list =
		breakdown_element.querySelector(".breakdown-checks");
	const breakdown_entrances_list = breakdown_element.querySelector(
		".breakdown-entrances"
	);
	const breakdown_mapped_list = breakdown_element.querySelector(
		".breakdown-block-mapped-list"
	);
	const new_breakdown_checks_list = await initialize_checks_list(
		breakdown_checks_list,
		checks
	);
	const new_entrances_lists = await initialize_entrances_list(
		breakdown_entrances_list,
		breakdown_mapped_list,
		entrances
	);

	// Replace checks, entrances, and mapped lists
	breakdown_element
		.querySelector(".breakdown-checks")
		.replaceWith(new_breakdown_checks_list);
	breakdown_element
		.querySelector(".breakdown-entrances")
		.replaceWith(new_entrances_lists.entrances_list);
	breakdown_element
		.querySelector(".breakdown-block-mapped-list")
		.replaceWith(new_entrances_lists.mapped_list);

	// Hide breakdowns that aren't for the current scene
	if (current_scene == scene) {
		breakdown_element.classList.add("order-first");
		breakdown_element.classList.remove("hidden", "order-last");
		breakdown_element.dataset.current = true;
	} else {
		breakdown_element.classList.add("hidden", "order-last");
		breakdown_element.classList.remove("order-first");
		breakdown_element.dataset.current = false;
	}

	// Return initialized breakdown
	return breakdown_element;
}

async function initialize_checks_list(checks_list, checks) {
	// Initialize new checks list for the breakdown
	let new_breakdown_block_checks_list = checks_list
		.querySelector(".breakdown-block-checks-list")
		.cloneNode(true);
	let breakdown_block_checks_list_item =
		new_breakdown_block_checks_list.firstElementChild.cloneNode(true);

	// Clear new checks list and append blank entry
	new_breakdown_block_checks_list.innerHTML = "";
	new_breakdown_block_checks_list.appendChild(
		breakdown_block_checks_list_item.cloneNode(true)
	);
	breakdown_block_checks_list_item.classList.remove("hidden");

	// Update the check totals
	checks_list.querySelector(
		".breakdown-block-checks-title"
	).textContent = `Checks: ${checks.collected}/${checks.total} (${checks.remaining} left)`;
	checks_list.querySelector(
		".breakdown-block-checks-title"
	).dataset.checksCollected = checks.collected;
	checks_list.querySelector(
		".breakdown-block-checks-title"
	).dataset.checksRemaining = checks.remaining;
	checks_list.querySelector(
		".breakdown-block-checks-title"
	).dataset.checksTotal = checks.total;

	// Populate new checks list
	Object.keys(checks.checks).forEach((check) => {
		if (checks.checks[check].name == "") {
			breakdown_block_checks_list_item.classList.remove("hidden");
		} else {
			breakdown_block_checks_list_item.classList.add("hidden");
		}
		breakdown_block_checks_list_item.textContent = `❌ ${check}`;
		breakdown_block_checks_list_item.dataset.check = check;
		new_breakdown_block_checks_list.appendChild(
			breakdown_block_checks_list_item.cloneNode(true)
		);
	});

	// Replace checks list
	checks_list
		.querySelector(".breakdown-block-checks-list")
		.replaceWith(new_breakdown_block_checks_list);

	return checks_list;
}

async function initialize_entrances_list(
	entrances_list,
	mapped_list,
	entrances
) {
	// Create new entrances and mapped lists and append empty entrance and mapped element
	let new_breakdown_block_entrances_list = entrances_list
		.querySelector(".breakdown-block-entrances-list")
		.cloneNode(true);
	let breakdown_block_entrances_list_item =
		new_breakdown_block_entrances_list.firstElementChild.cloneNode(true);
	new_breakdown_block_entrances_list.innerHTML = "";
	new_breakdown_block_entrances_list.appendChild(
		breakdown_block_entrances_list_item.cloneNode(true)
	);
	breakdown_block_entrances_list_item.classList.remove("hidden");

	let new_breakdown_block_mapped_list = mapped_list.cloneNode(true);
	let breakdown_block_mapped_list_item =
		new_breakdown_block_mapped_list.firstElementChild.cloneNode(true);
	new_breakdown_block_mapped_list.innerHTML = "";
	new_breakdown_block_mapped_list.appendChild(
		breakdown_block_mapped_list_item.cloneNode(true)
	);
	breakdown_block_mapped_list_item.classList.remove("hidden");

	// Update the entrance totals
	entrances_list.querySelector(
		".breakdown-block-entrances-title"
	).textContent = `Entrances: ${entrances.found}/${entrances.total} (${entrances.remaining} left)`;
	entrances_list.querySelector(
		".breakdown-block-entrances-title"
	).dataset.entrancesFound = entrances.found;
	entrances_list.querySelector(
		".breakdown-block-entrances-title"
	).dataset.entrancesRemaining = entrances.remaining;
	entrances_list.querySelector(
		".breakdown-block-entrances-title"
	).dataset.entrancesTotal = entrances.total;

	// Populate new entrances and mapped lists
	Object.keys(entrances.doors).forEach((entrance) => {
		breakdown_block_entrances_list_item.dataset.entrance = entrance;
		breakdown_block_entrances_list_item.textContent = `❌ ${entrance}`;
		breakdown_block_mapped_list_item.dataset.mapped = entrance;
		if (entrances.doors[entrance].door == "") {
			breakdown_block_entrances_list_item.classList.remove("hidden");
			new_breakdown_block_entrances_list.appendChild(
				breakdown_block_entrances_list_item.cloneNode(true)
			);
			breakdown_block_mapped_list_item.textContent = `✔️ ${entrance} -> `;
			breakdown_block_mapped_list_item.dataset.scene = "";
			breakdown_block_mapped_list_item.classList.add("hidden");
			new_breakdown_block_mapped_list.appendChild(
				breakdown_block_mapped_list_item.cloneNode(true)
			);
		} else {
			breakdown_block_entrances_list_item.classList.add("hidden");
			new_breakdown_block_entrances_list.appendChild(
				breakdown_block_entrances_list_item.cloneNode(true)
			);
			breakdown_block_mapped_list_item.textContent = `✔️ ${entrance} -> ${entrances.doors[entrance].door}`;
			breakdown_block_mapped_list_item.dataset.scene =
				entrances.doors[entrance].scene;
			breakdown_block_mapped_list_item.classList.remove("hidden");
			new_breakdown_block_mapped_list.appendChild(
				breakdown_block_mapped_list_item.cloneNode(true)
			);
		}
	});

	entrances_list
		.querySelector(".breakdown-block-entrances-list")
		.replaceWith(new_breakdown_block_entrances_list);

	// Return entrance and mapped list elements
	return {
		entrances_list: entrances_list,
		mapped_list: new_breakdown_block_mapped_list,
	};
}

async function initialize_summary_list(summary_list, checks, entrances) {
	// Initialize summary list
	let summary_list_item =
		summary_list.firstElementChild.firstElementChild.cloneNode(true);
	summary_list.firstElementChild.innerHTML = "";
	summary_list.firstElementChild.appendChild(summary_list_item.cloneNode(true));
	summary_list_item.classList.remove("hidden");

	for (const scene of Object.keys(checks.scenes)) {
		// Initialize summary element
		summary_list_item = await initialize_summary(
			summary_list_item,
			scene,
			checks.scenes[scene],
			entrances.scenes[scene]
		);

		// Add summary element to summary list
		summary_list.firstElementChild.appendChild(
			summary_list_item.cloneNode(true)
		);
	}

	// Return initialized summary list
	return summary_list;
}

async function initialize_summary(summary_element, scene, checks, entrances) {
	// Initialize summary data
	summary_element.dataset.scene = scene;
	summary_element.querySelector(".summary-title").textContent = scene;

	const summary_checks = summary_element.querySelector(".summary-checks");
	summary_checks.dataset.checksCollected = checks.collected;
	summary_checks.dataset.checksRemaining = checks.remaining;
	summary_checks.dataset.checksTotal = checks.total;
	summary_checks.textContent = `Checks: ${checks.collected}/${checks.total} (${checks.remaining})`;

	const summary_entrances = summary_element.querySelector(".summary-entrances");
	summary_entrances.dataset.entrancesFound = entrances.found;
	summary_entrances.dataset.entrancesRemaining = entrances.remaining;
	summary_entrances.dataset.entrancesTotal = entrances.total;
	summary_entrances.textContent = `Entrances: ${entrances.found}/${entrances.total} (${entrances.remaining})`;

	// Apply summary color coding
	summary_element.firstElementChild.replaceWith(
		await apply_summary_colors({
			element: summary_element.firstElementChild,
			checks_collected: checks.collected,
			checks_remaining: checks.remaining,
			checks_total: checks.total,
			entrances_found: entrances.found,
			entrances_remaining: entrances.remaining,
			entrances_total: entrances.total,
		})
	);

	// Hide summaries for scenes with no entrances and the current scene and completed areas if "Hide completed areas" is checked
	if ((entrances.total <= 0 && checks.total <= 0) || current_scene == scene) {
		summary_element.dataset.current = current_scene == scene;
		summary_element.classList.add("hidden");
	} else {
		if (hide_completed_areas) {
			if (checks.remaining > 0 && entrances.remaining > 0) {
				summary_element.classList.remove("hidden");
			} else {
				summary_element.classList.add("hidden");
			}
		} else {
			summary_element.dataset.current = false;
			summary_element.classList.remove("hidden");
		}
	}

	// Return initialized summary
	return summary_element;
}

async function update_summary(scene, checks, entrances) {
	// Grab summary checks and entrances
	const summary_checks = document.querySelector(
		`.summary[data-scene="${scene}"] .summary-checks`
	);
	const summary_entrances = document.querySelector(
		`.summary[data-scene="${scene}"] .summary-entrances`
	);

	// Make variables for whether checks or entrances updated
	const checks_changed =
		summary_checks.dataset.checksCollected != checks.collected;
	const entrances_changed =
		summary_entrances.dataset.entrancesFound != entrances.found;

	// Check for changes, and if so, update
	if (checks_changed) {
		summary_checks.dataset.checksCollected = checks.collected;
		summary_checks.dataset.checksRemaining = checks.remaining;
		summary_checks.dataset.checksTotal = checks.total;
		summary_checks.textContent = `Checks: ${checks.collected}/${checks.total} (${checks.remaining})`;
	}
	if (entrances_changed) {
		summary_entrances.dataset.entrancesFound = entrances.found;
		summary_entrances.dataset.entrancesRemaining = entrances.remaining;
		summary_entrances.dataset.entrancesTotal = entrances.total;
		summary_entrances.textContent = `Entrances: ${entrances.found}/${entrances.total} (${entrances.remaining})`;
	}

	// Apply color coding
	if (checks_changed || entrances_changed) {
		document
			.querySelector(`.summary[data-scene="${scene}"]`)
			.firstElementChild.replaceWith(
				await apply_summary_colors({
					element: document.querySelector(`.summary[data-scene="${scene}"]`)
						.firstElementChild,
					checks_collected: checks.collected,
					checks_remaining: checks.remaining,
					checks_total: checks.total,
					entrances_found: entrances.found,
					entrances_remaining: entrances.remaining,
					entrances_total: entrances.total,
				})
			);
	}
}

async function update_summary_list(data, changed) {
	// Check whether entrances or checks changed
	if (changed.checks && current_checks > 0) {
		Object.keys(data.scenes).forEach((scene) =>
			update_summary(scene, data.scenes[scene], {
				found: document.querySelector(
					`.summary[data-scene="${scene}"] .summary-entrances`
				).dataset.entrancesFound,
				remaining: document.querySelector(
					`.summary[data-scene="${scene}"] .summary-entrances`
				).dataset.entrancesRemaining,
				total: document.querySelector(
					`.summary[data-scene="${scene}"] .summary-entrances`
				).dataset.entrancesTotal,
			})
		);
	} else if (changed.entrances && current_entrances > 0) {
		Object.keys(data.scenes).forEach((scene) =>
			update_summary(
				scene,
				{
					collected: document.querySelector(
						`.summary[data-scene="${scene}"] .summary-checks`
					).dataset.checksCollected,
					remaining: document.querySelector(
						`.summary[data-scene="${scene}"] .summary-checks`
					).dataset.checksRemaining,
					total: document.querySelector(
						`.summary[data-scene="${scene}"] .summary-checks`
					).dataset.checksTotal,
				},
				data.scenes[scene]
			)
		);
	}
}

async function update_breakdown_checks(scene, checks) {
	// Update the check totals
	document.querySelector(
		`.breakdown[data-scene="${scene}"] .breakdown-block-checks-title`
	).textContent = `Checks: ${checks.collected}/${checks.total} (${checks.remaining} left)`;
	document.querySelector(
		`.breakdown[data-scene="${scene}"] .breakdown-block-checks-title`
	).dataset.checksCollected = checks.collected;
	document.querySelector(
		`.breakdown[data-scene="${scene}"] .breakdown-block-checks-title`
	).dataset.checksRemaining = checks.remaining;
	document.querySelector(
		`.breakdown[data-scene="${scene}"] .breakdown-block-checks-title`
	).dataset.checksTotal = checks.total;

	// Hide collected checks
	for (const check of Object.keys(checks.checks)) {
		const check_item = document.querySelector(
			`.breakdown[data-scene="${scene}"] [data-check="${check}"]`
		);
		if (checks.checks[check].name == "") {
			check_item.classList.remove("hidden");
		} else {
			check_item.classList.add("hidden");
		}
	}
}

async function update_breakdown_entrances(scene, entrances) {
	// Update the entrance totals
	document.querySelector(
		`.breakdown[data-scene="${scene}"] .breakdown-block-entrances-title`
	).textContent = `Entrances: ${entrances.found}/${entrances.total} (${entrances.remaining} left)`;
	document.querySelector(
		`.breakdown[data-scene="${scene}"] .breakdown-block-entrances-title`
	).dataset.entrancesFound = entrances.found;
	document.querySelector(
		`.breakdown[data-scene="${scene}"] .breakdown-block-entrances-title`
	).dataset.entrancesRemaining = entrances.remaining;
	document.querySelector(
		`.breakdown[data-scene="${scene}"] .breakdown-block-entrances-title`
	).dataset.entrancesTotal = entrances.total;

	// Map found entrances
	for (const entrance of Object.keys(entrances.doors)) {
		const entrance_item = document.querySelector(
			`.breakdown[data-scene="${scene}"] [data-entrance="${entrance}"]`
		);
		const mapped_item = document.querySelector(
			`.breakdown[data-scene="${scene}"] [data-mapped="${entrance}"]`
		);
		if (entrances.doors[entrance].door == "") {
			entrance_item.classList.remove("hidden");
			mapped_item.classList.add("hidden");
		} else {
			entrance_item.classList.add("hidden");
			mapped_item.classList.remove("hidden");
			mapped_item.dataset.scene = entrances.doors[entrance].scene;
			mapped_item.textContent = `✔️ ${entrance} -> ${entrances.doors[entrance].door}`;
		}
	}
}

async function update_breakdown(scene, checks, entrances) {
	// Grab breakdown checks and entrances
	const breakdown_checks = document.querySelector(
		`.breakdown[data-scene="${scene}"] .breakdown-block-checks-title`
	);
	const breakdown_entrances = document.querySelector(
		`.breakdown[data-scene="${scene}"] .breakdown-block-entrances-title`
	);

	// Check for changes, and if so, update
	if (breakdown_checks.dataset.checksCollected != checks.collected) {
		update_breakdown_checks(scene, checks);
	}
	if (breakdown_entrances.dataset.entrancesFound != entrances.found) {
		update_breakdown_entrances(scene, entrances);
	}
}

async function update_breakdown_list(data, changed) {
	// Check whether entrances or checks changed
	if (changed.checks) {
		Object.keys(data.scenes).forEach((scene) =>
			update_breakdown(scene, data.scenes[scene], {
				found: document.querySelector(
					`.breakdown[data-scene="${scene}"] .breakdown-block-entrances-title`
				).dataset.entrancesFound,
			})
		);
	} else if (changed.entrances) {
		Object.keys(data.scenes).forEach((scene) =>
			update_breakdown(
				scene,
				{
					collected: document.querySelector(
						`.breakdown[data-scene="${scene}"] .breakdown-block-checks-title`
					).dataset.checksCollected,
				},
				data.scenes[scene]
			)
		);
	}
}

async function update_hints(hints) {
	let hints_list = document.getElementById("hints-list").cloneNode(true);
	let hints_list_item = hints_list.firstElementChild.cloneNode(true);
	hints_list.innerHTML = "";
	hints_list.appendChild(hints_list_item.cloneNode(true));
	hints_list_item.classList.remove("hidden");
	for (const hint_index of Object.keys(hints)) {
		hints[hint_index] = await translate.parse_hints(hints[hint_index]);
		hints_list_item.firstElementChild.innerHTML = hints[hint_index];
		hints_list.appendChild(hints_list_item.cloneNode(true));
	}
	document.getElementById("hints-list").replaceWith(hints_list);
}

async function update_scene(scene) {
	// Grab current scene and updated scene breakdown/summary elements
	const current_breakdown_old = document.querySelector(
		`.breakdown[data-current="true"]`
	);
	const current_breakdown = document.querySelector(
		`.breakdown[data-scene="${scene}"]`
	);
	const current_summary_old = document.querySelector(
		`.summary[data-current="true"]`
	);
	const current_summary = document.querySelector(
		`.summary[data-scene="${scene}"]`
	);

	// Update the current breakdown
	current_breakdown_old.dataset.current = false;
	current_breakdown_old.classList.add("hidden", "order-last");
	current_breakdown_old.classList.remove("order-first");
	current_breakdown.dataset.current = true;
	current_breakdown.classList.add("order-first");
	current_breakdown.classList.remove("hidden", "order-last");
	const old_checks_remaining = parseInt(
		current_summary_old.querySelector(".summary-checks").dataset.checksRemaining
	);
	const old_entrances_remaining = parseInt(
		current_summary_old.querySelector(".summary-entrances").dataset
			.entrancesRemaining
	);
	const old_checks_total = parseInt(
		current_summary_old.querySelector(".summary-checks").dataset.checksTotal
	);
	const old_entrances_total = parseInt(
		current_summary_old.querySelector(".summary-entrances").dataset
			.entrancesTotal
	);

	// Update the current summary
	current_summary_old.dataset.current = false;
	if (hide_completed_areas) {
		if (old_checks_remaining > 0 && old_entrances_remaining > 0) {
			current_summary_old.classList.remove("hidden");
		} else {
			current_summary_old.classList.add("hidden");
			if (old_entrances_total <= 0 && old_checks_total <= 0) {
				current_summary_old.classList.add("hidden");
			} else {
				current_summary_old.classList.remove("hidden");
			}
		}
	} else {
		if (old_entrances_total <= 0 && old_checks_total <= 0) {
			current_summary_old.classList.add("hidden");
		} else {
			current_summary_old.classList.remove("hidden");
		}
	}
	current_summary.dataset.current = true;
	current_summary.classList.add("hidden");
}
