import { workerData, parentPort } from "worker_threads";
import requireFromString from "require-from-string";
import { error } from "../log.js";

const CONFIG = workerData.CONFIG;

// Import all the tags
const TAG_MODULES = [];
for (const TAG of workerData.tags) {
    TAG_MODULES.push(requireFromString(TAG));
}

type ConverStatus = {
    ignore_next_char_command: boolean
}

type TagConverter = {
    from: string,
    to(...paramaters: string[]): string
}

function find_tag_converter(tag_name: string, TAG_SETS: TagConverter[][]): TagConverter | undefined {
    for (const TAG_SET of TAG_SETS) {
        for (const TAG of TAG_SET) {
            if (TAG.from == tag_name) return TAG;
        }
    }
    error("run/convert", `Could not find tag named '${tag_name}'`);
    return undefined;
}

function seperate_arguments(argument_str: string, seperator: string): string[] {
    let nest_level = 1;
    const PARAMATERS: string[] = [""];
    for (const CHAR of argument_str) {
        if (CHAR == "(") {
            nest_level++;
        } else if (CHAR == ")") {
            nest_level--;
        }
        if (CHAR == seperator && nest_level == 1) {
            PARAMATERS.push("");
            continue;
        }
        PARAMATERS[PARAMATERS.length - 1] += CHAR;
    }
    return PARAMATERS.map(paramater => paramater.trim());
}

function find_arguments_string(str: string, start: number): string {
    if (str[start] != "(") return "";
    let nest_level = 1;
    let scan_position = start + 1;
    let content = "(";
    let is_inside_string = false;
    while (scan_position < str.length && nest_level > 0) {
        if (str[scan_position] == "\"") {
            is_inside_string = !is_inside_string;
        }

        if (!is_inside_string) {
            if (str[scan_position] == "(") {
                nest_level++;
            } else if (str[scan_position] == ")") {
                nest_level--;
            }
        }

        content += str[scan_position];

        scan_position++;
    }
    return content;
}

let OUTPUT = "";
const STATUS: ConverStatus = {
    ignore_next_char_command: false
};
const CONTENT = workerData.content;
for (let char_index = 0; char_index < CONTENT.length; char_index++) {
    if (STATUS.ignore_next_char_command) {
        STATUS.ignore_next_char_command = false;
        OUTPUT += CONTENT[char_index];
        continue;
    }

    if (CONTENT[char_index] == "\\") {
        STATUS.ignore_next_char_command = true;
        continue;
    }

    if (workerData.CONFIG.use.tags && CONTENT[char_index] == "@") {
        const TAG_START = CONTENT
            .slice(char_index, CONTENT.length)
            .match(/^@[A-Za-z0-9\-_$.]*/)[0];

        const TAG_NAME = TAG_START.slice(1, TAG_START.length);

        const TAG_ARGUMENTS_STRING = find_arguments_string(CONTENT, char_index + TAG_START.length);
        const TAG_ARGUMENTS = seperate_arguments(TAG_ARGUMENTS_STRING.slice(1, -1), ",");

        OUTPUT += find_tag_converter(TAG_NAME, TAG_MODULES)?.to(...TAG_ARGUMENTS);
        char_index += TAG_START.length + TAG_ARGUMENTS_STRING.length - 1;
        continue;
    }

    if (workerData.CONFIG.use.inline && CONTENT.slice(char_index, char_index + CONFIG.inline.from.start.length) == CONFIG.inline.from.start) {
        let content = "";
        let nest_level = 1;
        let index = char_index + CONFIG.inline.from.start.length;
        while (index < CONTENT.length && !(nest_level > 0 && CONTENT.slice(index, index + CONFIG.inline.from.end.length) == CONFIG.inline.from.end)) {
            content += CONTENT[index];
            if (CONTENT[index] == "{" || CONTENT[index] == "(") {
                nest_level++;
            }
            if (CONTENT[index] == "}" || CONTENT[index] == ")") {
                nest_level--;
            }
            index++;
        }
        OUTPUT += CONFIG.inline.to.start + content + CONFIG.inline.to.end;
        char_index += CONFIG.inline.from.start.length + content.length + CONFIG.inline.from.end.length - 1;
        continue;
    }

    OUTPUT += CONTENT[char_index];
}

parentPort?.postMessage(OUTPUT);