local MOD_NAME = "SurrounDeadOnline-Phase5G55AutoContinue"
local LOOP_INTERVAL_MS = 200
local OPENING_READY_TICKS_REQUIRED = 5
local MENU_READY_TICKS_REQUIRED = 6
local MAX_FAILED_ATTEMPTS = 3

local CONTINUE_FUNCTION =
    "BndEvt__MenuWidget_ContinueGame_K2Node_ComponentBoundEvent_25_OnButtonClickedEvent__DelegateSignature"

local dataPath = [[__DATA_PATH__]]

local triggerPath =
    dataPath .. "\\phase5g55.autoload_once.txt"

local openingReadyPath =
    dataPath .. "\\lua_phase5g55_opening_screen_ready.txt"

local openingClickedPath =
    dataPath .. "\\lua_phase5g55_opening_screen_clicked.txt"

local loadedPath =
    dataPath .. "\\lua_phase5g55_autoload_loaded.txt"

local menuReadyPath =
    dataPath .. "\\lua_phase5g55_menu_ready.txt"

local invokedPath =
    dataPath .. "\\lua_phase5g55_autoload_invoked.txt"

local errorPath =
    dataPath .. "\\lua_phase5g55_autoload_error.txt"

local openingReadyTicks = 0
local menuReadyTicks = 0
local failedAttempts = 0
local openingReadyWritten = false
local menuReadyWritten = false
local invoked = false
local finished = false
local scheduled = false

local function log(message)
    print(string.format("[%s] %s\n", MOD_NAME, tostring(message)))
end

local function fileExists(path)
    local file = io.open(path, "r")

    if file then
        file:close()
        return true
    end

    return false
end

local function atomicWrite(path, contents)
    local tempPath = path .. ".tmp"
    local file, errorMessage = io.open(tempPath, "w")

    if not file then
        return false, errorMessage
    end

    file:write(contents)
    file:flush()
    file:close()

    os.remove(path)

    local ok, renameError = os.rename(tempPath, path)

    if not ok then
        return false, renameError
    end

    return true, nil
end

local function clearFile(path)
    os.remove(path)
end

local function writeError(stage, message)
    atomicWrite(
        errorPath,
        "phase=5G.5.5\n" ..
        "autoContinueError=1\n" ..
        "stage=" .. tostring(stage) .. "\n" ..
        "attempt=" .. tostring(failedAttempts) .. "\n" ..
        "function=" .. CONTINUE_FUNCTION .. "\n" ..
        "message=" .. tostring(message) .. "\n"
    )
end

clearFile(openingReadyPath)
clearFile(menuReadyPath)
clearFile(invokedPath)
clearFile(errorPath)

atomicWrite(
    loadedPath,
    "phase=5G.5.5\n" ..
    "luaAutoLoaderLoaded=1\n" ..
    "dataPathMode=installer-literal\n" ..
    "openingWidgetClass=PressAnyKeyWidget_C\n" ..
    "menuWidgetClass=MenuWidget_C\n" ..
    "continueFunction=" .. CONTINUE_FUNCTION .. "\n" ..
    "repeatedKeyboardInputUsed=0\n" ..
    "repeatedMouseInputUsed=0\n"
)

log("Phase 5G.5.5 staged auto-loader started.")
log("Waiting through the Unreal loading screen for PressAnyKeyWidget_C.")

LoopAsync(LOOP_INTERVAL_MS, function()
    if finished then
        return true
    end

    if not fileExists(triggerPath) then
        return false
    end

    if scheduled then
        return false
    end

    scheduled = true

    ExecuteInGameThread(function()
        scheduled = false

        if finished or invoked or not fileExists(triggerPath) then
            return
        end

        local openingWidget = FindFirstOf("PressAnyKeyWidget_C")
        local menuWidget = FindFirstOf("MenuWidget_C")

        local openingValid =
            openingWidget and openingWidget:IsValid()

        local menuValid =
            menuWidget and menuWidget:IsValid()

        if not fileExists(openingClickedPath) then
            menuReadyTicks = 0

            if openingValid then
                openingReadyTicks = openingReadyTicks + 1

                if (
                    openingReadyTicks >=
                        OPENING_READY_TICKS_REQUIRED and
                    not openingReadyWritten
                ) then
                    openingReadyWritten = true

                    atomicWrite(
                        openingReadyPath,
                        "phase=5G.5.5\n" ..
                        "openingScreenReady=1\n" ..
                        "objectClass=PressAnyKeyWidget_C\n" ..
                        "stableTicks=" .. tostring(openingReadyTicks) .. "\n" ..
                        "unrealLoadingScreenFinished=1\n" ..
                        "singleClickRequired=1\n"
                    )

                    log(
                        "PressAnyKeyWidget_C is stable. " ..
                        "The Unreal loading screen has finished."
                    )
                end
            else
                openingReadyTicks = 0
            end

            return
        end

        if not menuValid then
            menuReadyTicks = 0
            return
        end

        menuReadyTicks = menuReadyTicks + 1

        if (
            menuReadyTicks >= MENU_READY_TICKS_REQUIRED and
            not menuReadyWritten
        ) then
            menuReadyWritten = true

            atomicWrite(
                menuReadyPath,
                "phase=5G.5.5\n" ..
                "menuWidgetFound=1\n" ..
                "readyTicks=" .. tostring(menuReadyTicks) .. "\n" ..
                "openingClickMarkerSeen=1\n" ..
                "function=" .. CONTINUE_FUNCTION .. "\n"
            )

            log("MenuWidget_C is stable. Invoking the real Continue event.")
        end

        if menuReadyTicks < MENU_READY_TICKS_REQUIRED then
            return
        end

        failedAttempts = failedAttempts + 1

        local ok, errorMessage = pcall(function()
            menuWidget:BndEvt__MenuWidget_ContinueGame_K2Node_ComponentBoundEvent_25_OnButtonClickedEvent__DelegateSignature()
        end)

        if not ok then
            writeError("continue-event", errorMessage)

            log(
                "Automatic Continue failed on attempt " ..
                tostring(failedAttempts) ..
                ": " ..
                tostring(errorMessage)
            )

            menuReadyTicks = 0

            if failedAttempts >= MAX_FAILED_ATTEMPTS then
                clearFile(triggerPath)
                finished = true
                log("Automatic Continue stopped after maximum failed attempts.")
            end

            return
        end

        invoked = true
        finished = true

        clearFile(triggerPath)
        clearFile(errorPath)

        atomicWrite(
            invokedPath,
            "phase=5G.5.5\n" ..
            "autoContinueInvoked=1\n" ..
            "openingWidgetConfirmedBeforeClick=1\n" ..
            "openingScreenSingleClick=1\n" ..
            "attempt=" .. tostring(failedAttempts) .. "\n" ..
            "objectClass=MenuWidget_C\n" ..
            "function=" .. CONTINUE_FUNCTION .. "\n" ..
            "repeatedKeyboardInputUsed=0\n" ..
            "repeatedMouseInputUsed=0\n"
        )

        log("The real SurrounDead Continue event was invoked.")
    end)

    return false
end)
