-- 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.

--]]

THSoilMap = {}
local THSoilMap_mt = Class(THSoilMap)
THSoilMap.SOIL_MAP_ID = {
    MAPUS = "mapUS",
    MAPAS = "mapAS",
    MAPEU = "mapEU",
    MAPFR = "mapFR",
    MAPSA = "mapSA",
    MAP_ALPINE = "mapAlpine",
    MAP_GENERIC = "generic"
}
THUtils.createEnumTable(THSoilMap.SOIL_MAP_ID)
THSoilMap.BIT_VECTOR_NAME = {
    SOIL_MAP = "SoilMap",
    SEED_RATE_MAP = "SeedRateMap",
    COVER_MAP = "coverMap",
    NITROGEN_MAP = "nitrogenMap",
    NITROGEN_NOISE_MAP = "nitrogenNoiseMap",
    PH_MAP = "phMap",
    PH_NOISE_MAP = "pHNoiseMap",
    YIELD_MAP = "YieldMap",
}
THUtils.createEnumTable(THSoilMap.BIT_VECTOR_NAME)
local function initScript()
    local self = THSoilMap.new()
    if self ~= nil then
        _G.g_thSoilMap = self
        THUtils.overwriteFunction("Logging", "error", false, false, self, THSoilMap.overwrite_loggingError)
        THUtils.overwriteFunction(g_thGlobalEnv, "loadBitVectorMapNew", false, false, self, THSoilMap.overwrite_loadBitVectorMapNew)
        THUtils.overwriteFunction(g_thGlobalEnv, "loadBitVectorMapFromFile", false, false, self, THSoilMap.overwrite_loadBitVectorMapFromFile)
        THUtils.overwriteFunction(g_thGlobalEnv, "getBitVectorMapSize", false, false, self, THSoilMap.overwrite_getBitVectorMapSize)
        THUtils.overwriteFunction(THPFConfig, "loadFromMapXML", false, false, self, THSoilMap.overwrite_loadFromMapXML)
        THUtils.overwriteFunction(THPFConfig, "onLoadMapFinished", false, false, self, THSoilMap.overwrite_onLoadMapFinished)
    end
end
function THSoilMap.new(customMt)
    customMt = customMt or THSoilMap_mt
    local self = setmetatable({}, customMt)
    self.modulePath = g_thPFConfig.coreData.mod.path .. "modules/soilMap/"
    self.soilMapSize = 1024
    self.soilMapPath = self.modulePath .. "soilMaps/"
    self.noiseMapPath = self.modulePath .. "noiseMaps/"
    self.forceInjectSoilMap = false
    self.isBitVectorMapReloading = false
    self.showBitVectorResetDialog = false
    self.bitVectorSkipLoadFilenames = {}
    self.bypassSoilMapSizeLimit = false
    self.pauseBitVectorProcessing = false
    return self
end
function THSoilMap.getBitVectorMapSize(self, bitVectorMap)
    self.pauseBitVectorProcessing = true
    local sizeX, sizeY = getBitVectorMapSize(bitVectorMap)
    self.pauseBitVectorProcessing = false
    return sizeX, sizeY
end
function THSoilMap.setBitVectorEnvironmentValues(_, bitVectorMap, sizeX, sizeY, filename)
    local pfData = g_thPFConfig.precisionFarming
    if THUtils.argIsValid(type(bitVectorMap) == THValueType.NUMBER and bitVectorMap > 0, "bitVectorMap", bitVectorMap)
        and THUtils.argIsValid(type(sizeX) == THValueType.NUMBER and sizeX > 0, "sizeX", sizeX)
        and THUtils.argIsValid(type(sizeY) == THValueType.NUMBER and sizeY > 0, "sizeY", sizeY)
        and THUtils.argIsValid(filename == nil or type(filename) == THValueType.STRING, "filename", filename)
        and pfData.isFound
    then
        local pfTarget = pfData.target
        local bitVectorName = getName(bitVectorMap)
        if bitVectorName == THSoilMap.BIT_VECTOR_NAME.SOIL_MAP then
            if filename ~= nil then
                pfTarget.soilMap.loadFilename = filename
            end
        elseif bitVectorName == THSoilMap.BIT_VECTOR_NAME.SEED_RATE_MAP then
            pfTarget.seedRateMap.sizeX = sizeX
            pfTarget.seedRateMap.sizeY = sizeY
        elseif bitVectorName == THSoilMap.BIT_VECTOR_NAME.COVER_MAP then
            pfTarget.coverMap.sizeX = sizeX
            pfTarget.coverMap.sizeY = sizeY
        elseif bitVectorName == THSoilMap.BIT_VECTOR_NAME.NITROGEN_MAP then
            pfTarget.nitrogenMap.sizeX = sizeX
            pfTarget.nitrogenMap.sizeY = sizeY
        elseif bitVectorName == THSoilMap.BIT_VECTOR_NAME.NITROGEN_NOISE_MAP then
            pfTarget.nitrogenMap.noiseResolution = sizeX
            if filename ~= nil then
                pfTarget.nitrogenMap.noiseFilename = filename
            end
        elseif bitVectorName == THSoilMap.BIT_VECTOR_NAME.PH_MAP then
            pfTarget.pHMap.sizeX = sizeX
            pfTarget.pHMap.sizeY = sizeY
        elseif bitVectorName == THSoilMap.BIT_VECTOR_NAME.PH_NOISE_MAP then
            pfTarget.pHMap.noiseResolution = sizeX
            if filename ~= nil then
                pfTarget.pHMap.noiseFilename = filename
            end
        elseif bitVectorName == THSoilMap.BIT_VECTOR_NAME.YIELD_MAP then
            pfTarget.yieldMap.sizeX = sizeX
            pfTarget.yieldMap.sizeY = sizeY
        end
        return true
    end
    return false
end
function THSoilMap.overwrite_loggingError(self, superFunc, messageText, formatArg1, formatArg2, formatArg3, formatArg4, formatArg5, ...)
    local allowRun = true
    THUtils.pcall(function()
        if self.bitVectorSkipLoadFilenames[formatArg1] ~= nil then
            if type(messageText) == THValueType.STRING
                and string.find(messageText, "loading bit vector map")
            then
                self.bitVectorSkipLoadFilenames[formatArg1] = nil
                allowRun = false
            end
        end
    end)
    if allowRun then
        return superFunc(messageText, formatArg1, formatArg2, formatArg3, formatArg4, formatArg5, ...)
    end
end
function THSoilMap.overwrite_loadBitVectorMapNew(self, superFunc, bitVectorMap, sizeX, sizeY, ...)
    if self.pauseBitVectorProcessing then
        return superFunc(bitVectorMap, sizeX, sizeY, ...)
    end
    local mission = g_thPFConfig.coreData.mission
    local pfData = g_thPFConfig.precisionFarming
    local bitVectorName = nil
    local isKnownBitVectorMap = false
    local prependSuccess = false
    if mission ~= nil and pfData.isFound
        and type(bitVectorMap) == THValueType.NUMBER and bitVectorMap > 0
        and type(sizeX) == THValueType.NUMBER and sizeX <= 1024
        and type(sizeY) == THValueType.NUMBER and sizeY <= 1024
    then
        THUtils.pcall(function()
            bitVectorName = getName(bitVectorMap)
            if bitVectorName ~= nil then
                isKnownBitVectorMap = THSoilMap.BIT_VECTOR_NAME.getId(bitVectorName) ~= nil
                if isKnownBitVectorMap then
                    if bitVectorName == THSoilMap.BIT_VECTOR_NAME.SOIL_MAP then
                        if self.soilMapName ~= nil then
                            sizeX = 2048
                            sizeY = 2048
                        end
                    elseif self.soilMapSize > 1024 then
                        sizeX = self.soilMapSize
                        sizeY = self.soilMapSize
                    end
                end
                prependSuccess = true
            end
        end)
    end
    local function appendFunc(...)
        if prependSuccess then
            THUtils.pcall(function()
                local bitVectorSizeX, bitVectorSizeY = self:getBitVectorMapSize(bitVectorMap)
                if bitVectorSizeX ~= nil and bitVectorSizeY ~= nil then
                    local maxBitVectorMapSize = math.max(bitVectorSizeX, bitVectorSizeY)
                    if maxBitVectorMapSize > 1024 then
                        if bitVectorName == THSoilMap.BIT_VECTOR_NAME.SOIL_MAP then
                            self.soilMapSize = maxBitVectorMapSize
                        end
                        if isKnownBitVectorMap then
                            self:setBitVectorEnvironmentValues(bitVectorMap, bitVectorSizeX, bitVectorSizeY)
                            THUtils.displayMsg("Injecting bitVectorMap: %s (%d x %d)", bitVectorName, bitVectorSizeX, bitVectorSizeY)
                        end
                    elseif self.soilMapSize > 1024 then
                        local injectMsg = "Injecting <= 1024 x 1024 bitVectorMap: %s (%d x %d)"
                        if isKnownBitVectorMap then
                            THUtils.errorMsg(nil, injectMsg, bitVectorName, bitVectorSizeX, bitVectorSizeY)
                        else
                            if THUtils.getIsDebugEnabled() then
                                THUtils.displayMsg(injectMsg, bitVectorName, bitVectorSizeX, bitVectorSizeY)
                            end
                        end
                    end
                end
            end)
        end
        return ...
    end
    return appendFunc(superFunc(bitVectorMap, sizeX, sizeY, ...))
end
function THSoilMap.overwrite_loadBitVectorMapFromFile(self, superFunc, bitVectorMap, filename, numChannels, ...)
    if self.pauseBitVectorProcessing then
        return superFunc(bitVectorMap, filename, numChannels, ...)
    end
    local pfData = g_thPFConfig.precisionFarming
    local fileLocation = "OTHER"
    local oldFilename = filename
    local bitVectorName = nil
    local extraFuncArgs = nil
    local isPFBitVectorMap, isKnownBitVectorMap = false, false
    local prependSuccess = false
    if pfData.isFound
        and type(bitVectorMap) == THValueType.NUMBER and bitVectorMap > 0
        and type(filename) == THValueType.STRING and filename ~= ""
    then
        THUtils.pcall(function(...)
            local PrecisionFarming = pfData.mod.environment.PrecisionFarming
            bitVectorName = getName(bitVectorMap)
            if bitVectorName ~= nil then
                if string.find(filename, "[pP]recisionFarming") then
                    isPFBitVectorMap = true
                end
                isKnownBitVectorMap = THSoilMap.BIT_VECTOR_NAME.getId(bitVectorName) ~= nil
                if isPFBitVectorMap or isKnownBitVectorMap then
                    if string.find(filename, PrecisionFarming.BASE_DIRECTORY, nil, true) then
                        fileLocation = "DEFAULT"
                    elseif g_thPFConfig.coreData.savegamePath ~= nil and string.find(filename, g_thPFConfig.coreData.savegamePath, nil, true) then
                        fileLocation = "SAVEGAME"
                    elseif g_thPFConfig.coreData.map.isMod and string.find(filename, g_thPFConfig.coreData.map.path, nil, true) then
                        fileLocation = "MAP"
                    elseif string.find(filename, g_thPFConfig.coreData.mod.path, nil, true) then
                        fileLocation = "INTERNAL"
                    end
                    if bitVectorName == THSoilMap.BIT_VECTOR_NAME.SOIL_MAP then
                        if self.soilMapFilename ~= nil and filename ~= self.soilMapFilename then
                            if fileLocation == "DEFAULT"
                                or (fileLocation == "MAP" and self.forceInjectSoilMap)
                                or self.isBitVectorMapReloading
                            then
                                filename = self.soilMapFilename
                            end
                        end
                    elseif self.soilMapSize > 1024 then
                        if bitVectorName == THSoilMap.BIT_VECTOR_NAME.NITROGEN_NOISE_MAP
                            or bitVectorName == THSoilMap.BIT_VECTOR_NAME.PH_NOISE_MAP
                        then
                            if fileLocation == "DEFAULT"
                                or self.isBitVectorMapReloading
                            then
                                local newFilename = string.format("ph_noise_%s.png", self.soilMapSize)
                                local absNewFilename = THUtils.getFilename(newFilename, self.noiseMapPath)
                                if THUtils.getFileExists(absNewFilename) and filename ~= absNewFilename then
                                    filename = absNewFilename
                                end
                            end
                        end
                    end
                end
                extraFuncArgs = THUtils.pack(...)
                prependSuccess = true
            end
        end, ...)
    end
    local function appendFunc(rSuccess, ...)
        local isReloading = self.isBitVectorMapReloading == true
        local reloadFilename = nil
        self.isBitVectorMapReloading = false
        if rSuccess and prependSuccess then
            THUtils.pcall(function()
                local bitVectorSizeX, bitVectorSizeY = self:getBitVectorMapSize(bitVectorMap)
                if bitVectorSizeX ~= nil and bitVectorSizeY ~= nil then
                    local maxBitVectorMapSize = math.max(bitVectorSizeX, bitVectorSizeY)
                    if bitVectorName == THSoilMap.BIT_VECTOR_NAME.SOIL_MAP then
                        if maxBitVectorMapSize > 1024 then
                            self.soilMapSize = maxBitVectorMapSize
                            self.bypassSoilMapSizeLimit = true
                            self:setBitVectorEnvironmentValues(bitVectorMap, bitVectorSizeX, bitVectorSizeY, filename)
                        elseif fileLocation ~= "MAP" and fileLocation ~= "INTERNAL" then
                            if self.defaultSoilMapFilename ~= nil and filename ~= self.defaultSoilMapFilename then
                                reloadFilename = self.defaultSoilMapFilename
                            end
                            if reloadFilename == nil
                                and self.soilMapFilename ~= nil and filename ~= self.soilMapFilename
                            then
                                reloadFilename = self.soilMapFilename
                            end
                        end
                    else
                        if bitVectorName == THSoilMap.BIT_VECTOR_NAME.NITROGEN_NOISE_MAP
                            or bitVectorName == THSoilMap.BIT_VECTOR_NAME.PH_NOISE_MAP
                        then
                            if maxBitVectorMapSize ~= self.soilMapSize then
                                if isReloading then
                                    rSuccess = false
                                else
                                    reloadFilename = filename
                                end
                            end
                        elseif isKnownBitVectorMap
                            or (isPFBitVectorMap and fileLocation == "SAVEGAME")
                        then
                            if maxBitVectorMapSize ~= self.soilMapSize then
                                if fileLocation == "SAVEGAME" then
                                    self.bitVectorSkipLoadFilenames[filename] = true
                                    rSuccess = false
                                end
                            end
                        end
                        if rSuccess and reloadFilename == nil then
                            if maxBitVectorMapSize == self.soilMapSize then
                                if isKnownBitVectorMap then
                                    self:setBitVectorEnvironmentValues(bitVectorMap, bitVectorSizeX, bitVectorSizeY, filename)
                                end
                                if filename ~= oldFilename then
                                    THUtils.displayMsg("Loading bitVectorMap %s: (%d x %d) >> %s", bitVectorName, bitVectorSizeX, bitVectorSizeY, filename)
                                end
                            elseif isKnownBitVectorMap or isPFBitVectorMap then
                                if THUtils.getIsDebugEnabled() then
                                    THUtils.errorMsg(false, "Possible bitVectorMap size mismatch (%d vs %d): %s >> %s", maxBitVectorMapSize, self.soilMapSize, bitVectorName, filename)
                                end
                            end
                        end
                    end
                end
            end)
        end
        if reloadFilename ~= nil then
            extraFuncArgs = extraFuncArgs or { n = 0 }
            self.isBitVectorMapReloading = true
            return loadBitVectorMapFromFile(bitVectorMap, reloadFilename, numChannels, THUtils.unpack(extraFuncArgs))
        else
            return rSuccess, ...
        end
    end
    return appendFunc(superFunc(bitVectorMap, filename, numChannels, ...))
end
function THSoilMap.overwrite_getBitVectorMapSize(self, superFunc, bitVectorMap, ...)
    if self.pauseBitVectorProcessing then
        return superFunc(bitVectorMap, ...)
    end
    local pfData = g_thPFConfig.precisionFarming
    local function appendFunc(rSizeX, rSizeY, ...)
        if pfData.isFound
            and type(bitVectorMap) == THValueType.NUMBER and bitVectorMap > 0
        then
            THUtils.pcall(function()
                local bitVectorName = getName(bitVectorMap)
                if bitVectorName == THSoilMap.BIT_VECTOR_NAME.SOIL_MAP then
                    if self.bypassSoilMapSizeLimit
                        and (rSizeX > 1024 or rSizeY > 1024)
                    then
                        THUtils.displayMsg("Bypassing %s limit of 1024 x 1024", bitVectorName)
                        THUtils.displayMsg("- new size: %d x %d", rSizeX, rSizeY)
                        rSizeX = 1024
                        rSizeY = 1024
                        self.bypassSoilMapSizeLimit = false
                    end
                end
            end)
        end
        return rSizeX, rSizeY, ...
    end
    return appendFunc(superFunc(bitVectorMap, ...))
end
function THSoilMap.overwrite_loadFromMapXML(self, superFunc, parent, xmlFile, xmlKey, ...)
    local function appendFunc(rSuccess, ...)
        if rSuccess then
            THUtils.pcall(function()
                local soilMapKey = xmlKey .. ".soilMap"
                local soilMapId = xmlFile:getString(soilMapKey .. "#id")
                local soilMapSize = xmlFile:getInt(soilMapKey .. "#size", 2048)
                local forceInjectSoilMap = xmlFile:getBool(soilMapKey .. "#force", false)
                self.forceInjectSoilMap = false
                if g_thPFConfig.coreData.map.isMod then
                    local pfXMLKey = "map.precisionFarming"
                    if xmlFile:hasProperty(pfXMLKey) then
                        local defaultSoilMapFilename = xmlFile:getString(pfXMLKey .. ".soilMap#filename")
                        if defaultSoilMapFilename ~= nil and defaultSoilMapFilename ~= "" then
                            local absDefaultSoilMapFilename = THUtils.getFilename(defaultSoilMapFilename, g_thPFConfig.coreData.map.path)
                            if absDefaultSoilMapFilename ~= nil and absDefaultSoilMapFilename ~= "" then
                                self.defaultSoilMapFilename = absDefaultSoilMapFilename
                            end
                        end
                    end
                end
                if soilMapId ~= nil and soilMapId ~= "" then
                    local soilMapName = THSoilMap.SOIL_MAP_ID.getValue(soilMapId)
                    if soilMapName == nil then
                        local soilMapIds = THSoilMap.SOIL_MAP_ID.getValues()
                        THUtils.xmlErrorMsg(xmlKey, nil, THMessage.INVALID_VALUE, "soilMapId", soilMapId)
                        if soilMapIds ~= nil then
                            THUtils.displayMsg("- available values:")
                            for _, value in pairs(soilMapIds) do
                                THUtils.displayMsg("  + %s", value)
                            end
                        end
                    else
                        local soilMapFilename = string.format("%s%s/%s.png", self.soilMapPath, soilMapSize, soilMapName)
                        if not THUtils.getFileExists(soilMapFilename) then
                            THUtils.xmlErrorMsg(soilMapKey, nil, THMessage.FILE_NOT_FOUND, soilMapFilename)
                        else
                            self.soilMapName = soilMapName
                            self.soilMapFilename = soilMapFilename
                            self.forceInjectSoilMap = forceInjectSoilMap == true
                        end
                    end
                end
            end)
        end
        return rSuccess, ...
    end
    return appendFunc(superFunc(parent, xmlFile, xmlKey, ...))
end
function THSoilMap.overwrite_onLoadMapFinished(self, superFunc, parent, mission, ...)
    local pfData = g_thPFConfig.precisionFarming
    local function appendFunc(...)
        THUtils.pcall(function()
            if THUtils.getIsDebugEnabled() then
                local pfTarget = pfData.target
                THUtils.displayMsg("")
                THUtils.displayMsg("THSoilMap information:")
                THUtils.printTable(self)
                if self.soilMapSize > 1024 then
                    THUtils.displayMsg("SoilMap information:")
                    THUtils.printTableValues(pfTarget.soilMap)
                    THUtils.displayMsg("SeedRateMap information:")
                    THUtils.printTableValues(pfTarget.seedRateMap)
                    THUtils.displayMsg("CoverMap information:")
                    THUtils.printTableValues(pfTarget.coverMap)
                    THUtils.displayMsg("NitrogenMap information:")
                    THUtils.printTableValues(pfTarget.nitrogenMap)
                    THUtils.displayMsg("PHMap information:")
                    THUtils.printTableValues(pfTarget.pHMap)
                    THUtils.displayMsg("YieldMap information:")
                    THUtils.printTableValues(pfTarget.yieldMap)
                end
            end
        end)
        return ...
    end
    return appendFunc(superFunc(parent, mission, ...))
end
THUtils.pcall(initScript)