# Libraries
import json
import os
import re
import util.dazedwrap as dazedwrap
import threading
import time
import traceback
import tiktoken
import openai
import copy
# Removed concurrent.futures usage for simplicity; running synchronously
from pathlib import Path
from colorama import Fore
from dotenv import load_dotenv
from retry import retry
from tqdm import tqdm
from util.translation import TranslationConfig, translateAI as sharedtranslateAI, getPricingConfig, calculateCost
from ruamel.yaml import YAML


# Open AI
load_dotenv()
if os.getenv("api").replace(" ", "") != "":
    openai.base_url = os.getenv("api")
openai.organization = os.getenv("org")
openai.api_key = os.getenv("key")

# Globals
MODEL = os.getenv("model")
TIMEOUT = int(os.getenv("timeout"))
LANGUAGE = os.getenv("language").capitalize()
PROMPT = Path("prompt.txt").read_text(encoding="utf-8")
VOCAB = Path("vocab.txt").read_text(encoding="utf-8")
THREADS = int(os.getenv("threads"))
LOCK = threading.Lock()
WIDTH = int(os.getenv("width"))
LISTWIDTH = int(os.getenv("listWidth"))
NOTEWIDTH = int(os.getenv("noteWidth"))
MAXHISTORY = 10
ESTIMATE = ""
TOKENS = [0, 0]
NAMESLIST = []
MISMATCH = []  # Lists files that throw a mismatch error (Length of GPT list response is wrong)
PBAR = None
FILENAME = None
TIMETOTAL = 0  # Total Time Taken for all translations

# Regex - Need to change this if you want to translate from/to other languages. Default is Japanese Regex
LANGREGEX = r"[一-龠ぁ-ゔァ-ヴーａ-ｚＡ-Ｚ０-９\uFF61-\uFF9F]+"

# Get pricing configuration based on the model
PRICING_CONFIG = getPricingConfig(MODEL)
INPUTAPICOST = PRICING_CONFIG["inputAPICost"]
OUTPUTAPICOST = PRICING_CONFIG["outputAPICost"]
BATCHSIZE = PRICING_CONFIG["batchSize"]
FREQUENCY_PENALTY = PRICING_CONFIG["frequencyPenalty"]

# tqdm Globals
BAR_FORMAT = "{l_bar}{bar:10}{r_bar}{bar:-10b}"
POSITION = 0
LEAVE = False

# Initialize Translation Config
TRANSLATION_CONFIG = TranslationConfig(
    model=MODEL,
    language=LANGUAGE,
    prompt=PROMPT,
    vocab=VOCAB,
    langRegex=LANGREGEX,
    batchSize=BATCHSIZE,
    maxHistory=MAXHISTORY,
    estimateMode=False  # Will be set dynamically based on ESTIMATE
)
LEAVE = False

# Config (Default)
FIRSTLINESPEAKERS = False
FACENAME101 = False
NAMES = False
BRFLAG = False
FIXTEXTWRAP = True
IGNORETLTEXT = False

# Dialogue / Scroll / Choices (Main Codes)
CODE401 = True
CODE405 = True
CODE102 = True

# Optional
CODE101 = True
CODE408 = False

# Variables
CODE122 = False

# Other
CODE355655 = False
CODE357 = False
CODE657 = False
CODE356 = False
CODE320 = False
CODE324 = False
CODE111 = False
CODE108 = False


def handleACE(filename, estimate):
    global ESTIMATE, TOKENS, FILENAME
    ESTIMATE = estimate
    FILENAME = filename

    # Translate
    start = time.time()
    translatedData = openFiles(filename)

    # Translate
    if not estimate:
        try:
            with open("translated/" + filename, "w", encoding="utf-8", newline="\n") as outFile:
                yaml = YAML(pure=True)
                yaml.width = 4096
                yaml.default_style = "'"
                yaml.dump(translatedData[0], outFile)
        except Exception:
            traceback.print_exc()
            return "Fail"

    # Print File
    end = time.time()
    tqdm.write(getResultString(translatedData, end - start, filename))
    with LOCK:
        TOKENS[0] += translatedData[1][0]
        TOKENS[1] += translatedData[1][1]

    # Print Total
    totalString = getResultString(["", TOKENS, None], end - start, "TOTAL")

    # Print any errors on maps
    if len(MISMATCH) > 0:
        return totalString + Fore.RED + f"\nMismatch Errors: {MISMATCH}" + Fore.RESET
    else:
        return totalString


def openFiles(filename):
    yaml = YAML(pure=True)  # Need a yaml instance per thread.
    yaml.width = 4096
    yaml.default_style = "'"

    with open("files/" + filename, "r", encoding="UTF-8") as f:
        data = yaml.load(f)
        # Map Files
        if "Map" in filename and "MapInfos" not in filename:
            translatedData = parseMap(data, filename)

        # CommonEvents Files
        elif "CommonEvents" in filename:
            translatedData = parseCommonEvents(data, filename)

        # Actor File
        elif "Actors" in filename:
            translatedData = parseNames(data, filename, "Actors")

        # Armor File
        elif "Armors" in filename:
            translatedData = parseNames(data, filename, "Armors")

        # Weapons File
        elif "Weapons" in filename:
            translatedData = parseNames(data, filename, "Weapons")

        # Classes File
        elif "Classes" in filename:
            translatedData = parseNames(data, filename, "Classes")

        # Enemies File
        elif "Enemies" in filename:
            translatedData = parseNames(data, filename, "Enemies")

        # Items File
        elif "Items" in filename:
            translatedData = parseNames(data, filename, "Items")

        # MapInfo File
        elif "MapInfos" in filename:
            translatedData = parseNames(data, filename, "MapInfos")

        # Skills File
        elif "Skills" in filename:
            translatedData = parseNames(data, filename, "Skills")

        # Troops File
        elif "Troops" in filename:
            translatedData = parseTroops(data, filename)

        # States File
        elif "States" in filename:
            translatedData = parseSS(data, filename)

        # System File
        elif "System" in filename:
            translatedData = parseSystem(data, filename)

        # Scenario File
        elif "Scenario" in filename:
            translatedData = parseScenario(data, filename)

        else:
            raise NameError(filename + " Not Supported")

    return translatedData


def getResultString(translatedData, translationTime, filename):
    global TIMETOTAL
    # File Print String
    cost = calculateCost(translatedData[1][0], translatedData[1][1], MODEL)
    totalTokenstring = (
        Fore.YELLOW + "[Input: " + str(translatedData[1][0]) + "]"
        "[Output: "
        + str(translatedData[1][1])
        + "]" "[Cost: ${:,.4f}".format(cost)
        + "]"
    )
    if filename != "TOTAL":
        timeString = Fore.BLUE + "[" + str(round(translationTime, 1)) + "s]"
        TIMETOTAL += round(translationTime, 1)
    else:
        timeString = Fore.BLUE + "[" + str(round(TIMETOTAL, 1)) + "s]"

    if translatedData[2] is None:
        # Success
        return filename + ": " + totalTokenstring + timeString + Fore.GREEN + " \u2713 " + Fore.RESET
    else:
        # Fail
        try:
            raise translatedData[2]
        except Exception as e:
            traceback.print_exc()
            errorString = str(e) + Fore.RED
            return filename + ": " + totalTokenstring + timeString + Fore.RED + " \u2717 " + errorString + Fore.RESET


def save_progress_yaml(data, filename):
    """Atomically write current YAML data to translated/filename; skip in estimate mode."""
    try:
        if ESTIMATE:
            return
        os.makedirs("translated", exist_ok=True)
        tmp_path = os.path.join("translated", f"{filename}.tmp")
        final_path = os.path.join("translated", filename)
        yaml = YAML(pure=True)
        yaml.width = 4096
        yaml.default_style = "'"
        with open(tmp_path, "w", encoding="utf-8", newline="\n") as outFile:
            yaml.dump(data, outFile)
        os.replace(tmp_path, final_path)
    except Exception:
        traceback.print_exc()


def maybe_save_progress_yaml(data, filename, tokens):
    """Save YAML progress only when tokens indicate actual translation work."""
    try:
        if not tokens:
            return
        if isinstance(tokens, (list, tuple)) and len(tokens) >= 2 and (tokens[0] or tokens[1]):
            save_progress_yaml(data, filename)
    except Exception:
        traceback.print_exc()


def parseMap(data, filename):
    totalTokens = [0, 0]
    totalLines = 0
    events = data["events"]
    global LOCK

    # Translate display_name for Map files
    if "Map" in filename:
        response = translateAI(
            data["display_name"],
            "Reply with only the " + LANGUAGE + " translation of the RPG location name",
            False,
        )
        totalTokens[0] += response[1][0]
        totalTokens[1] += response[1][1]
        data["display_name"] = response[0].replace('"', "")

    # Process each page synchronously and persist after each
    with tqdm(bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE) as pbar:
        pbar.desc = filename
        for key in events:
            if key is not None:
                # This translates ID of events. (May break the game)
                if "<namepop" in events[key]["name"]:
                    response = translateNoteOmitSpace(events[key], r"<namepop\s(.*?)\s?\d?>.*")
                    totalTokens[0] += response[0]
                    totalTokens[1] += response[1]
                for page in events[key]["pages"]:
                    if page is not None:
                        try:
                            tt = searchCodes(page, pbar, [], filename)
                            totalTokens[0] += tt[0]
                            totalTokens[1] += tt[1]
                        except Exception as e:
                            traceback.print_exc()
                            return [data, totalTokens, e]
                        finally:
                            maybe_save_progress_yaml(data, filename, tt)
    return [data, totalTokens, None]


def translateNote(event, regex, wordwrap=False):
    # Regex String
    jaString = event["note"]
    match = re.findall(regex, jaString, re.DOTALL)
    if match:
        tokens = [0, 0]
        i = 0
        while i < len(match):
            initialJAString = match[i]
            modifiedJAString = initialJAString
            # Remove any textwrap
            if wordwrap:
                modifiedJAString = modifiedJAString.replace("\n", " ")

            # Translate
            response = translateAI(
                modifiedJAString,
                "Reply with only the " + LANGUAGE + " translation.",
                False,
            )
            translatedText = response[0]
            tokens[0] += response[1][0]
            tokens[1] += response[1][1]

            # Textwrap
            if wordwrap:
                translatedText = dazedwrap.wrapText(translatedText, width=NOTEWIDTH)
                translatedText = translatedText.replace('"', "")

            jaString = jaString.replace(initialJAString, translatedText)
            event["note"] = jaString
            i += 1
        return tokens
    return [0, 0]


# For notes that can't have spaces.
def translateNoteOmitSpace(event, regex):
    # Regex that only matches text inside LB.
    jaString = event["note"]

    match = re.findall(regex, jaString, re.DOTALL)
    if match:
        oldJAString = match[0]
        # Remove any textwrap
        jaString = re.sub(r"\n", " ", oldJAString)

        # Translate
        response = translateAI(
            jaString,
            "Reply with the " + LANGUAGE + " translation of the location name.",
            False,
        )
        translatedText = response[0]

        translatedText = translatedText.replace('"', "")
        translatedText = translatedText.replace(" ", "_")
        event["note"] = event["note"].replace(oldJAString, translatedText)
        return response[1]
    return [0, 0]


def parseCommonEvents(data, filename):
    totalTokens = [0, 0]
    totalLines = 0
    global LOCK

    # Get total for progress bar
    for page in data:
        if page is not None:
            totalLines += len(page["list"])

    with tqdm(bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE) as pbar:
        pbar.desc = filename
        for page in data:
            if page is not None:
                try:
                    tt = searchCodes(page, pbar, [], filename)
                    totalTokens[0] += tt[0]
                    totalTokens[1] += tt[1]
                except Exception as e:
                    traceback.print_exc()
                    return [data, totalTokens, e]
                finally:
                    maybe_save_progress_yaml(data, filename, tt)
    return [data, totalTokens, None]


def parseTroops(data, filename):
    totalTokens = [0, 0]
    totalLines = 0
    global LOCK

    # Get total for progress bar
    for troop in data:
        if troop is not None:
            for page in troop["pages"]:
                totalLines += len(page["list"]) + 1  # The +1 is because each page has a name.

    with tqdm(bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE) as pbar:
        pbar.desc = filename
        for troop in data:
            if troop is not None:
                for page in troop["pages"]:
                    if page is not None:
                        try:
                            tt = searchCodes(page, pbar, [], filename)
                            totalTokens[0] += tt[0]
                            totalTokens[1] += tt[1]
                        except Exception as e:
                            traceback.print_exc()
                            return [data, totalTokens, e]
                        finally:
                            save_progress_yaml(data, filename)
    return [data, totalTokens, None]


def parseNames(data, filename, context):
    totalTokens = [0, 0]

    # Precompute total work units for progress bar (exclude notes)
    def count_work_units(entries, ctx):
        total = 0
        for entry in entries:
            if not entry:
                continue
            name = entry.get("name", "")
            desc = entry.get("description", "")
            nickname = entry.get("nickname", "")
            if ctx == "Actors":
                if name:
                    total += 1
                if nickname:
                    total += 1
                if desc:
                    total += 1
            elif ctx in ["Armors", "Weapons", "Items"]:
                if name:
                    total += 1
                if desc:
                    total += 1
            elif ctx == "Skills":
                if name:
                    total += 1
                if desc:
                    total += 1
                for n in range(1, 5):
                    if entry.get(f"message{n}"):
                        total += 1
            elif ctx in ["Enemies", "Classes", "MapInfos"]:
                if name:
                    total += 1
        return total

    total_units = count_work_units(data, context)

    with tqdm(total=total_units, bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE) as pbar:
        pbar.desc = filename
        try:
            result = searchNames(data, pbar, context)
            totalTokens[0] += result[0]
            totalTokens[1] += result[1]
        except Exception as e:
            traceback.print_exc()
            return [data, totalTokens, e]
        finally:
            maybe_save_progress_yaml(data, filename, result)
    return [data, totalTokens, None]


def parseSS(data, filename):
    totalTokens = [0, 0]

    # Precompute total units: name, description, message1..4 (exclude notes)
    def count_work_units(states):
        total = 0
        for st in states:
            if not st:
                continue
            if st.get("name"):
                total += 1
            if st.get("description"):
                total += 1
            for n in range(1, 5):
                if st.get(f"message{n}"):
                    total += 1
        return total

    total_units = count_work_units(data)

    with tqdm(total=total_units, bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE) as pbar:
        pbar.desc = filename
        for ss in data:
            if ss is not None:
                try:
                    result = searchSS(ss, pbar)
                    totalTokens[0] += result[0]
                    totalTokens[1] += result[1]
                except Exception as e:
                    traceback.print_exc()
                    return [data, totalTokens, e]
                finally:
                    maybe_save_progress_yaml(data, filename, result)
    return [data, totalTokens, None]


def parseSystem(data, filename):
    totalTokens = [0, 0]

    # Precompute total units for Ace schema
    def count_work_units(sys):
        total = 0
        # Terms: sum list lengths of each term list
        for term in sys.get("terms", {}):
            termList = sys["terms"][term]
            if isinstance(termList, list):
                total += len(termList)
        # game_title might be a string; count as 1 if non-empty
        gt = sys.get("game_title")
        if isinstance(gt, str) and gt:
            total += 1
        # variables is a list
        total += len(sys.get("variables", []) or [])
        total += len(sys.get("weapon_types", []) or [])
        total += len(sys.get("armor_types", []) or [])
        total += len(sys.get("skill_types", []) or [])
        return total

    total_units = count_work_units(data)

    with tqdm(total=total_units, bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE) as pbar:
        pbar.desc = filename
        input_log = {}
        output_log = {}
        try:
            # Helper to flatten lists to Line1, Line2, ...
            def flatten_lines(prefix, values, target):
                for idx, v in enumerate(values):
                    target[f"{prefix}Line{idx+1}"] = v

            # Capture pre-translation values
            if "terms" in data:
                for key, value in data["terms"].items():
                    if isinstance(value, list):
                        flatten_lines(key, value, input_log)
                    else:
                        input_log[key] = value
            for key in ["gameTitle", "game_title", "armor_types", "skill_types", "weapon_types"]:
                if key in data:
                    val = data[key]
                    if isinstance(val, list):
                        flatten_lines(key, val, input_log)
                    else:
                        input_log[key] = val
            # Run translation
            result = searchSystem(data, pbar)
            totalTokens[0] += result[0]
            totalTokens[1] += result[1]
            # Capture post-translation values
            if "terms" in data:
                for key, value in data["terms"].items():
                    if isinstance(value, list):
                        flatten_lines(key, value, output_log)
                    else:
                        output_log[key] = value
            for key in ["gameTitle", "game_title", "armor_types", "skill_types", "weapon_types"]:
                if key in data:
                    val = data[key]
                    if isinstance(val, list):
                        flatten_lines(key, val, output_log)
                    else:
                        output_log[key] = val
        except Exception as e:
            traceback.print_exc()
            return [data, totalTokens, e]
        finally:
            maybe_save_progress_yaml(data, filename, result)
            # Log translation history for system.yaml in established line-by-line format
            try:
                with open("log/translationHistory.txt", "a", encoding="utf-8") as log_file:
                    log_file.write("Input:\n")
                    log_file.write(json.dumps(input_log, ensure_ascii=False, indent=4))
                    log_file.write("\nOutput:\n")
                    log_file.write(json.dumps(output_log, ensure_ascii=False, indent=4))
                    log_file.write("\n")
            except Exception:
                pass
    return [data, totalTokens, None]


def parseScenario(data, filename):
    totalTokens = [0, 0]
    totalLines = 0
    global LOCK

    # Get total for progress bar
    for page in data.items():
        totalLines += len(page[1])

    with tqdm(bar_format=BAR_FORMAT, position=POSITION, leave=LEAVE) as pbar:
        pbar.desc = filename
        for page in data.items():
            if page[1] is not None:
                try:
                    tt = searchCodes(page[1], pbar, [], filename)
                    totalTokens[0] += tt[0]
                    totalTokens[1] += tt[1]
                except Exception as e:
                    traceback.print_exc()
                    return [data, totalTokens, e]
                finally:
                    maybe_save_progress_yaml(data, filename, tt)
    return [data, totalTokens, None]


def searchNames(data, pbar, context):
    totalTokens = [0, 0]
    nameList = []
    profileList = []
    nicknameList = []
    descriptionList = []
    noteList = []
    i = 0  # Counter
    j = 0  # Counter 2
    filling = False
    mismatch = False
    batchFull = False

    # Set the context of what we are translating
    if "Actors" in context:
        newContext = "Reply with only the " + LANGUAGE + " translation of the NPC name"
    if "Armors" in context:
        newContext = "Reply with only the " + LANGUAGE + " translation of the RPG equipment name"
    if "Classes" in context:
        newContext = "Reply with only the " + LANGUAGE + " translation of the RPG class name"
    if "MapInfos" in context:
        newContext = "Reply with only the " + LANGUAGE + " translation of the location name"
    if "Enemies" in context:
        newContext = "Reply with only the " + LANGUAGE + " translation of the enemy NPC name"
    if "Weapons" in context:
        newContext = "Reply with only the " + LANGUAGE + " translation of the RPG weapon name"
    if "Items" in context:
        newContext = "Reply with only the " + LANGUAGE + " translation of the RPG item name"
    if "Skills" in context:
        newContext = "Reply with only the " + LANGUAGE + " translation of the RPG skill name"

    # Names
    with open("log/translations.txt", "a", encoding="utf-8") as file:
        file.write(f"\n#{context}\n")
    while i < len(data) or filling == True:
        if i < len(data):
            # Empty Data
            if data[i] is None or data[i]["name"] == "":
                i += 1
                continue

            # Filling up Batch
            filling = True
            if context in "Actors":
                if len(nameList) < BATCHSIZE:
                    if data[i]["name"] != "":
                        nameList.append(data[i]["name"])
                    if "nickname" in data[i] and data[i]["nickname"]:
                        nicknameList.append(data[i]["nickname"])
                    if "description" in data[i] and data[i]["description"]:
                        profileList.append(data[i]["description"].replace("\n", " "))

                    # Notes
                    if "<note:" in data[i]["note"]:
                        tokensResponse = translateNote(data[i], r"<note:(.*?)>")
                        totalTokens[0] += tokensResponse[0]
                        totalTokens[1] += tokensResponse[1]
                    if "PE拡張" in data[i]["note"]:
                        tokensResponse = translateNote(data[i], r"<PE拡張:(.*?)>")
                        totalTokens[0] += tokensResponse[0]
                        totalTokens[1] += tokensResponse[1]
                    i += 1
                else:
                    batchFull = True
            if context in ["Armors", "Weapons", "Items"]:
                if len(nameList) < BATCHSIZE:
                    nameList.append(data[i]["name"])
                    if "description" in data[i] and data[i]["description"] != "":
                        description = data[i]["description"]
                        description = description.replace("\n", " ")
                        descriptionList.append(description)
                    if "<hint:" in data[i]["note"]:
                        tokensResponse = translateNote(data[i], r"<hint:(.*?)>")
                        totalTokens[0] += tokensResponse[0]
                        totalTokens[1] += tokensResponse[1]
                    if "<SGDescription:" in data[i]["note"]:
                        tokensResponse = translateNote(data[i], r"<SGDescription:(.*?)>")
                        totalTokens[0] += tokensResponse[0]
                        totalTokens[1] += tokensResponse[1]
                    if "<SG説明:" in data[i]["note"]:
                        tokensResponse = translateNote(data[i], r"<SG説明:\n?(.*?)>")
                        totalTokens[0] += tokensResponse[0]
                        totalTokens[1] += tokensResponse[1]
                    if "<SG説明2:" in data[i]["note"]:
                        tokensResponse = translateNote(data[i], r"<SG説明2:\n?(.*?)>")
                        totalTokens[0] += tokensResponse[0]
                        totalTokens[1] += tokensResponse[1]
                    if "<SG説明3:" in data[i]["note"]:
                        tokensResponse = translateNote(data[i], r"<SG説明3:\n?(.*?)>")
                        totalTokens[0] += tokensResponse[0]
                        totalTokens[1] += tokensResponse[1]
                    if "<SG説明4:" in data[i]["note"]:
                        tokensResponse = translateNote(data[i], r"<SG説明4:\n?(.*?)>")
                        totalTokens[0] += tokensResponse[0]
                        totalTokens[1] += tokensResponse[1]
                    if "<SGカテゴリ:" in data[i]["note"]:
                        tokensResponse = translateNote(data[i], r"<SGカテゴリ:(.*?)>")
                        totalTokens[0] += tokensResponse[0]
                        totalTokens[1] += tokensResponse[1]
                    if "Switch Shop Description" in data[i]["note"]:
                        tokensResponse = translateNote(data[i], r"<Switch Shop Description>\n(.*)\n")
                        totalTokens[0] += tokensResponse[0]
                        totalTokens[1] += tokensResponse[1]
                    if "<MapText:" in data[i]["note"]:
                        tokensResponse = translateNote(data[i], r"<MapText:(.*?)>")
                        totalTokens[0] += tokensResponse[0]
                        totalTokens[1] += tokensResponse[1]
                    if "<WATs:" in data[i]["note"]:
                        tokensResponse = translateNote(data[i], r"WATs:(.+?)>")
                        totalTokens[0] += tokensResponse[0]
                        totalTokens[1] += tokensResponse[1]
                    if "<ADT:" in data[i]["note"]:
                        tokensResponse = translateNote(data[i], r"ADTs?:(.+?)>")
                        totalTokens[0] += tokensResponse[0]
                        totalTokens[1] += tokensResponse[1]
                    if "<detail" in data[i]["note"]:
                        tokensResponse = translateNote(data[i], r"<detail:(.*?)>")
                        totalTokens[0] += tokensResponse[0]
                        totalTokens[1] += tokensResponse[1]
                    if "<Name" in data[i]["note"]:
                        tokensResponse = translateNote(data[i], r"<Name:(.*?)>")
                        totalTokens[0] += tokensResponse[0]
                        totalTokens[1] += tokensResponse[1]
                    if "sub_1" in data[i]["note"]:
                        tokensResponse = translateNote(data[i], r"<sub_1:([^>]+)", True)
                        totalTokens[0] += tokensResponse[0]
                        totalTokens[1] += tokensResponse[1]
                    if "sub_2" in data[i]["note"]:
                        tokensResponse = translateNote(data[i], r"<sub_2:([^>]+)", True)
                        totalTokens[0] += tokensResponse[0]
                        totalTokens[1] += tokensResponse[1]
                    if "sub_3" in data[i]["note"]:
                        tokensResponse = translateNote(data[i], r"<sub_3:([^>]+)", True)
                        totalTokens[0] += tokensResponse[0]
                        totalTokens[1] += tokensResponse[1]
                    if "infowindow" in data[i]["note"]:
                        tokensResponse = translateNote(data[i], r"<infowindow:(.*?)>", True)
                        totalTokens[0] += tokensResponse[0]
                        totalTokens[1] += tokensResponse[1]
                    if "ExtendDesc" in data[i]["note"]:
                        tokensResponse = translateNote(data[i], r"<ExtendDesc:(.*?)>", True)
                        totalTokens[0] += tokensResponse[0]
                        totalTokens[1] += tokensResponse[1]
                    

                    i += 1
                else:
                    batchFull = True
            if context in ["Skills"]:
                if len(nameList) < BATCHSIZE:
                    nameList.append(data[i]["name"])
                    if "description" in data[i] and data[i]["description"]:
                        descriptionList.append(data[i]["description"].replace("\n", " "))

                    # Messages
                    number = 1
                    while number < 5:
                        if f"message{number}" in data[i] and data[i][f"message{number}"]:
                            if data[i][f"message{number}"][0] in ["は", "を", "の", "に", "が"]:
                                msgResponse = translateAI(
                                    "Taro" + data[i][f"message{number}"],
                                    "reply with only the gender neutral "
                                    + LANGUAGE
                                    + " translation of the action log. Always start the sentence with Taro. For example, Translate 'Taroを倒した！' as 'Taro was defeated!'",
                                    False,
                                )
                                data[i][f"message{number}"] = msgResponse[0].replace("Taro", "")
                                totalTokens[0] += msgResponse[1][0]
                                totalTokens[1] += msgResponse[1][1]
                                if pbar is not None:
                                    pbar.refresh()
                                number += 1

                            else:
                                msgResponse = translateAI(
                                    data[i][f"message{number}"],
                                    "reply with only the gender neutral " + LANGUAGE + " translation",
                                    False,
                                )
                                data[i][f"message{number}"] = msgResponse[0]
                                totalTokens[0] += msgResponse[1][0]
                                totalTokens[1] += msgResponse[1][1]
                                if pbar is not None:
                                    pbar.refresh()
                                number += 1
                        else:
                            number += 1

                    # Notes
                    if "<WAT:" in data[i]["note"]:
                        tokensResponse = translateNote(data[i], r"WATs:(.+?)>")
                        totalTokens[0] += tokensResponse[0]
                        totalTokens[1] += tokensResponse[1]
                    if "<ADT:" in data[i]["note"]:
                        tokensResponse = translateNote(data[i], r"ADTs?:(.+?)>")
                        totalTokens[0] += tokensResponse[0]
                        totalTokens[1] += tokensResponse[1]
                    if "<拡張説明:" in data[i]["note"]:
                        tokensResponse = translateNote(data[i], r"<拡張説明:(.+?)>")
                        totalTokens[0] += tokensResponse[0]
                        totalTokens[1] += tokensResponse[1]
                    if "<STS DESC>" in data[i]["note"]:
                        tokensResponse = translateNote(data[i], r"<STS DESC>\n(.+?)\n<")
                        totalTokens[0] += tokensResponse[0]
                        totalTokens[1] += tokensResponse[1]

                    i += 1
                else:
                    batchFull = True
            if context in ["Enemies", "Classes", "MapInfos"]:
                if len(nameList) < BATCHSIZE:
                    nameList.append(data[i]["name"])

                    # Notes
                    if "note" in data[i]:
                        if "<note:" in data[i]["note"]:
                            tokensResponse = translateNote(data[i], r"<note:(.*?)>")
                            totalTokens[0] += tokensResponse[0]
                            totalTokens[1] += tokensResponse[1]
                        if "<desc" in data[i]["note"]:
                            tokensResponse = translateNote(data[i], r"<desc\d:(.*?)>")
                            totalTokens[0] += tokensResponse[0]
                            totalTokens[1] += tokensResponse[1]
                        if "図鑑特徴" in data[i]["note"]:
                            tokensResponse = translateNote(data[i], r"<図鑑特徴:(.+?)>")
                            totalTokens[0] += tokensResponse[0]
                            totalTokens[1] += tokensResponse[1]
                        if "図鑑説明" in data[i]["note"]:
                            tokensResponse = translateNote(data[i], r"<図鑑説明:(.+?)>")
                            totalTokens[0] += tokensResponse[0]
                            totalTokens[1] += tokensResponse[1]
                    i += 1
                else:
                    batchFull = True

        # Batch Full
        if batchFull == True or i >= len(data):
            k = j  # Original Index
            if context in "Actors":
                # Name
                response = translateAI(nameList, newContext, True)
                translatedNameBatch = response[0]
                totalTokens[0] += response[1][0]
                totalTokens[1] += response[1][1]
                if pbar is not None and nameList:
                    pbar.refresh()

                # Nickname
                if nicknameList:
                    response = translateAI(nicknameList, newContext, True)
                    translatedNicknameBatch = response[0]
                    totalTokens[0] += response[1][0]
                    totalTokens[1] += response[1][1]
                    if pbar is not None:
                        pbar.refresh()

                # Profile
                if profileList:
                    response = translateAI(profileList, "", True)
                    translatedProfileBatch = response[0]
                    totalTokens[0] += response[1][0]
                    totalTokens[1] += response[1][1]
                    if pbar is not None:
                        pbar.refresh()

                # Set Data
                if len(nameList) == len(translatedNameBatch):
                    j = k
                    while j < i:
                        # Empty Data
                        if data[j] is None or data[j]["name"] == "":
                            j += 1
                            continue
                        else:
                            # Get Text
                            if data[j]["name"] != "":
                                with open("log/translations.txt", "a", encoding="utf-8") as file:
                                    file.write(f'{data[j]["name"]} ({translatedNameBatch[0]})\n')
                                    data[j]["name"] = translatedNameBatch[0]
                                translatedNameBatch.pop(0)
                            if "nickname" in data[j] and data[j]["nickname"]:
                                data[j]["nickname"] = translatedNicknameBatch[0]
                                translatedNicknameBatch.pop(0)
                            if "description" in data[j] and data[j]["description"]:
                                data[j]["description"] = dazedwrap.wrapText(translatedProfileBatch[0], LISTWIDTH)
                                translatedProfileBatch.pop(0)

                            # If Batch is empty. Move on.
                            if len(translatedNameBatch) == 0:
                                nameList.clear()
                                profileList.clear()
                                nicknameList.clear()
                                batchFull = False
                                filling = False
                            j += 1
                else:
                    mismatch = True

            if context in ["Armors", "Weapons", "Items", "Skills"]:
                # Name
                response = translateAI(nameList, newContext, True)
                translatedNameBatch = response[0]
                totalTokens[0] += response[1][0]
                totalTokens[1] += response[1][1]
                if pbar is not None and nameList:
                    pbar.refresh()

                # Description
                if descriptionList:
                    response = translateAI(
                        descriptionList,
                        f"Reply with only the {LANGUAGE} translation of the text.",
                        True,
                    )
                    translatedDescriptionBatch = response[0]
                    totalTokens[0] += response[1][0]
                    totalTokens[1] += response[1][1]
                    if pbar is not None:
                        pbar.refresh()

                # Set Data
                if len(nameList) == len(translatedNameBatch):
                    j = k
                    with open("log/translations.txt", "a", encoding="utf-8") as file:
                        while j < i:
                            # Empty Data
                            if data[j] is None or data[j]["name"] == "":
                                j += 1
                                continue
                            else:
                                # Get Text
                                file.write(f'{data[j]['name']} ({translatedNameBatch[0]})\n')
                                data[j]["name"] = translatedNameBatch[0]
                                translatedNameBatch.pop(0)
                                if "description" in data[j] and data[j]["description"] != "":
                                    translatedDescriptionBatch[0] = dazedwrap.wrapText(translatedDescriptionBatch[0], LISTWIDTH)
                                    data[j]["description"] = translatedDescriptionBatch[0]
                                    translatedDescriptionBatch.pop(0)

                            # If Batch is empty. Move on.
                            if len(translatedNameBatch) == 0:
                                nameList.clear()
                                descriptionList.clear()
                                batchFull = False
                                filling = False
                            j += 1
                else:
                    mismatch = True
            if context in ["Enemies", "Classes", "MapInfos"]:
                response = translateAI(nameList, newContext, True)
                translatedNameBatch = response[0]
                totalTokens[0] += response[1][0]
                totalTokens[1] += response[1][1]
                if pbar is not None and nameList:
                    pbar.refresh()

                # Set Data
                if len(nameList) == len(translatedNameBatch):
                    j = k
                    while j < i:
                        # Empty Data
                        if data[j] is None or data[j]["name"] == "":
                            j += 1
                            continue
                        else:
                            with open("log/translations.txt", "a", encoding="utf-8") as file:
                                file.write(f'{data[j]["name"]} ({translatedNameBatch[0]})\n')
                            # Get Text
                            data[j]["name"] = translatedNameBatch[0]
                            translatedNameBatch.pop(0)

                            # If Batch is empty. Move on.
                            if len(translatedNameBatch) == 0:
                                nameList.clear()
                                batchFull = False
                                filling = False
                            j += 1
                else:
                    mismatch = True

            # Mismatch
            if mismatch == True:
                MISMATCH.append(nameList)
                nameList.clear()
                profileList.clear()
                descriptionList.clear()
                filling = False
                mismatch = False
                batchFull = False

                i += 1

    return totalTokens


def searchCodes(page, pbar, jobList, filename):
    if len(jobList) > 0:
        list401 = jobList[0]
        list122 = jobList[1]
        list355655 = jobList[2]
        list108 = jobList[3]
        list356 = jobList[4]
        list357 = jobList[5]
        list408 = jobList[6]
        setData = False
    else:
        list401 = []
        list122 = []
        list355655 = []
        list108 = []
        list356 = []
        list357 = []
        list408 = []
        setData = True
    textHistory = []
    match = []
    totalTokens = [0, 0]
    translatedText = ""
    speaker = ""
    speakerID = None
    syncIndex = 0
    CLFlag = False
    maxHistory = MAXHISTORY
    VNameValue = None
    global LOCK
    global NAMESLIST
    global MISMATCH
    global PBAR
    with LOCK:
        PBAR = pbar

    # Begin Parsing File
    try:
        # Normal Format
        if "list" in page:
            codeList = page["list"]

        # Special Format (Scenario)
        else:
            codeList = page

        # Iterate through page
        i = 0
        while i < len(codeList):
            with LOCK:
                # syncIndex will keep i in sync when it gets modified
                if syncIndex > i:
                    i = syncIndex
                if len(codeList) <= i:
                    break

            # Declare Varss
            currentGroup = []
            nametag = ""

            ## Event Code: 401 Show Text
            if "c" in codeList[i] and codeList[i]["c"] in [401, 405, -1] and (CODE401 or CODE405):
                # Save Code and starting index (j)
                code = codeList[i]["c"]
                j = i
                endtag = ""
                instantLineFlag = False

                # Grab String
                if len(codeList[i]["p"]) > 0:
                    jaString = codeList[i]["p"][0]
                    oldjaString = jaString
                else:
                    codeList[i]["c"] = -1
                    i += 1
                    continue

                # # For Retarded Devs
                # retardRegex = r'([\\]+[nN]\[[\\]+V\[\d*?\]\])'
                # match = re.search(retardRegex, jaString)
                # if match:
                #     if VNameValue == 1:
                #         jaString = re.sub(retardRegex, 'リッカ', jaString)
                #     if VNameValue == 2:
                #         jaString = re.sub(retardRegex, 'ミミ', jaString)
                #     if VNameValue == 3:
                #         jaString = re.sub(retardRegex, 'ヒトミ', jaString)
                #     if VNameValue == 4:
                #         jaString = re.sub(retardRegex, 'Taro', jaString)
                #     if VNameValue == 5:
                #         jaString = re.sub(retardRegex, '富士見', jaString)

                # Speaker Check
                speakerList = []

                # m and z Codes
                match = re.search(r"(.*?)[\\]+m\[\d+?\][\\]+z\[\d+?\]", jaString)
                if match:
                    speakerList.append(match.group(1))
                    if "\\c" in speakerList[0]:
                        speakerList = re.findall(
                            r"^[\\]+[cC]\[[\d]+\]【?(.+?)】?[\\]+[Cc]\[[\d]\]\\?\\?$",
                            speakerList[0],
                        )

                # Brackets
                if len(speakerList) == 0:
                    speakerList = re.findall(r"^(?:[\\]+[rlRL]\[[\w\d\-]+\])?【(.*?)】$|^(?:[\\]+[rlRL]\[[\w\d\-]+\])?【(.*?)】[\\]*[a-zA-Z]*\[.*\]$", jaString)
                    if speakerList:
                        if speakerList[0][0]:
                            speakerList = [speakerList[0][0]]
                        else:
                            speakerList = [speakerList[0][1]]

                # Colors
                if len(speakerList) == 0:
                    speakerList = re.findall(
                        r"^[\\]+[cC]\[[\d]+\]【?(.+?)】?[\\]+[Cc]\[?[\d]?\]?\\?\\?$",
                        jaString,
                    )

                # Colons
                if len(speakerList) == 0:
                    speakerList = re.findall(
                        r"[\\]*[cC]?\[?\d*\]?(.+)：$",
                        jaString,
                    )

                # First Line Speakers
                if len(speakerList) == 0 and FIRSTLINESPEAKERS is True:
                    # Remove any RPGMaker Code at start (including \r[...] and \l[...] patterns)
                    ffMatch = re.search(
                        r"^((?:[\\]+[^cCnNiIkKvV]+\[[\d\w]+\])+|(?:[\\]+[rlRL]\[[\w\d\-]+\]))",
                        jaString,
                    )
                    if ffMatch != None:
                        jaString = jaString.replace(ffMatch.group(0), "")
                        nametag += ffMatch.group(0)

                    # Test Speaker
                    if (
                        len(jaString) < 40
                        and "c" in codeList[i + 1]
                        and codeList[i + 1]["c"] in [401, 405, -1]
                        and len(codeList[i + 1]["p"]) > 0
                        and len(codeList[i + 1]["p"][0]) > 0
                    ):
                        nextString = codeList[i + 1]["p"][0].strip()
                        if nextString and nextString[0] in [
                            "「",
                            '"',
                            "(",
                            "（",
                            "*",
                            "[",
                        ]:
                            speakerList = re.findall(r".+", jaString)

                # Replace Speaker
                if len(speakerList) != 0 and codeList[i + 1]["c"] in [401, 405, -1]:
                    # Get Speaker
                    response = getSpeaker(speakerList[0])
                    speaker = response[0]
                    totalTokens[0] += response[1][0]
                    totalTokens[1] += response[1][1]

                    # Set Data
                    if not setData:
                        # Check if there's a \r[...] or \l[...] code at the beginning
                        codePrefix = re.search(r"^([\\]+[rlRL]\[[\w\d\-]+\])", jaString)
                        if codePrefix:
                            # Replace only the speaker name, preserving the code and brackets
                            codeList[i]["p"][0] = jaString.replace(f"【{speakerList[0]}】", f"【{speaker}】")
                        else:
                            codeList[i]["p"][0] = nametag + jaString.replace(speakerList[0], speaker)
                    nametag = ""

                    # Iterate to next string
                    i += 1
                    j = i
                    while codeList[i]["c"] in [-1]:
                        i += 1
                        j = i
                    jaString = codeList[i]["p"][0]

                # Replace Symbols
                jaString = jaString.replace("…", "...")
                jaString = jaString.replace("。", ".")
                jaString = jaString.replace("･", ".")
                jaString = jaString.replace("「", '"')
                jaString = jaString.replace("」", '"')

                # Check if there is text to translate
                if not re.search(r"\w+", jaString):
                    i += 1
                    continue

                # Validate Japanese Text
                if not re.search(LANGREGEX, jaString) and IGNORETLTEXT:
                    i += 1
                    continue

                # Using this to keep track of 401's in a row.
                currentGroup.append(jaString)

                # Join Up 401's into single string
                if len(codeList) > i + 1:
                    while codeList[i + 1]["c"] in [401, 405, -1] and len(codeList[i]["p"]) > 0 and not re.match(r"^(\s*[\\]+[aAbBdDeEfFgGhHjJlLmMoOpPqQrRsStTuUwWxXyYzZ]+\[[\w\d\[\]\\]+\])", codeList[i+1]["p"][0]):
                        if not setData:
                            codeList[i]["p"] = []
                            codeList[i]["c"] = -1
                        i += 1
                        j = i

                        jaString = codeList[i]["p"][0]
                        if jaString.strip():
                            currentGroup.append(jaString)

                        # Make sure not the end of the list.
                        if len(codeList) <= i + 1:
                            break

                # Format String
                if len(currentGroup) > 0:
                    finalJAString = "\n".join(currentGroup)
                    oldjaString = finalJAString

                    # Set Back
                    if not setData:
                        codeList[i]["p"] = [finalJAString]

                    ### \\n<Speaker>
                    regex = r"([\\]+[kKnN][wWcCrRrEe]?[\[<](.*?)[>])"
                    match = re.search(regex, finalJAString)

                    # Set Name
                    if match:
                        nametag = match.group(1)
                        speaker = match.group(2)

                        # Translate Speaker
                        response = getSpeaker(speaker)
                        tledSpeaker = response[0]
                        totalTokens[0] += response[1][0]
                        totalTokens[1] += response[1][1]

                        # Set Nametag and Remove from Final String
                        finalJAString = finalJAString.replace(nametag, "")
                        nametag = nametag.replace(speaker, tledSpeaker)
                        speaker = tledSpeaker

                    # Remove Extra Stuff bad for translation.
                    finalJAString = finalJAString.replace("ﾞ", "")
                    finalJAString = finalJAString.replace("…", "...")
                    finalJAString = finalJAString.replace("。", ".")
                    finalJAString = re.sub(r"(\.{3}\.+)", "...", finalJAString)
                    finalJAString = finalJAString.replace("　", "")
                    finalJAString = finalJAString.replace("「", '"')
                    finalJAString = finalJAString.replace("」", '"')
                    finalJAString = finalJAString.replace("\\,", ',')

                    ### Remove format codes
                    # Furigana
                    rcodeMatch = re.findall(r"([\\]+[r][b]?\[.*?,(.*?)\])", finalJAString)
                    if len(rcodeMatch) > 0:
                        for match in rcodeMatch:
                            finalJAString = finalJAString.replace(match[0], match[1])

                    # Remove any RPGMaker Code at start
                    ffMatch = re.search(
                        r"^((?:[\\]+[^cCnNiIkKvV{}]+\[[\d\w]+\])+)",
                        finalJAString,
                    )
                    if ffMatch != None:
                        finalJAString = finalJAString.replace(ffMatch.group(1), "")
                        nametag = ffMatch.group(1) + nametag

                    # Remove _ABL Codes
                    ffMatch = re.search(r"^(_ABL).*", finalJAString)
                    if ffMatch != None:
                        finalJAString = finalJAString.replace(ffMatch.group(1), "")
                        nametag += ffMatch.group(1)

                    # Center Lines
                    if "\\CL" in finalJAString or "\\ac" in finalJAString:
                        finalJAString = finalJAString.replace("\\CL ", "")
                        finalJAString = finalJAString.replace("\\CL", "")
                        finalJAString = finalJAString.replace("\\ac ", "")
                        finalJAString = finalJAString.replace("\\ac", "")
                        CLFlag = True

                    # Handle Formatting Codes
                    if "\\>" in finalJAString:
                        instantLineFlag = True
                        finalJAString = finalJAString.replace("\\>", "")

                    # Check if Empty
                    if finalJAString == "":
                        if nametag:
                            codeList[j]["p"][0] = codeList[j]["p"][0].replace(match.group(2), tledSpeaker)
                        i += 1
                        continue

                    # Pass 1 (Grabbing Data)
                    if setData:
                        # Remove Textwrap
                        if FIXTEXTWRAP:
                            finalJAString = finalJAString.replace("\n", " ")
                        if "\\px[200]" in finalJAString:
                            finalJAString = finalJAString.replace("\\px[200]", "")

                        # Append
                        if finalJAString != "":
                            if speaker == "" and finalJAString != "":
                                list401.append(finalJAString)
                            elif finalJAString != "":
                                list401.append(f"[{speaker}]: {finalJAString}")
                            else:
                                list401.append(speaker)
                        speaker = ""
                        match = []
                        nametag = ""
                        currentGroup = []
                        syncIndex = i + 1

                        # Keep textHistory list at length maxHistory
                        textHistory.append('"' + finalJAString + '"')
                        if len(textHistory) > maxHistory:
                            textHistory.pop(0)

                    # Pass 2 (Setting Data)
                    else:
                        # Grab Translated String
                        if len(list401) > 0:
                            translatedText = list401[0]

                            # Remove speaker
                            match = re.search(r'(^\[.+?\]\s?[|:]\s?)', translatedText)
                            if match:
                                translatedText = translatedText.replace(match.group(1), "") 

                            # Fix '- '
                            translatedText = translatedText.replace("- ", "-")

                            # Textwrap
                            if FIXTEXTWRAP is True:
                                finalJAString = re.sub(r"\n", " ", finalJAString)
                                finalJAString = finalJAString.replace("<br>", " ")

                            if FIXTEXTWRAP is True and "_ABL" in nametag:
                                translatedText = dazedwrap.wrapText(translatedText, width=100)
                            elif FIXTEXTWRAP is True:
                                translatedText = dazedwrap.wrapText(translatedText, width=WIDTH)

                            # Formatting Code
                            if instantLineFlag:
                                translatedText = translatedText.replace("\n", "\n\\>")
                                translatedText = f"\\>{translatedText}"

                            # BR Flag
                            if BRFLAG is True:
                                translatedText = translatedText.replace("\n", "<br>")

                            # px
                            if "\\px[200]" in nametag:
                                translatedText = translatedText.replace("\\px[200]", "")
                                translatedText = translatedText.replace("\n", "\n\\px[200]")

                            ### Add Var Strings
                            # CL Flag
                            if CLFlag:
                                translatedText = "\\ac " + translatedText
                                translatedText = translatedText.replace("\n", "\n\\ac ")
                                translatedText = re.sub(r"[\\]+?ac\s+", r"\\ac ", translatedText)
                                CLFlag = False

                            # Add Nametag Back In
                            translatedText = nametag + translatedText
                            nametag = ""

                            # Endtag
                            if endtag != "":
                                translatedText = translatedText + endtag
                                endtag = ""

                            # Set Code
                            codeList[j]["c"] = code

                            # Handle 405
                            if codeList[j]["c"] == 405:
                                # 1. Split translatedText by newlines
                                lines = [line for line in translatedText.split('\n') if line.strip() != ""]
                                
                                # 2. Set the first string to codeList[j]["p"]
                                codeList[j]["p"] = [lines[0]]
                                
                                # 3. Make copies for each additional line and insert them
                                for idx, line in enumerate(lines[1:]):
                                    new_item = copy.deepcopy(codeList[j])
                                    new_item["p"] = [line]
                                    codeList.insert(j + idx + 1, new_item)
                                
                                # 4. Update syncIndex to the last modified/added position
                                syncIndex = j + len(lines)

                            # Handle 401
                            else:
                                codeList[j]["p"] = [translatedText]
                                codeList[j]["c"] = code
                                syncIndex = i + 1

                            # Reset
                            speaker = ""
                            match = []
                            currentGroup = []
                            list401.pop(0)

            ## Event Code: 122 [Set Variables]
            if "c" in codeList[i] and codeList[i]["c"] == 122 and CODE122 is True:
                # This is going to be the var being set. (IMPORTANT)
                if codeList[i]["p"][0] not in list(range(0, 2000)):
                    i += 1
                    continue

                jaString = codeList[i]["p"][4]

                # # For Retarded Devs
                # VNameValue = jaString
                # i += 1
                # continue

                # Validate String
                if not isinstance(jaString, str):
                    i += 1
                    continue

                # Definitely don't want to mess with files
                if 'gameV' in jaString or '_' in jaString or '"[' in jaString:
                    i += 1
                    continue

                # # Avoid anything not quoted
                # if '\"' not in jaString:
                #     i += 1
                #     continue

                # Validate Japanese Text
                # if not re.search(LANGREGEX, jaString):
                #     i += 1
                #     continue

                # Set String
                matchedText = None
                if len(re.findall(r"([\'\"\`])", jaString)) >= 2:
                    matchedText = re.search(r"[\'\"\`](.*)[\'\"\`]", jaString)
                    if matchedText and matchedText.group(1).strip():
                        # Remove Textwrap
                        finalJAString = matchedText.group(1).replace("\\n", " ")

                        # Pass 1
                        if setData:
                            if finalJAString != "":
                                list122.append(finalJAString)

                        # Pass 2
                        else:
                            if len(list122) > 0:
                                # Grab and Replace
                                translatedText = list122[0]
                                translatedText = jaString.replace(jaString, translatedText)

                                # Remove characters that may break scripts
                                charList = ['"', "\\n"]
                                for char in charList:
                                    translatedText = translatedText.replace(char, "")

                                # Force 4 Escapes
                                translatedText = re.sub(r'(?<![\\])([\\]{1})(?=\w)', r'\\\\', translatedText)

                                # Textwrap
                                translatedText = dazedwrap.wrapText(translatedText, width=LISTWIDTH)
                                translatedText = translatedText.replace("\n", "\\n")

                                # Set
                                codeList[i]["p"][4] = f"`{translatedText}`"
                                list122.pop(0)

            ## Event Code: 357 [Picture Text] [Optional]
            if "c" in codeList[i] and codeList[i]["c"] == 357 and CODE357 is True:
                headerString = codeList[i]["p"][0]
                argVar = None

                def translatePlugins(argVar, font):
                    ### Message Text First
                    if argVar in codeList[i]["p"][3]:
                        acExist = False
                        jaString = codeList[i]["p"][3][argVar]

                        # Check ac
                        if "\\ac" in jaString:
                            acExist = True
                        else:
                            acExist = False

                        # If there isn't any Japanese in the text just skip
                        # if not re.search(r'[一-龠]+|[ぁ-ゔ]+|[ァ-ヴー]+', jaString):
                        #     i += 1
                        #     continue

                        # Remove any textwrap & TL
                        jaString = jaString.replace("\\n", " ")
                        if acExist:
                            jaString = jaString.replace("\\ac ", " ")
                            jaString = jaString.replace("\\ac", "")

                        # Pass 1
                        if setData:
                            list357.append(jaString)

                        # Pass 2
                        else:
                            if len(list357) > 0:
                                # Grab and Replace
                                translatedText = list357[0]
                                translatedText = jaString.replace(jaString, translatedText)

                                # Remove characters that may break scripts
                                charList = ['"', "\\n"]
                                for char in charList:
                                    translatedText = translatedText.replace(char, "")

                                # Textwrap
                                # translatedText = dazedwrap.wrapText(translatedText, 80)
                                # translatedText = translatedText.replace("\n", "\\n")
                                # translatedText = re.sub(r"[\\]+c", r"\\\\c", translatedText)
                                translatedText = re.sub(r"[\\]+\*item", r"\\\\*item", translatedText)

                                # Center Text
                                if acExist:
                                    translatedText = f'\\ac {translatedText.replace('\n', '\n\\ac ')}'

                                # Check and Set Font
                                if "fontSize" in codeList[i]["p"][3]:
                                    if font:
                                        codeList[i]["p"][3]["fontSize"] = font

                                # Set
                                codeList[i]["p"][3][argVar] = f"{translatedText}"
                                list357.pop(0)

                # Map Plugins
                headerMappings = {
                    "LL_InfoPopupWIndow": ("messageText", None),
                    "QuestSystem": ("DetailNote", None),
                    "BalloonInBattle": ("text", None),
                    "MNKR_CommonPopupCoreMZ": ("text", None),
                    "DestinationWindow": ("destination", None),
                    "_TMLogWindowMZ": ("text", None),
                    "TorigoyaMZ_NotifyMessage": ("message", None),
                    "SoR_GabWindow": ("arg1", None),
                    "DarkPlasma_CharacterText": ("text", None),
                    "DTextPicture": ("text", None),
                    "TextPicture": ("text", None),
                    "TRP_SkitMZ": ("name", None),
                }

                for key, (argVar, font) in headerMappings.items():
                    if key in headerString:
                        translatePlugins(argVar, font)

                if headerString == "LL_GalgeChoiceWindow":
                    ### Message Text First
                    jaString = codeList[i]["p"][3]["messageText"]

                    # Remove any textwrap & TL
                    jaString = re.sub(r"\n", " ", jaString)
                    response = translateAI(jaString, "", False)
                    translatedText = response[0]
                    totalTokens[0] += response[1][0]
                    totalTokens[1] += response[1][1]

                    # Textwrap & Set
                    translatedText = dazedwrap.wrapText(translatedText, width=WIDTH)
                    codeList[i]["p"][3]["messageText"] = translatedText

                    ### Choices
                    jaString = codeList[i]["p"][3]["choices"]
                    matchList = re.findall(r'"label[\\]*":[\\]*"(.*?)[\\]', jaString)
                    if matchList != None:
                        # Translate
                        question = codeList[i]["p"][3]["messageText"]
                        response = translateAI(
                            matchList,
                            f"Previous text for context: {question}\n",
                            True,
                        )
                        totalTokens[0] += response[1][0]
                        totalTokens[1] += response[1][1]
                        translatedText = jaString

                        # Replace Strings
                        for j in range(len(matchList)):
                            translatedText = translatedText.replace(matchList[j], response[0][j])

                        # Set Data
                        codeList[i]["p"][3]["choices"] = translatedText

            ## Event Code: 657 [Picture Text] [Optional]
            if "c" in codeList[i] and codeList[i]["c"] == 657 and CODE657 is True:
                if "text" in codeList[i]["p"][0]:
                    jaString = codeList[i]["p"][0]
                    if not isinstance(jaString, str):
                        i += 1
                        continue

                    # Definitely don't want to mess with files
                    if "_" in jaString:
                        i += 1
                        continue

                    # If there isn't any Japanese in the text just skip
                    if not re.search(LANGREGEX, jaString):
                        i += 1
                        continue

                    # Remove outside text
                    startString = re.search(r"^[^一-龠ぁ-ゔァ-ヴー\<\>【】\\]+", jaString)
                    jaString = re.sub(r"^[^一-龠ぁ-ゔァ-ヴー\<\>【】\\]+", "", jaString)
                    endString = re.search(r"[^一-龠ぁ-ゔァ-ヴー\<\>【】。！？\\]+$", jaString)
                    jaString = re.sub(r"[^一-龠ぁ-ゔァ-ヴー\<\>【】。！？\\]+$", "", jaString)
                    if startString is None:
                        startString = ""
                    else:
                        startString = startString.group()
                    if endString is None:
                        endString = ""
                    else:
                        endString = endString.group()

                    # Remove any textwrap
                    jaString = re.sub(r"\n", " ", jaString)

                    # Translate
                    response = translateAI(jaString, "", True)
                    totalTokens[0] += response[1][0]
                    totalTokens[1] += response[1][1]
                    translatedText = response[0]

                    # Remove characters that may break scripts
                    charList = [".", '"', "'"]
                    for char in charList:
                        translatedText = translatedText.replace(char, "")

                    # Textwrap
                    translatedText = dazedwrap.wrapText(translatedText, width=WIDTH)
                    translatedText = startString + translatedText + endString

                    # Set Data
                    codeList[i]["p"][0] = translatedText

            ## Event Code: 101 [Name] [Optional]
            if "c" in codeList[i] and codeList[i]["c"] == 101 and CODE101 is True:
                isVar = False

                # Grab String
                jaString = ""
                if len(codeList[i]["p"]) > 4:
                    jaString = codeList[i]["p"][4]
                # Check for Var
                elif len(codeList[i]["p"]) > 0:
                    jaString = codeList[i]["p"][0]
                    isVar = True
                if not isinstance(jaString, str):
                    i += 1
                    continue

                # Force Speaker using var
                if "memerisu" in jaString.lower():
                    speaker = "Memerisu"
                    i += 1
                    continue
                elif "thina" in jaString.lower():
                    speaker = "Tina"
                    i += 1
                    continue
                elif "\\ap" in jaString:
                    speaker = re.search(r"[\\]+AP\[(.*?)\]", jaString).group(1)
                    i += 1
                    continue

                # Get Speaker
                match = re.search(r"^(?:[\\]+[cC]\[\d+?\])?([\w\s]+)", jaString)
                if match:
                    jaString = match.group(1)
                    response = getSpeaker(jaString)
                    totalTokens[0] += response[1][0]
                    totalTokens[1] += response[1][1]
                    speaker = response[0]

                    # Validate Speaker is not empty
                    if len(speaker) > 0:
                        if isVar == False:
                            codeList[i]["p"][4] = codeList[i]["p"][4].replace(jaString, speaker)
                            i += 1
                            continue
                        else:
                            codeList[i]["p"][0] = codeList[i]["p"][0].replace(jaString, speaker)
                            isVar = False
                            i += 1
                            continue
                    else:
                        speaker = ""
                elif FACENAME101:
                    faceName = codeList[i]["p"][0]
                    if faceName == "Actor1_1":
                        speaker = "Sakura"
                    if faceName == "Actor2_1":
                        speaker = "Suzune"
                    if faceName == "Actor3_1":
                        speaker = "Kaji"
                    if faceName == "Actor4_1":
                        speaker = "Kirari"
                    if faceName == "Actor5_1":
                        speaker = "Onsen"
                    if faceName == "Actor6_1":
                        speaker = "Gufu"
                    if faceName == "Actor7_1":
                        speaker = "Kahimeru"
                    if faceName == "Actor10_1":
                        speaker = "Miuma"
                    if faceName == "Actor11_1":
                        speaker = "Nurari"
                    if faceName == "Actor12_1":
                        speaker = "Kokotsuzumi"

            ## Event Code: 355 or 655 Scripts [Optional]
            if "c" in codeList[i] and (codeList[i]["c"] == 355 or codeList[i]["c"] == 655) and CODE355655 is True:
                jaString = codeList[i]["p"][0]
                
                patterns = {
                    "テキスト-": (r"テキスト-(.+)")
                    # "var text": (r"var\stext\d+\s=\s\"(.+)\""),
                    # "logtxt = ": (r"logtxt\s=\s'(.+)'" 
                    # ".setNickname": (r'.setNickname\(\\?"(.+?)\\?"\)'
                    # "_subject=": (r'_subject=(.+?)_'
                    # "text =": (r"text\s*=\s*'(.+[^\\])'"),
                    # "ex_a_name": (r'ex_a_name\(\d+,"(.+)"\)'),
                    # "gameVariables.setValue": (r":\$gameVariables.setValue\(\d+,'(.+)'\)"),
                    # "BattleManager._logWindow.push('addText'": (r"BattleManager._logWindow.push\('addText',\s'(.+)'\)"),
                }

                for key, (regex) in patterns.items():
                    if key in jaString:
                        match = re.search(regex, jaString)
                        if match:
                            # Pass 1
                            if setData:
                                list355655.append(match.group(1))

                            # Pass 2
                            else:
                                # Grab and Replace
                                translatedText = list355655[0]
                                list355655.pop(0)

                                # Only escape if not already escaped 
                                matchList = re.findall(r"(.+)'\s*[$+].+?'(.+)", translatedText)
                                if matchList:
                                    for string in matchList[0]:
                                        escapedMatch = re.sub(r"(?<!\\)'", r"\\'", string)
                                        translatedText = translatedText.replace(string, escapedMatch)
                                else:
                                    translatedText = re.sub(r"(?<!\\)'", r"\\'", translatedText)
                                translatedText = re.sub(r'(?<!\\)"', r'"', translatedText)
                                # Double backslashes before control codes
                                translatedText = re.sub(r'(?<![\\])([\\]{1})(?=\w)', r'\\\\', translatedText)

                                # Set
                                codeList[i]["p"][0] = jaString.replace(match.group(1), translatedText)
                        break

            ## Event Code: 408 (Script)
            if "c" in codeList[i] and (codeList[i]["c"] == 408) and CODE408 is True:
                # Save starting index
                j = i
                jaString = codeList[i]["p"][0] if len(codeList[i]["p"]) > 0 else ""
                
                # Join consecutive 408 codes
                combinedText = jaString
                if len(codeList) > i + 1:
                    while i + 1 < len(codeList) and "c" in codeList[i + 1] and codeList[i + 1]["c"] == 408:
                        i += 1
                        if len(codeList[i]["p"]) > 0:
                            combinedText += " " + codeList[i]["p"][0]
                        # Mark as -1 only in Pass 2
                        if not setData:
                            codeList[i]["p"] = []
                            codeList[i]["c"] = -1

                # Pass 1
                if setData:
                    if combinedText.strip():  # Only add non-empty text
                        list408.append(combinedText)
                
                # Pass 2
                else:
                    if len(list408) > 0:
                        translatedText = list408[0]
                        list408.pop(0)

                        # Textwrap
                        translatedText = dazedwrap.wrapText(translatedText, width=WIDTH)

                        # Set Data - ensure list has at least one element
                        if len(codeList[j]["p"]) == 0:
                            codeList[j]["p"] = [""]
                        codeList[j]["p"][0] = f"{translatedText}"

            ## Event Code: 108 (Script)
            if "c" in codeList[i] and (codeList[i]["c"] == 108) and CODE108 is True:
                jaString = codeList[i]["p"][0]

                # If there isn't any Japanese in the text just skip
                if not re.search(LANGREGEX, jaString):
                    i += 1
                    continue

                # Translate
                if "info:" in jaString:
                    regex = r"info:(.*)"
                elif "ActiveMessage:" in jaString:
                    regex = r"<ActiveMessage:(.*)>?"
                elif "event_text" in jaString:
                    regex = r"event_text\s*:\s*(.*)"
                elif "Menu Name" in jaString:
                    regex = r"Menu\sName\s*:\s*(.*)>"
                else:
                    i += 1
                    continue

                # Need to remove outside code and put it back later
                match = re.search(regex, jaString)
                if match:
                    # Pass 1
                    if setData:
                        list108.append(match.group(1))

                        # # Grab Next
                        # j = i
                        # while codeList[j + 1]["c"] == 408:
                        #     j += 1
                        #     list108[0] = list108[0] + codeList[j]["p"][0].replace(">", "")
                        #     codeList[j]["p"][0] = ""
                        #     list108[0] = list108[0].replace("\n", " ")

                    # Pass 2
                    else:
                        # Grab and Replace
                        translatedText = list108[0]
                        list108.pop(0)

                        # Textwrap
                        # if codeList[i + 1]["c"] == 408:
                        #     translatedText = dazedwrap.wrapText(translatedText, WIDTH)

                        # Remove characters that may break scripts
                        charList = ['"']
                        for char in charList:
                            translatedText = translatedText.replace(char, "")
                        translatedText = translatedText.replace('"', '"')
                        translatedText = translatedText.replace(" ", "_")
                        translatedText = jaString.replace(match.group(1), translatedText)

                        # Add >
                        if "ActiveMessage" in translatedText and ">" not in translatedText:
                            translatedText = translatedText + ">"

                        # Set Data
                        codeList[i]["p"][0] = translatedText

            ## Event Code: 356
            if "c" in codeList[i] and codeList[i]["c"] == 356 and CODE356 is True:
                jaString = codeList[i]["p"][0]
                oldjaString = jaString

                # Grab Speaker
                if "Tachie showName" in jaString:
                    matchList = re.findall(r"Tachie showName (.+)", jaString)
                    if len(matchList) > 0:
                        # Translate
                        response = translateAI(
                            matchList[0],
                            "Reply with the " + LANGUAGE + " translation of the NPC name.",
                            False,
                        )
                        translatedText = response[0]
                        totalTokens[0] += response[1][0]
                        totalTokens[1] += response[1][1]

                        # Set Text
                        speaker = translatedText
                        speaker = speaker.replace(" ", " ")
                        codeList[i]["p"][0] = jaString.replace(matchList[0], speaker)
                    i += 1
                    continue

                # Want to translate this script
                if "D_TEXT " in jaString:
                    regex = r"D_TEXT\s(.+)\s?.*"
                elif "ShowInfo" in jaString:
                    regex = r"ShowInfo\s(.*)"
                elif "PushGab" in jaString:
                    regex = r"PushGab\s(.*)"
                elif "addLog" in jaString:
                    regex = r"addLog\s(.*)"
                elif "DW_" in jaString:
                    regex = r"DW_.*?\s(.*)"
                elif "CommonPopup" in jaString:
                    regex = r"CommonPopup\sadd\stext:(.*?)[\\]+}"
                elif "AddCustomChoice" in jaString:
                    regex = r"AddCustomChoice\s\d+\s(.+)\s\d"
                else:
                    regex = r""

                # Remove any textwrap
                jaString = re.sub(r"\n", "_", jaString)

                # Capture Arguments and text
                textMatch = re.search(regex, jaString)
                if textMatch and textMatch.group(0) != "":
                    text = textMatch.group(1)

                    # Pass 1
                    if setData:
                        text = text.replace("_", " ")
                        list356.append(text)

                    # Pass 2
                    else:
                        if len(list356) > 0:
                            # Grab
                            translatedText = list356[0]

                            # Remove characters that may break scripts
                            charList = [".", '"']
                            for char in charList:
                                translatedText = translatedText.replace(char, "")

                            # Cant have spaces?
                            translatedText = translatedText.replace(" ", "_")
                            translatedText = translatedText.replace("__", "_")

                            # Put Args Back
                            translatedText = jaString.replace(text, translatedText)

                            # Set Data
                            codeList[i]["p"][0] = translatedText
                            list356.pop(0)

                if "namePop" in jaString:
                    matchList = re.findall(r"<namePop:(.*?)\s?[\d-]+?", jaString)
                    if len(matchList) > 0:
                        # Translate
                        text = matchList[0]
                        response = translateAI(text, "Reply with the " + LANGUAGE + " Translation", False)
                        translatedText = response[0]
                        totalTokens[0] += response[1][0]
                        totalTokens[1] += response[1][1]

                        # Set Data
                        translatedText = jaString.replace(text, translatedText)
                        codeList[i]["p"][0] = translatedText

                if "LL_InfoPopupWIndowMV" in jaString:
                    matchList = re.findall(r"LL_InfoPopupWIndowMV\sshowWindow\s(.+?) .+", jaString)
                    if len(matchList) > 0:
                        # Translate
                        text = matchList[0]
                        response = translateAI(text, "Reply with the " + LANGUAGE + " Translation", False)
                        translatedText = response[0]
                        totalTokens[0] += response[1][0]
                        totalTokens[1] += response[1][1]

                        # Set Data
                        translatedText = translatedText.replace(" ", "_")
                        translatedText = jaString.replace(text, translatedText)
                        codeList[i]["p"][0] = translatedText

                if "OriginMenuStatus SetParam" in jaString:
                    matchList = re.findall(r"OriginMenuStatus\sSetParam\sparam[\d]\s(.*)", jaString)
                    if len(matchList) > 0:
                        # Translate
                        text = matchList[0]
                        response = translateAI(text, "Reply with the " + LANGUAGE + " Translation", False)
                        translatedText = response[0]
                        totalTokens[0] += response[1][0]
                        totalTokens[1] += response[1][1]

                        # Set Data
                        translatedText = translatedText.replace(" ", "_")
                        translatedText = jaString.replace(text, translatedText)
                        codeList[i]["p"][0] = translatedText

                # LL_GalgeChoiceWindowMV Message
                if "LL_GalgeChoiceWindowMV setMessageText" in jaString:
                    ### Message Text First
                    match = re.search(r"LL_GalgeChoiceWindowMV setMessageText (.+)", jaString)
                    if match:
                        jaString = match.group(1)

                        # Remove any textwrap & TL
                        jaString = re.sub(r"\n", " ", jaString)
                        response = translateAI(jaString, "", False)
                        translatedText = response[0]
                        totalTokens[0] += response[1][0]
                        totalTokens[1] += response[1][1]

                        # Textwrap & Replace Whitespace
                        translatedText = dazedwrap.wrapText(translatedText, width=WIDTH)
                        translatedText = translatedText.replace(" ", "_")

                        # Replace and Set
                        translatedText = match.group(0).replace(match.group(1), translatedText)
                        codeList[i]["p"][0] = translatedText

                # LL_GalgeChoiceWindowMV Choices
                if "LL_GalgeChoiceWindowMV setChoices":
                    match = re.search(r"LL_GalgeChoiceWindowMV setChoices (.+)", jaString)
                    if match:
                        jaString = match.group(1)
                        choiceList = jaString.split(",")

                        # Translate
                        question = translatedText
                        response = translateAI(
                            choiceList,
                            f"Previous text for context: {question}\n",
                            True,
                        )
                        totalTokens[0] += response[1][0]
                        totalTokens[1] += response[1][1]
                        choiceListTL = response[0]
                        translatedText = match.group(0)

                        # Replace Strings
                        for j in range(len(choiceListTL)):
                            choiceListTL[j] = choiceListTL[j].replace(" ", "_")
                            translatedText = translatedText.replace(choiceList[j], choiceListTL[j])

                        # Set Data
                        codeList[i]["p"][0] = translatedText

            ### Event Code: 102 Show Choice
            if "c" in codeList[i] and codeList[i]["c"] == 102 and CODE102 is True:
                choiceList = []
                varList = []
                for choice in range(len(codeList[i]["p"][0])):
                    jaString = codeList[i]["p"][0][choice]
                    jaString = jaString.replace(" 。", ".")

                    # Avoid Empty Strings
                    if jaString == "":
                        continue

                    # If and En Statements
                    ifVar = ""
                    ifList = re.findall(r"([ei][nf]\(.+?\)\)?\)?)", jaString)
                    if len(ifList) != 0:
                        for var in ifList:
                            jaString = jaString.replace(var, "")
                            ifVar += var
                    varList.append(ifVar)

                    # Append to List
                    choiceList.append(jaString)

                # Translate
                if len(textHistory) > 0:
                    response = translateAI(
                        choiceList,
                        f"Reply with the English translation of the dialogue choice.\n\nPrevious text for context: {str(textHistory)}\n",
                        True,
                    )
                    translatedTextList = response[0]
                    totalTokens[0] += response[1][0]
                    totalTokens[1] += response[1][1]
                else:
                    response = translateAI(choiceList, "Reply with the English translation of the dialogue choice.", True)
                    translatedTextList = response[0]
                    totalTokens[0] += response[1][0]
                    totalTokens[1] += response[1][1]

                # Check Mismatch
                if len(translatedTextList) == len(choiceList):
                    for choice in range(len(codeList[i]["p"][0])):
                        jaString = codeList[i]["p"][0][choice]
                        jaString = jaString.replace(" 。", ".")

                        # Avoid Empty Strings
                        if jaString == "":
                            continue

                        translatedText = translatedTextList[choice]

                        # Set Data
                        totalTokens[0] += response[1][0]
                        totalTokens[1] += response[1][1]
                        if translatedText != "":
                            translatedText = varList[choice] + translatedText[0].upper() + translatedText[1:]
                        else:
                            translatedText = varList[choice] + translatedText
                        codeList[i]["p"][0][choice] = translatedText
                else:
                    if filename not in MISMATCH:
                        MISMATCH.append(filename)

            ### Event Code: 111 Script
            if "c" in codeList[i] and codeList[i]["c"] == 111 and CODE111 is True:
                for j in range(len(codeList[i]["p"])):
                    jaString = codeList[i]["p"][j]

                    # Check if String
                    if not isinstance(jaString, str):
                        i += 1
                        continue

                    # Only TL the Game Variable
                    if "$gameVariables" not in jaString:
                        i += 1
                        continue

                    # This is going to be the var being set. (IMPORTANT)
                    if "1045" not in jaString:
                        i += 1
                        continue

                    # Need to remove outside code and put it back later
                    matchList = re.findall(r"'(.*?)'", jaString)

                    for match in matchList:
                        response = translateAI(match, "", False)
                        translatedText = response[0]
                        totalTokens[0] += response[1][0]
                        totalTokens[1] += response[1][1]

                        # Remove characters that may break scripts
                        charList = [".", '"', "'", "\\n"]
                        for char in charList:
                            translatedText = translatedText.replace(char, "")

                        jaString = jaString.replace(match, translatedText)

                    # Set Data
                    translatedText = jaString
                    codeList[i]["p"][j] = translatedText

            ### Event Code: 320 Set Variable
            if "c" in codeList[i] and codeList[i]["c"] == 320 and CODE320 is True:
                jaString = codeList[i]["p"][1]
                if not isinstance(jaString, str):
                    i += 1
                    continue

                # Definitely don't want to mess with files
                if "■" in jaString or "_" in jaString:
                    i += 1
                    continue

                # If there isn't any Japanese in the text just skip
                if not re.search(LANGREGEX, jaString):
                    i += 1
                    continue

                # Translate
                response = getSpeaker(jaString)
                translatedText = response[0]
                totalTokens[0] += response[1][0]
                totalTokens[1] += response[1][1]

                # Remove characters that may break scripts
                charList = [".", '"', "'", "\\n"]
                for char in charList:
                    translatedText = translatedText.replace(char, "")

                # Set Data
                codeList[i]["p"][1] = translatedText

            # Iterate
            else:
                i += 1

        # EOF
        list401TL = []
        list408TL = []
        list122TL = []
        list356TL = []
        list357TL = []
        list355655TL = []
        list108TL = []
        PBAR = pbar

        # 401
        if len(list401) > 0:
            response = translateAI(list401, "", True)
            list401TL = response[0]
            totalTokens[0] += response[1][0]
            totalTokens[1] += response[1][1]
            if len(list401TL) != len(list401):
                with LOCK:
                    if filename not in MISMATCH:
                        MISMATCH.append(filename)

        # 122
        if len(list122) > 0:
            response = translateAI(list122, "Keep your translation as brief as possible", True)
            list122TL = response[0]
            totalTokens[0] += response[1][0]
            totalTokens[1] += response[1][1]
            if len(list122TL) != len(list122):
                with LOCK:
                    if filename not in MISMATCH:
                        MISMATCH.append(filename)

        # 355/655
        if len(list355655) > 0:
            response = translateAI(list355655, textHistory, True)
            list355655TL = response[0]
            totalTokens[0] += response[1][0]
            totalTokens[1] += response[1][1]
            if len(list355655TL) != len(list355655):
                with LOCK:
                    if filename not in MISMATCH:
                        MISMATCH.append(filename)

        # 108
        if len(list108) > 0:
            response = translateAI(list108, textHistory, True)
            list108TL = response[0]
            totalTokens[0] += response[1][0]
            totalTokens[1] += response[1][1]
            if len(list108TL) != len(list108):
                with LOCK:
                    if filename not in MISMATCH:
                        MISMATCH.append(filename)

        # 356
        if len(list356) > 0:
            response = translateAI(list356, textHistory, True)
            list356TL = response[0]
            totalTokens[0] += response[1][0]
            totalTokens[1] += response[1][1]
            if len(list356TL) != len(list356):
                with LOCK:
                    if filename not in MISMATCH:
                        MISMATCH.append(filename)

        # 357
        if len(list357) > 0:
            response = translateAI(list357, textHistory, True)
            list357TL = response[0]
            totalTokens[0] += response[1][0]
            totalTokens[1] += response[1][1]
            if len(list357TL) != len(list357):
                with LOCK:
                    if filename not in MISMATCH:
                        MISMATCH.append(filename)

        # 408
        if len(list408) > 0:
            response = translateAI(list408, "", True)
            list408TL = response[0]
            totalTokens[0] += response[1][0]
            totalTokens[1] += response[1][1]
            if len(list408TL) != len(list408):
                with LOCK:
                    if filename not in MISMATCH:
                        MISMATCH.append(filename)

        # Start Pass 2
        if setData:
            searchCodes(
                page,
                pbar,
                [list401TL, list122TL, list355655TL, list108TL, list356TL, list357TL, list408TL],
                filename,
            )

        # Delete all -1 codes
        codeListFinal = []
        for i in range(len(codeList)):
            if "c" in codeList[i] and codeList[i]["c"] != -1:
                codeListFinal.append(codeList[i])

        # Normal Format
        if "list" in page:
            page["list"] = codeListFinal

        # Special Format (Scenario)
        else:
            page[:] = codeListFinal
    except IndexError as e:
        traceback.print_exc()
    except Exception as e:
        traceback.print_exc()

    return totalTokens


def searchSS(state, pbar):
    totalTokens = [0, 0]

    # Name
    nameResponse = (
        translateAI(
            state["name"],
            "Reply with only the " + LANGUAGE + " translation of the RPG Skill name.",
            False,
        )
        if "name" in state
        else ""
    )

    # Description
    descriptionResponse = (
        translateAI(
            state["description"],
            "Reply with only the " + LANGUAGE + " translation of the description.",
            False,
        )
        if "description" in state
        else ""
    )

    # Messages
    message1Response = ""
    message4Response = ""
    message2Response = ""
    message3Response = ""

    if "message1" in state:
        if len(state["message1"]) > 0 and state["message1"][0] in [
            "は",
            "を",
            "の",
            "に",
            "が",
        ]:
            message1Response = translateAI(
                "Taro" + state["message1"],
                "reply with only the gender neutral "
                + LANGUAGE
                + " translation of the action log. Always start the sentence with Taro. For example,\
Translate 'Taroを倒した！' as 'Taro was defeated!'",
                False,
            )
        else:
            message1Response = translateAI(
                state["message1"],
                "reply with only the gender neutral " + LANGUAGE + " translation",
                False,
            )

    if "message2" in state:
        if len(state["message2"]) > 0 and state["message2"][0] in [
            "は",
            "を",
            "の",
            "に",
            "が",
        ]:
            message2Response = translateAI(
                "Taro" + state["message2"],
                "reply with only the gender neutral "
                + LANGUAGE
                + " translation of the action log. Always start the sentence with Taro. For example,\
Translate 'Taroを倒した！' as 'Taro was defeated!'",
                False,
            )
        else:
            message2Response = translateAI(
                state["message2"],
                "reply with only the gender neutral " + LANGUAGE + " translation",
                False,
            )

    if "message3" in state:
        if len(state["message3"]) > 0 and state["message3"][0] in [
            "は",
            "を",
            "の",
            "に",
            "が",
        ]:
            message3Response = translateAI(
                "Taro" + state["message3"],
                "reply with only the gender neutral "
                + LANGUAGE
                + " translation of the action log. Always start the sentence with Taro. For example,\
Translate 'Taroを倒した！' as 'Taro was defeated!'",
                False,
            )
        else:
            message3Response = translateAI(
                state["message3"],
                "reply with only the gender neutral " + LANGUAGE + " translation",
                False,
            )

    if "message4" in state:
        if len(state["message4"]) > 0 and state["message4"][0] in [
            "は",
            "を",
            "の",
            "に",
            "が",
        ]:
            message4Response = translateAI(
                "Taro" + state["message4"],
                "reply with only the gender neutral "
                + LANGUAGE
                + " translation of the action log. Always start the sentence with Taro. For example,\
Translate 'Taroを倒した！' as 'Taro was defeated!'",
                False,
            )
        else:
            message4Response = translateAI(
                state["message4"],
                "reply with only the gender neutral " + LANGUAGE + " translation",
                False,
            )

    # Translate State Notes
    if "help" in state["note"]:
        noteResponse = translateNote(state, r"<help:([^>]*)>")
        totalTokens[0] += noteResponse[0]
        totalTokens[1] += noteResponse[1]
    if "STATE_HELP" in state["note"]:
        noteResponse = translateNote(state, r"<STATE_HELP>\n(.*)\n")
        totalTokens[0] += noteResponse[0]
        totalTokens[1] += noteResponse[1]
    if "ShowHoverState" in state["note"]:
        noteResponse = translateNote(state, r"<ShowHoverState:\s?(.+?)>")
        totalTokens[0] += noteResponse[0]
        totalTokens[1] += noteResponse[1]
    if "<Detail" in state["note"]:
        noteResponse = translateNote(state, r"<Detail:\s?(.+?)>")
        totalTokens[0] += noteResponse[0]
        totalTokens[1] += noteResponse[1]

    # Count totalTokens
    totalTokens[0] += nameResponse[1][0] if nameResponse != "" else 0
    totalTokens[1] += nameResponse[1][1] if nameResponse != "" else 0
    totalTokens[0] += descriptionResponse[1][0] if descriptionResponse != "" else 0
    totalTokens[1] += descriptionResponse[1][1] if descriptionResponse != "" else 0
    totalTokens[0] += message1Response[1][0] if message1Response != "" else 0
    totalTokens[1] += message1Response[1][1] if message1Response != "" else 0
    totalTokens[0] += message2Response[1][0] if message2Response != "" else 0
    totalTokens[1] += message2Response[1][1] if message2Response != "" else 0
    totalTokens[0] += message3Response[1][0] if message3Response != "" else 0
    totalTokens[1] += message3Response[1][1] if message3Response != "" else 0
    totalTokens[0] += message4Response[1][0] if message4Response != "" else 0
    totalTokens[1] += message4Response[1][1] if message4Response != "" else 0

    # Set Data
    if "name" in state:
        state["name"] = nameResponse[0].replace('"', "")
    if "description" in state:
        # Textwrap
        translatedText = descriptionResponse[0]
        translatedText = dazedwrap.wrapText(translatedText, width=LISTWIDTH)
        state["description"] = translatedText.replace('"', "")
    if "message1" in state:
        state["message1"] = message1Response[0].replace('"', "").replace("Taro", "")
    if "message2" in state:
        state["message2"] = message2Response[0].replace('"', "").replace("Taro", "")
    if "message3" in state:
        state["message3"] = message3Response[0].replace('"', "").replace("Taro", "")
    if "message4" in state:
        state["message4"] = message4Response[0].replace('"', "").replace("Taro", "")

    return totalTokens


def searchSystem(data, pbar):
    totalTokens = [0, 0]
    context = "Reply with only the " + LANGUAGE + ' translation of the UI textbox."'

    # Title (handle both 'gameTitle' and 'game_title', skip if missing)
    game_title = data.get("gameTitle")
    if game_title is None:
        game_title = data.get("game_title")
    if game_title is not None:
        response = translateAI(
            game_title,
            " Reply with the " + LANGUAGE + " translation of the game title name",
            False,
        )
        totalTokens[0] += response[1][0]
        totalTokens[1] += response[1][1]
        # Write back to whichever key existed
        if "gameTitle" in data:
            data["gameTitle"] = response[0].strip(".")
        else:
            data["game_title"] = response[0].strip(".")

    # Terms
    for term in data["terms"]:
        if term != "messages":
            termList = data["terms"][term]
            for i in range(len(termList)):  # Last item is a messages object
                if termList[i] is not None:
                    response = translateAI(termList[i], context, False)
                    totalTokens[0] += response[1][0]
                    totalTokens[1] += response[1][1]
                    termList[i] = response[0].replace('"', "").strip()

    # Armor Types
    for i in range(len(data["armor_types"])):
        response = translateAI(
            data["armor_types"][i],
            "Reply with only the " + LANGUAGE + " translation of the armor type",
            False,
        )
        totalTokens[0] += response[1][0]
        totalTokens[1] += response[1][1]
        data["armor_types"][i] = response[0].replace('"', "").strip()

    # Skill Types
    for i in range(len(data["skill_types"])):
        response = translateAI(
            data["skill_types"][i],
            "Reply with only the " + LANGUAGE + " translation",
            False,
        )
        totalTokens[0] += response[1][0]
        totalTokens[1] += response[1][1]
        data["skill_types"][i] = response[0].replace('"', "").strip()

    # Equip Types
    for i in range(len(data["weapon_types"])):
        response = translateAI(
            data["weapon_types"][i],
            "Reply with only the " + LANGUAGE + " translation of the equipment type. No disclaimers.",
            False,
        )
        totalTokens[0] += response[1][0]
        totalTokens[1] += response[1][1]
        data["weapon_types"][i] = response[0].replace('"', "").strip()

    # # Variables (Optional ususally)
    # for i in range(len(data['variables'])):
    #     response = translateAI(data['variables'][i], 'Reply with only the '+ LANGUAGE +' translation of the title', False)
    #     totalTokens[0] += response[1][0]
    #     totalTokens[1] += response[1][1]
    #     data['variables'][i] = response[0].replace('\"', '').strip()

    # Messages and lists (handle both string and list values, log as Line1, Line2, ...)
    messages = data["terms"]
    for key, value in messages.items():
        if isinstance(value, list):
            new_list = []
            for idx, item in enumerate(value):
                if isinstance(item, str):
                    response = translateAI(
                        item,
                        f"Reply with only the {LANGUAGE} translation of the battle text.\nTranslate '常時ダッシュ' as 'Always Dash'\nTranslate '次の%1まで' as Next %1.",
                        False,
                    )
                    translatedText = response[0]
                    charList = [".", '"', "\\n"]
                    for char in charList:
                        translatedText = translatedText.replace(char, "")
                    totalTokens[0] += response[1][0]
                    totalTokens[1] += response[1][1]
                    new_list.append(translatedText)
                else:
                    new_list.append(item)
            # For logging: create Line1, Line2, ... keys for this list
            messages[key] = new_list
        elif isinstance(value, str):
            response = translateAI(
                value,
                f"Reply with only the {LANGUAGE} translation of the battle text.\nTranslate '常時ダッシュ' as 'Always Dash'\nTranslate '次の%1まで' as Next %1.",
                False,
            )
            translatedText = response[0]
            charList = [".", '"', "\\n"]
            for char in charList:
                translatedText = translatedText.replace(char, "")
            totalTokens[0] += response[1][0]
            totalTokens[1] += response[1][1]
            messages[key] = translatedText
        else:
            messages[key] = value

    return totalTokens

# Save some money and enter the character before translation
def getSpeaker(speaker):
    match speaker:
        case "ファイン":
            return ["Fine", [0, 0]]
        case "":
            return ["", [0, 0]]
        case _:
            # Find Speaker
            for i in range(len(NAMESLIST)):
                if speaker == NAMESLIST[i][0]:
                    return [NAMESLIST[i][1], [0, 0]]

            # Translate and Store Speaker
            response = translateAI(
                f"{speaker}",
                "Reply with the " + LANGUAGE + " translation of the NPC name.",
                False,
            )
            response[0] = response[0].title()
            response[0] = response[0].replace("'S", "'s")
            response[0] = response[0].replace("Speaker: ", "")

            # Retry if name doesn't translate for some reason
            if re.search(r"([a-zA-Z？?])", response[0]) == None:
                response = translateAI(
                    f"{speaker}",
                    "Reply with the " + LANGUAGE + " translation of the NPC name.",
                    False,
                )
                response[0] = response[0].title()
                response[0] = response[0].replace("'S", "'s")

            speakerList = [speaker, response[0]]
            NAMESLIST.append(speakerList)
            return response
    return [speaker, [0, 0]]

def translateAI(text, history, fullPromptFlag):
    """
    Legacy wrapper function for the new shared translation utility.
    This maintains compatibility with existing code while using the new shared implementation.
    """
    global PBAR, MISMATCH, FILENAME
    
    # Update config estimate mode based on global ESTIMATE
    TRANSLATION_CONFIG.estimateMode = bool(ESTIMATE)
    
    # Call the new shared translation function
    return sharedtranslateAI(
        text=text,
        history=history,
        fullPromptFlag=fullPromptFlag,
        config=TRANSLATION_CONFIG,
        filename=FILENAME,
        pbar=PBAR,
        lock=LOCK,
        mismatchList=MISMATCH
    )
