import {
	getArticleName,
} from "qrc:/js/lib/graph_utils";
import {
	AssemblyMapEntry,
} from "qrc:/js/lib/interfaces";
import {
	createExternalJoining,
	createInternalJoining,
	InternalJoining,
} from "qrc:/js/lib/joining_utils";
import {
	cleanRelativeDirPath,
	createAssemblyMap,
	createBrepMapFromAssembly,
} from "qrc:/js/lib/utils";

import {
	writeFile,
} from "./export_fs_util";

declare interface Resource {
	dir: string;
	fileName: string;
}

class Translation {
	key: string;
	translation: string;

	constructor(key: string, translation: string) {
		this.key = key;
		this.translation = translation;
	}
}

function createAssemblyMapFromJoining(internalJoining: InternalJoining): Array<AssemblyMapEntry> {
	const recursiveAddToArray = (array: Array<AssemblyMapEntry>, joining: InternalJoining, parentCosys?: CoordinateSystem3) => {
		for (const step of joining.joiningSteps) {
			for (const entry of step.entries) {
				const assembly = entry.assembly;
				const assemblyMap = createAssemblyMap(assembly, parentCosys);
				for (const candidate of assemblyMap) {
					if (array.find(element => element.id === candidate.id) !== undefined) {
						continue;
					}
					array.push(candidate);
				}
				if (entry.subJoining === undefined) {
					continue;
				}
				const cosys = (() => {
					const childCosys = wsi4.geo.assembly.worldCoordinateSystem(assembly);
					if (parentCosys === undefined) {
						return childCosys;
					} else {
						return wsi4.geo.util.applyCoordinateSystem(parentCosys, childCosys);
					}
				})();
				recursiveAddToArray(array, entry.subJoining, cosys);
			}
		}
	};

	const result: Array<AssemblyMapEntry> = [];
	recursiveAddToArray(result, internalJoining);
	return result;
}

function createTranslationsArray() {
	return [
		new Translation("previousStep", wsi4.util.translate("PreviousStep")),
		new Translation("nextStep", wsi4.util.translate("NextStep")),
		new Translation("enterSubJoining", wsi4.util.translate("EnterSubJoining")),
		new Translation("leaveSubJoining", wsi4.util.translate("EnterSubJoining")),
		new Translation("jumpToStart", wsi4.util.translate("JumpToStart")),
		new Translation("jumpToEnd", wsi4.util.translate("JumpToEnd")),
		new Translation("toggleStepList", wsi4.util.translate("ToggleStepList")),
		new Translation("joiningNaviHeader", wsi4.util.translate("JoiningNaviHeader")),
		new Translation("modelMovementHeader", wsi4.util.translate("ModelMovementHeader")),
		new Translation("settingsHeader", wsi4.util.translate("SettingsHeader")),
		new Translation("aboutHeader", wsi4.util.translate("AboutHeader")),
		new Translation("createdWithWsi4", wsi4.util.translate("CreatedWithWsi4")),
		new Translation("autoAdjustView", wsi4.util.translate("AutoAdjustView")),
		new Translation("autoNavigation", wsi4.util.translate("AutoNavigation")),
	];
}

function writeResourceFile(cleanDir: string, resource: Resource): boolean {
	const sourcePath = resource.dir + "/" + resource.fileName;
	const fileContent = wsi4.io.fs.readFile(sourcePath);
	if (!fileContent) {
		wsi4.util.error("exportJoiningView(): cannot read resource file \"" + sourcePath + "\"");
		return false;
	}

	const targetPath = cleanDir + "/" + resource.fileName;
	if (!wsi4.io.fs.writeFile(targetPath, fileContent)) {
		wsi4.util.error("exportJoiningView(): cannot write resource file \"" + targetPath + "\"");
		return false;
	}
	return true;
}

function writeResourceFiles(cleanDir: string, resources: Array<Resource>): boolean {
	for (const resource of resources) {
		if (!writeResourceFile(cleanDir, resource)) {
			return false;
		}
	}
	return true;
}

// Resources required for assembly- / joining-view
const assemblyViewJsResources: Array<Resource> = [
	{dir: ":/js/thirdparty",
		fileName: "tween.umd.js"},
	{dir: ":/js/thirdparty",
		fileName: "three.min.js"},
	{dir: ":/js/thirdparty",
		fileName: "jquery.min.js"},
	{dir: ":/js/thirdparty",
		fileName: "polyfill.min.js"},
	{dir: ":/js",
		fileName: "resources_npm.js"},
];
const joiningViewJsResources: Array<Resource> = [
	{dir: ":/js/thirdparty",
		fileName: "popper.min.js"},
	{dir: ":/js/thirdparty",
		fileName: "tooltip.min.js"},
];
const joiningViewCssResources: Array<Resource> = [
	{dir: ":/css",
		fileName: "joiningview.css"},
	{dir: ":/css",
		fileName: "popper.css"},
];
const joiningViewSvgResources: Array<Resource> = [
	{dir: ":/logo",
		fileName: "wsoptics_no_r.svg"},
	{dir: ":/svg",
		fileName: "joiningview_mouse_help.svg"},
	{dir: ":/svg",
		fileName: "joiningview_touch_help.svg"},
];
const joiningViewFontResources: Array<Resource> = [
	{dir: ":/fonts",
		fileName: "robotoRegular.ttf"},
	{dir: ":/fonts",
		fileName: "glyphicons2.otf"},
];

// Write breps, assemblies and joining to file system
function writeBrepJsFile(vertex: Vertex, cleanDirPath: string): string|undefined {
	const fileName = "breps";
	const extension = "js";
	const assembly = wsi4.node.assembly(vertex);
	if (!assembly) {
		wsi4.util.error("writeBrepJsFile(): No assembly");
		return undefined;
	}
	const brepMap = createBrepMapFromAssembly(assembly);
	const fileContent = "var globalBrepMap = " + JSON.stringify(brepMap) + ";";
	if (!writeFile(cleanDirPath, fileName, extension, wsi4.util.stringToArrayBuffer(fileContent))) {
		return undefined;
	}
	return fileName + "." + extension;
}

function writeTranslationsJsFile(cleanDirPath: string): string|undefined {
	const fileName = "translations";
	const extension = "js";
	const translationsMap = createTranslationsArray();
	const fileContent = "var globalTranslations = " + JSON.stringify(translationsMap) + ";";
	if (!writeFile(cleanDirPath, fileName, extension, wsi4.util.stringToArrayBuffer(fileContent))) {
		return undefined;
	}
	return fileName + "." + extension;
}

function writeAssembliesJsFile(cleanDirPath: string, internalJoining: InternalJoining): string|undefined {
	const fileName = "assemblies";
	const extension = "js";
	const assemblyMap = createAssemblyMapFromJoining(internalJoining);
	const fileContent = "var globalAssemblyMap = " + JSON.stringify(assemblyMap) + ";";
	if (!writeFile(cleanDirPath, fileName, extension, wsi4.util.stringToArrayBuffer(fileContent))) {
		return undefined;
	}
	return fileName + "." + extension;
}

function writeJoiningJsFile(cleanDirPath: string, internalJoining: InternalJoining): string|undefined {
	const fileName = "joining";
	const extension = "js";
	const fileContent = "var globalJoining = " + JSON.stringify(createExternalJoining(internalJoining)) + ";";
	if (!writeFile(cleanDirPath, fileName, extension, wsi4.util.stringToArrayBuffer(fileContent))) {
		return undefined;
	}
	return fileName + "." + extension;
}

function includeStyleSheet(prefix: string, fileName: string, resourceRelDirPath: string): string {
	return prefix + "<link rel=\"stylesheet\" href=\"" + resourceRelDirPath + "/" + fileName + "\"/>";
}

function includeScript(prefix: string, fileName: string, resourceRelDirPath: string): string {
	return prefix + "<script src=\"" + resourceRelDirPath + "/" + fileName + "\"></script>";
}

function writeJoiningHtmlFile(
	vertex: Vertex, cleanDirPath: string, brepJsFileName: string, assemblyJsFileName: string, joiningJsFileName: string, translationsJsFileName: string, resourceRelDirPath: string): string|
	undefined {
	let html = "<!DOCTYPE html>";
	html += "\n<html>";
	html += "\n<head>";
	html += "\n	<title>WSoptics - WSi4</title>";
	html += "\n	<meta charset=\"UTF-8\"/>";
	html += "\n	<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0\"/>";

	// Add resources to script and write them to the resource directory
	for (const resource of joiningViewCssResources) {
		html += includeStyleSheet("\n\t\t", resource.fileName, resourceRelDirPath);
	}
	for (const resource of assemblyViewJsResources) {
		html += includeScript("\n\t\t", resource.fileName, resourceRelDirPath);
	}
	for (const resource of joiningViewJsResources) {
		html += includeScript("\n\t\t", resource.fileName, resourceRelDirPath);
	}

	html += includeScript("\n\t\t", brepJsFileName, resourceRelDirPath);
	html += includeScript("\n\t\t", assemblyJsFileName, resourceRelDirPath);
	html += includeScript("\n\t\t", joiningJsFileName, resourceRelDirPath);
	html += includeScript("\n\t\t", translationsJsFileName, resourceRelDirPath);

	// Required by resources (e. g. svg images); should be usable with '+' operator in javascript, so it is either empty or with trailing '/'
	html += "\n\t\t<script>var globalResourcesDir = \"" + resourceRelDirPath + "\";</script>\n";

	html += "\n</head>";
	html += "\n<body>";
	html += "\n        <div id=\"maindiv\">";
	html += "\n                <div id=\"joiningViewContainer\">";
	html += "\n                </div>";
	html += "\n                <div id=\"menuButtonContainer\">";
	html += "\n                        <button id=\"menuButton\" class=\"glyphicon transparentButton\"></button>";
	html += "\n                </div>";
	html += "\n                <div id=\"controlButtonsContainer\">";
	html += "\n                        <div></div>";
	html += "\n                        <div>";
	html += "\n                                <button id=\"prevStepButton\" class=\"glyphicon transparentButton\"></button>";
	html += "\n                        </div>";
	html += "\n                        <div></div>";
	html += "\n                        <div>";
	html += "\n                                <button id=\"leaveSubJoiningButton\" class=\"glyphicon transparentButton\"></button>";
	html += "\n                        </div>";
	html += "\n                        <div>";
	html += "\n                                <button id=\"toggleStepListButton\" class=\"glyphicon transparentButton\"></button>";
	html += "\n                        </div>";
	html += "\n                        <div>";
	html += "\n                                <button id=\"enterSubJoiningButton\" class=\"glyphicon transparentButton\"></button>";
	html += "\n                        </div>";
	html += "\n                        <div></div>";
	html += "\n                        <div>";
	html += "\n                                <button id=\"nextStepButton\" class=\"glyphicon transparentButton\"></button>";
	html += "\n                        </div>";
	html += "\n                        <div></div>";
	html += "\n                </div>";
	html += "\n        </div>";
	html += "\n        <div id=\"dialogOverlay\" tabindex=\"-1\"></div>";
	html += "\n        <div id=\"menuDialog\" class=\"dialog\" role=\"dialog\" title=\"Menu\">";
	html += "\n                <p>";
	html += "\n                        <h2 id=\"joiningNavigationHeader\"></h2>";
	html += "\n                        <table style=\"border: 0px;\">";
	html += "\n                                <tr>";
	html += "\n                                        <td><span id=\"prevStepHelpIcon\" class=\"glyphicon\"></span></td>";
	html += "\n                                        <td><span id=\"prevStepHelpText\"></span></td>";
	html += "\n                                </tr>";
	html += "\n                                <tr>";
	html += "\n                                        <td><span id=\"nextStepHelpIcon\" class=\"glyphicon\"></span></td>";
	html += "\n                                        <td><span id=\"nextStepHelpText\"></span></td>";
	html += "\n                                </tr>";
	html += "\n                                <tr>";
	html += "\n                                        <td><span id=\"jumpToStartHelpIcon\" class=\"glyphicon\"></span></td>";
	html += "\n                                        <td><span id=\"jumpToStartHelpText\"></span></td>";
	html += "\n                                </tr>";
	html += "\n                                <tr>";
	html += "\n                                        <td><span id=\"jumpToEndHelpIcon\" class=\"glyphicon\"></span></td>";
	html += "\n                                        <td><span id=\"jumpToEndHelpText\"></span></td>";
	html += "\n                                </tr>";
	html += "\n                                <tr>";
	html += "\n                                        <td><span id=\"enterSubJoiningHelpIcon\" class=\"glyphicon\"></span></td>";
	html += "\n                                        <td><span id=\"enterSubJoiningHelpText\"></span></td>";
	html += "\n                                </tr>";
	html += "\n                                <tr>";
	html += "\n                                        <td><span id=\"leaveSubJoiningHelpIcon\" class=\"glyphicon\"></span></td>";
	html += "\n                                        <td><span id=\"leaveSubJoiningHelpText\"></span></td>";
	html += "\n                                </tr>";
	html += "\n                                <tr>";
	html += "\n                                        <td><span id=\"toggleStepListHelpIcon\" class=\"glyphicon\"></span></td>";
	html += "\n                                        <td><span id=\"toggleStepListHelpText\"></span></td>";
	html += "\n                                </tr>";
	html += "\n                        </table>";
	html += "\n                </p>";
	html += "\n                <p>";
	html += "\n                        <h2 id=\"modelMovementHeader\"></h2>";
	html += "\n                        <image id=\"moveHelpImage\" alt=\"Move help\" style=\"width : 75%; height : auto;\"></image>";
	html += "\n                </p>";
	html += "\n                <p>";
	html += "\n                        <h2 id=\"settingsHeader\"></h2>";
	html += "\n                        <div id=\"settingsContent\"></div>";
	html += "\n                </p>";
	html += "\n                <p>";
	html += "\n                        <h2 id=\"aboutHeader\"></h2>";
	html += "\n                        <p>";
	html += "\n                                <a id=\"joiningSource\" target=\"_blank\" href=\"https://wsoptics.de/de/wsi4\"></a>";
	html += "\n                        </p>";
	html += "\n                </p>";
	html += "\n        </div>";
	html += "\n        <script>";
	html += "\n		   // Check browser support";
	html += "\n 		   if (!THREE.WEBGL.isWebGLAvailable()) {";
	html += "\n 		           var warning = THREE.WEBGL.getWebGLErrorMessage();";
	html += "\n 		           document.getElementById('dialogOverlay').appendChild(warning);";
	html += "\n 		   } else {";
	html += "\n                	// Setup joining view";
	html += "\n                	var assemblyView = new AssemblyView(document.getElementById(\"joiningViewContainer\"), globalAssemblyMap, globalBrepMap);";
	html += "\n                	var joiningView = new JoiningView(globalJoining, globalTranslations);";
	html += "\n";
	html += "\n                	// Setup menu dialog";
	html += "\n                	var dialogElement = document.getElementById(\"menuDialog\");";
	html += "\n                	var overlayElement = document.getElementById(\"dialogOverlay\");";
	html += "\n                	var menuDialog = new Dialog(dialogElement, overlayElement);";
	html += "\n                	menuDialog.setupEventListeners(\"menuButton\");";
	html += "\n                	dialogElement.addEventListener(\"focusout\", function(e) {";
	html += "\n                	        joiningView.updateUiElements();";
	html += "\n                	});";
	html += "\n";
	html += "\n                	// Animation loop";
	html += "\n                	var animate = function(time) {";
	html += "\n                	        requestAnimationFrame(animate);";
	html += "\n                	        assemblyView.animate();";
	html += "\n                	        TWEEN.update(time);";
	html += "\n                	}";
	html += "\n                	animate();";
	html += "\n";
	html += "\n                	document.addEventListener(\"gesturestart\", function (e) {";
	html += "\n                	        e.preventDefault();";
	html += "\n                	});";
	html += "\n                }";
	html += "\n        </script>";
	html += "\n</body>";
	html += "\n</html>";

	const fileName = getArticleName(vertex);
	const extension = "html";
	return writeFile(cleanDirPath, fileName, extension, wsi4.util.stringToArrayBuffer(html));
}

export function exportJoiningView(cleanDirPath: string, vertex: Vertex): string|undefined {
	// Directory the resources will be written to
	const resourceDirRelPath = cleanRelativeDirPath("resources", "_");
	const resourceDirAbsPath = cleanDirPath + "/" + resourceDirRelPath;
	if (!wsi4.io.fs.mkpath(resourceDirAbsPath)) {
		wsi4.util.error("exportJoiningView(): Cannot create directory " + resourceDirAbsPath);
		return undefined;
	}

	// Write resources to file system
	if (!writeResourceFiles(resourceDirAbsPath, assemblyViewJsResources)) {
		return undefined;
	}
	if (!writeResourceFiles(resourceDirAbsPath, joiningViewJsResources)) {
		return undefined;
	}
	if (!writeResourceFiles(resourceDirAbsPath, joiningViewCssResources)) {
		return undefined;
	}
	if (!writeResourceFiles(resourceDirAbsPath, joiningViewSvgResources)) {
		return undefined;
	}
	if (!writeResourceFiles(resourceDirAbsPath, joiningViewFontResources)) {
		return undefined;
	}

	const brepsFileName = writeBrepJsFile(vertex, resourceDirAbsPath);
	if (!brepsFileName) {
		return undefined;
	}

	const internalJoining = createInternalJoining(vertex);
	if (!internalJoining) {
		return undefined;
	}
	const assembliesFileName = writeAssembliesJsFile(resourceDirAbsPath, internalJoining);
	if (!assembliesFileName) {
		return undefined;
	}

	const joiningFileName = writeJoiningJsFile(resourceDirAbsPath, internalJoining);
	if (!joiningFileName) {
		return undefined;
	}

	const translationsFileName = writeTranslationsJsFile(resourceDirAbsPath);
	if (!translationsFileName) {
		return undefined;
	}

	return writeJoiningHtmlFile(vertex, cleanDirPath, brepsFileName, assembliesFileName, joiningFileName, translationsFileName, resourceDirRelPath);
}
