import { parentPort } from 'node:worker_threads';

import fs from 'node:fs';
import md5 from "./src/misc/md5.js";
import path from "path";

import defaultProcess from "./src/defaultProcess.js";
import npcInit from "./src/npcInit.js";
import skillDefinitions from "./src/skillDefinitions.js";
import wardrobeItems from "./src/wardrobeItems.js";



//https://stackoverflow.com/a/25221100/7200161
function baseFileName(fullpath){
    return fullpath.split('\\').pop().split('/').pop();
}

function convertFile(task){
	const VERSION = task.VERSION;
	const filePath = task.filePath;
	const outPath = task.outPath;
	const generatedFilesPrefix = task.generatedFilesPrefix;
	const options = task.options;

	try{
		const data = fs.readFileSync(filePath,{encoding: 'utf8'});
		const startTime = (new Date()).getTime();

		const baseFileNameStr = baseFileName(filePath);
		const outFilePath = path.join(outPath,generatedFilesPrefix,baseFileNameStr.split('.')[0]+'.tw');
		const outFilePathTS = path.join(outPath,generatedFilesPrefix,baseFileNameStr.split('.')[0]+'.ts');

		const qsp2twOptions = data.split('\n')?.[1];
		if(qsp2twOptions.startsWith("!! SKIP_QSRC2TW")){
			if(fs.existsSync(outFilePath))
				fs.unlink(outFilePath,(err) => {if (err) throw err;});
			if(fs.existsSync(outFilePathTS))
				fs.unlink(outFilePathTS,(err) => {if (err) throw err;});
			return [0,"SKIP"];
		}



		//#region Skip File Output if outfile exists, is based on the same codebase (determined by hash) and has used the same compier-version
			const codeHash = md5(data);
			if (fs.existsSync(outFilePath)) {

				try{
					const secondLineData = fs.readFileSync(outFilePath, "utf-8").split('\n')[1];
					const qsrc2twResultMatch = secondLineData.match(/<!--\s*qsrc2twResult=({.*})\s*-->/);
					if(qsrc2twResultMatch){
						const qsrc2twResult = JSON.parse(qsrc2twResultMatch[1]);

						if((qsrc2twResult.code && codeHash == qsrc2twResult.code) &&
							(qsrc2twResult.version && VERSION == qsrc2twResult.version)){
								return [0,"EXISTS"];
						}
					}
				}
				catch(e){
					//Something unexpected happens. No need to handle this, because we'll just run the default file-processing.
				}
			}
		//#endregion

		var convertMode = "default";
		var convertModeOptions = {};
		/*if(qsp2twOptions.startsWith("!! QSRC2TW_module")){
			convertMode = qsp2twOptions.trim().split(" ").toReversed()[0];
		}*/
		const modeLookupRegex = /\s*!!\s*QSRC2TW_module\s+(\w+)(\s+{[^}]+})?/;
		let modeLookupResult;
		try{
			if(modeLookupResult = modeLookupRegex.exec(qsp2twOptions)){
				convertMode = modeLookupResult[1];
				if(modeLookupResult[2])
					convertModeOptions = JSON.parse(modeLookupResult[2]);
			}
		}
		catch(e){
			console.log(e.toString());
		}

		/**
		 * Return value is Array [TwineCode, TSCode]. TwineCode must not be null.
		 */
		var convertFunction = (code,options)=>[null,null];
		switch (convertMode) {
			case "default": convertFunction = (code,options) => [defaultProcess(code),null]; break;
			case "npcInit": convertFunction = (code,options) => npcInit(code); break;
			case "stat_sklattrib_lvlset": convertFunction = (code,options) => skillDefinitions(code); break;
			case "wardrobeItems": convertFunction = (code,options) => wardrobeItems(code,options); break;
			default:
				console.warn("Unreckognized Convert Mode");
				break;
		}


		try{
			let twineCode = "";
			let twineCodeRaw = undefined;
			let tsCodeRaw = undefined;

			const defaultConsole = console;
			if(!options.verboseErrors)
				console = {log:(...args)=>{}, debug: (...args)=>{},warn:(...args)=>{}, error: (...args)=>{}};
			try{
				[twineCodeRaw,tsCodeRaw] = convertFunction(data,convertModeOptions);
			}
			catch(e){
				throw e;
			}
			finally{
				console = defaultConsole;
			}
			if(!twineCodeRaw){
				console.error("Twine Code must be generated by every convertMode");
				return [2,"Invalid convertFunction"];
			}else{
				twineCode = twineCodeRaw.split('\n')
								.toSpliced(1,0,`<!--qsrc2twResult={"version":${VERSION},"code":"${codeHash}","time":"${(new Date().toISOString())}"}-->`)
								.join('\n');
				fs.writeFile(outFilePath, twineCode, err => {
					if (err) {
						console.error(err);
					} else {
					}
				});
			}


			if(!tsCodeRaw){
				if(fs.existsSync(outFilePathTS))
					fs.unlink(outFilePathTS,(err) => {if (err) throw err;});
			}else{
				fs.writeFile(outFilePathTS, tsCodeRaw, err => {
					if (err) {
						console.error(err);
					} else {
					}
				});
			}

			const executionTime =  (new Date()).getTime() - startTime;

			console.log(`${baseFileNameStr.padEnd(30,'.')} ${executionTime} ms`);

			return [0,"SUCCESS"];
		}catch(e){
			//console.error(`Error in "${baseFileNameStr}". No output was generated`);
			return [1,`Error in "${baseFileNameStr}". No output was generated`,e];
		}
	}
	catch(e){
		//console.error();
		return [1,`Critical Error in "${filePath}". No output was generated`, e];
	}
}

parentPort.on('message', (task) => {
    parentPort.postMessage(convertFile(task));
});
