-- Copyright ©2024-2025 by Todd Hundersmarck (ThundR) 
-- All Rights Reserved

--[[

Unauthorized use and/or distribution of this work entitles myself, the author, to unlimited free and unrestricted use, access, and distribution of any works related to the unauthorized user and/or distributor.

--]]

local g_thModName = g_currentModName
THMapValidator = {}
local THMapValidator_mt = Class(THMapValidator)
THMapValidator.CLASS_NAME = "THMapValidator"
local function initScript()
    local self = THMapValidator.new()
    if self ~= nil then
        _G.g_thMapValidator = self
    end
end
function THMapValidator.new(customMt)
    customMt = customMt or THMapValidator_mt
    if THUtils.argIsValid(type(customMt) == THValueType.TABLE, "customMt", customMt) then
        local self = setmetatable({}, customMt)
        self.isServer = g_server ~= nil
        self.isClient = g_client ~= nil
        self.mapIsMod = false
        self.isExistingSave = false
        self.modName = g_thModName
        self.validationCallbacks = {
            byId = {},
            byIndex = {}
        }
        self.hasValidationIssues = false
        self.isValidationMessageActive = false
        if g_thEventManager ~= nil then
            g_thEventManager:addEventListener(self)
        end
        return self
    end
end
function THMapValidator.onPostInitTerrain(self, mission, terrainNode, mapI3DFilename)
    local mapDesc = mission.missionInfo.map
    local mapPath = mapDesc.baseDirectory
    local mapXMLFilename = THUtils.getFilename(mapDesc.mapXMLFilename, mapPath)
    self.mission = mission
    self.missionInfo = mission.missionInfo
    self.mapXMLFilename = mapXMLFilename
    self.mapI3DFilename = mapI3DFilename
    self.mapIsMod = mapDesc.isModMap == true
    local totalPlayTime = self.missionInfo.playTime or 0
    self.isExistingSave = false
    if self.missionInfo.savegameDirectory ~= nil and self.missionInfo.savegameDirectory ~= "" then
        if totalPlayTime > 0 then
            self.isExistingSave = true
        end
    end
    self.hasValidationIssues = false
    self.isValidationMessageActive = false
    if self.isClient and self.mapIsMod then
        local callbacksArray, numCallbacks = self:getValidationCallbacks()
        if callbacksArray ~= nil and numCallbacks > 0 then
            local mapI3DFile, mapXMLFile = self:loadMapDataFiles()
            for i = 1, numCallbacks do
                local callbackData = callbacksArray[i]
                local issuesText = ""
                local hasExtraArgs = callbackData.extraArgs ~= nil and callbackData.extraArgs.n > 0
                if mapI3DFile ~= nil and mapXMLFile ~= nil then
                    if callbackData.target ~= nil then
                        if hasExtraArgs then
                            issuesText = THUtils.call(callbackData.func, callbackData.target, mission, mapI3DFile, mapXMLFile, terrainNode, THUtils.unpack(callbackData.extraArgs))
                        else
                            issuesText = THUtils.call(callbackData.func, callbackData.target, mission, mapI3DFile, mapXMLFile, terrainNode)
                        end
                    elseif hasExtraArgs then
                        issuesText = THUtils.call(callbackData.func, mission, mapI3DFile, mapXMLFile, terrainNode, THUtils.unpack(callbackData.extraArgs))
                    else
                        issuesText = THUtils.call(callbackData.func, mission, mapI3DFile, mapXMLFile, terrainNode)
                    end
                end
                callbackData.issuesText = issuesText
                callbackData.hasIssues = issuesText ~= nil and issuesText ~= ""
                callbackData.isActive = callbackData.hasIssues == true
                if callbackData.isActive then
                    self.hasValidationIssues = true
                end
            end
            if mapI3DFile ~= nil then
                mapI3DFile:delete()
            end
            if mapXMLFile ~= nil then
                mapXMLFile:delete()
            end
        end
    end
end
function THMapValidator.onUpdate(self, dt)
    local mission = self.mission
    if mission ~= nil
        and mission.isMissionStarted and mission.isRunning
    then
        if self.isClient then
            if self.hasValidationIssues and not self.isValidationMessageActive then
                if not g_gui:getIsGuiVisible() then
                    local callbacksArray, numCallbacks = self:getValidationCallbacks()
                    local isActiveCallbackFound = false
                    if callbacksArray ~= nil and numCallbacks > 0 then
                        for i = 1, numCallbacks do
                            local callbackData = callbacksArray[i]
                            if callbackData.isActive then
                                if callbackData.hasIssues then
                                    self.isValidationMessageActive = true
                                    THUtils.errorMsg(nil, callbackData.issuesText)
                                    InfoDialog.show(callbackData.dialogTitle .. "\n" .. callbackData.issuesText, function()
                                        self.isValidationMessageActive = false
                                    end)
                                end
                                isActiveCallbackFound = true
                                callbackData.isActive = false
                                break
                            end
                        end
                    end
                    self.hasValidationIssues = isActiveCallbackFound == true
                end
            end
        end
    end
end
function THMapValidator.loadMapDataFiles(self)
    local mission = self.mission
    local mapI3DFilename = self.mapI3DFilename
    local mapXMLFilename = self.mapXMLFilename
    if mission ~= nil
        and mapI3DFilename ~= nil and mapI3DFilename ~= ""
        and mapXMLFilename ~= nil and mapXMLFilename ~= ""
    then
        local mapI3DFile, mapXMLFile = nil, nil
        if fileExists(mapI3DFilename) then
            mapI3DFile = XMLFile.load(self.modName .. "TempMapI3DXML", mapI3DFilename)
        end
        if fileExists(mapXMLFilename) then
            mapXMLFile = XMLFile.load(self.modName .. "TempMapXML", mapXMLFilename)
        end
        if mapI3DFile ~= nil and mapXMLFile ~= nil then
            return mapI3DFile, mapXMLFile
        end
        if mapI3DFile == nil then
            THUtils.errorMsg(nil, "Could not load map i3d file: %s", mapI3DFilename)
        else
            mapI3DFile:delete()
        end
        if mapXMLFile == nil then
            THUtils.errorMsg(nil, "Could not load map xml file: %s", mapXMLFilename)
        else
            mapXMLFile:delete()
        end
    end
end
function THMapValidator.getValidationCallback(self, callbackId, verbose)
    local idVarType = type(callbackId)
    local callbackData = nil
    verbose = THUtils.validateArg(not verbose or verbose == true, "verbose", verbose, true)
    if idVarType == THValueType.NUMBER then
        callbackData = self.validationCallbacks.byIndex[callbackId]
    elseif idVarType == THValueType.STRING then
        callbackData = self.validationCallbacks.byId[callbackId:upper()]
    elseif callbackId ~= nil then
        verbose = true
    end
    if callbackData == nil and verbose then
        THUtils.errorMsg(true, THMessage.ARGUMENT_INVALID, "callbackId", callbackId)
    end
    return callbackData
end
function THMapValidator.getValidationCallbacks(self)
    return self.validationCallbacks.byIndex, #self.validationCallbacks.byIndex
end
function THMapValidator.addValidationCallback(self, name, dialogTitle, callbackTarget, callbackFunc, ...)
    if THUtils.argIsValid(THUtils.isValidName(name), "name", name)
        and THUtils.argIsValid(dialogTitle == nil or type(dialogTitle) == THValueType.STRING, "dialogTitle", dialogTitle)
        and THUtils.argIsValid(type(callbackFunc) == THValueType.FUNCTION, "callbackFunc", callbackFunc)
    then
        local callbackId = string.upper(name)
        local callbackData = self.validationCallbacks.byId[callbackId]
        if callbackData ~= nil then
            THUtils.errorMsg(true, THMessage.DUPLICATE_ENTRY, "validation callback", name)
        else
            if dialogTitle == nil then
                dialogTitle = g_i18n:getText("thMessage_" .. name .. "IssuesTitle")
            else
                dialogTitle = g_i18n:convertText(dialogTitle, self.modName)
            end
            callbackData = {
                id = callbackId,
                name = name,
                index = #self.validationCallbacks.byIndex + 1,
                func = callbackFunc,
                target = callbackTarget,
                extraArgs = THUtils.pack(...),
                dialogTitle = dialogTitle,
                issuesText = "",
                hasIssues = false,
                isActive = false
            }
            self.validationCallbacks.byId[callbackData.id] = callbackData
            self.validationCallbacks.byIndex[callbackData.index] = callbackData
            return callbackData
        end
    end
end
THUtils.pcall(initScript)