--[[
===============================================================================
  Ziel:
    MotionPathEffects-XML ADDITIV zur Karte laden
    + COMPOST voll anbinden
    + Precision Farming Support
    + PF HUD / Mehrsprachige Kompost-Ausbringung

  Autor: Copyright © FrechDachs93 20.10.2025
 Features / Kombiniert:
    - COMPOST SprayType aus eigener config/sprayTypes.xml nachladen
      (erst nach dem Map-Laden, damit der FillType COMPOST existiert)

    - MotionPathEffects additiv registrieren
      (compost-Effekt + eigenes Streubild)

    - Merge:
      COMPOST_SPREADER -> MANURE_SPREADER
      inkl. Fallback bei materials=0

    - COMPOST als Dünger registrieren
      (wie MANURE, damit Düngerstufe zählt)

    - Precision Farming Support:
      COMPOST wird intern wie MANURE behandelt,
      damit Stickstoff, Verbrauch, Karte und Ausbringung
      vollständig mit Precision Farming funktionieren

    - PF HUD Erweiterung:
      Eigener HUD-Text für COMPOST-Ausbringung
      via l10n:
      PF_headline_n_compostSpreader

    - Mehrsprachige Unterstützung
      (alle HUD-Texte via Translation XML)

    - Optionales Debugging:
      Alle Mod-Logs über ENABLE_DEBUG
      global an/aus schaltbar
===============================================================================
]]

------------------------- 0) Basis / Setup ------------------------------------
local MOD_NAME = g_currentModName or "UnknownMod"
local MOD_DIR  = g_currentModDirectory or ""
local ENABLE_DEBUG = false   -- <--- HIER an/aus

-- kleiner Helfer: nur loggen wenn Debug an
local function dlog(fmt, ...)
    if ENABLE_DEBUG then
        Logging.info("[%s] "..fmt, MOD_NAME, ...)
    end
end

-- Fehler/Warnungen vom Mod selbst lassen wir lieber immer durch
local function wlog(fmt, ...)
    Logging.warning("[%s] "..fmt, MOD_NAME, ...)
end

dlog("TEST A")

------------------------- 1) Pfade --------------------------------------------
local EXTRA_MPE_TOP_XML   = "effects/motionPathEffects.xml"
local EXTRA_MPE_INNER_XML = "effects/compost/compostEffects.xml"
local SPRAY_TYPES_XML     = "config/sprayTypes.xml"

------------------------- 2) sprayTypes NACH ALLEM laden -----------------------
local function loadCompostSprayTypesFromXml()
    local p = Utils.getFilename(SPRAY_TYPES_XML, MOD_DIR)
    local xml = XMLFile.load("sprayTypes", p, nil)
    if xml ~= nil then
        if g_sprayTypeManager and g_sprayTypeManager.loadSprayTypes then
            g_sprayTypeManager:loadSprayTypes(xml.handle, "sprayTypes")
            dlog("COMPOST SprayTypes geladen: %s", p)
        else
            Logging.error("[%s] SprayType-Manager API nicht gefunden.", MOD_NAME)
        end
        xml:delete()
    else
        wlog("sprayTypes.xml nicht gefunden: %s", p)
    end
end

-- erst prüfen, ob der FillType schon existiert, sonst bringt das Laden nichts
local function ensureCompostSprayType()
    local ft = g_fillTypeManager and g_fillTypeManager:getFillTypeIndexByName("COMPOST") or nil
    if not ft then
        -- genau das war deine Warning – also hier später nochmal probieren
        dlog("COMPOST FillType ist noch nicht da – sprayTypes wird jetzt NICHT geladen.")
        return
    end
    loadCompostSprayTypesFromXml()
    dlog("COMPOST als Dünger registriert (sprayTypes).")
end

------------------------- 2.1) Precision Farming COMPOST Patch -----------------
local compostPFPatchDone = false

local function patchCompostForPrecisionFarming()
    if compostPFPatchDone then
        return
    end

    if NitrogenMap == nil then
        dlog("PF: NitrogenMap noch nicht vorhanden.")
        return
    end

    local ftCompost = g_fillTypeManager and g_fillTypeManager:getFillTypeIndexByName("COMPOST") or nil
    local ftManure  = g_fillTypeManager and g_fillTypeManager:getFillTypeIndexByName("MANURE") or nil

    if ftCompost == nil then
        wlog("PF: COMPOST FillType nicht gefunden.")
        return
    end

    if ftManure == nil then
        wlog("PF: MANURE FillType nicht gefunden.")
        return
    end

    local oldGetFillTypeIsFertilizer = NitrogenMap.getFillTypeIsFertilizer
    NitrogenMap.getFillTypeIsFertilizer = function(self, fillTypeIndex)
        if fillTypeIndex == ftCompost then
            return true
        end

        return oldGetFillTypeIsFertilizer(self, fillTypeIndex)
    end

    local oldGetFertilizerUsage = NitrogenMap.getFertilizerUsage
    NitrogenMap.getFertilizerUsage = function(self, workWidth, lastSpeed, changeValue, fillTypeIndex, dt, sprayAmountAutoMode, fruitType, nActualValue, nitrogenUsageLevelOffset)
        if fillTypeIndex == ftCompost then
            fillTypeIndex = ftManure
        end

        return oldGetFertilizerUsage(self, workWidth, lastSpeed, changeValue, fillTypeIndex, dt, sprayAmountAutoMode, fruitType, nActualValue, nitrogenUsageLevelOffset)
    end

    compostPFPatchDone = true

    dlog("PF: COMPOST als Stickstoff-Dünger registriert.")
end

------------------------- 3) MotionPathEffects laden ---------------------------
local function registerExtraMotionPathEffects()
    local function _tryLoad(pathRel)
        local full = Utils.getFilename(pathRel, MOD_DIR)
        local xml = XMLFile.load("extraMPE", full, nil)
        if xml ~= nil then
            if g_motionPathEffectManager and g_motionPathEffectManager.loadMotionPathEffects then
                g_motionPathEffectManager:loadMotionPathEffects(
                    xml.handle,
                    "motionPathEffects.motionPathEffect",
                    MOD_DIR,
                    MOD_NAME
                )
                dlog("MotionPathEffects geladen: %s", full)
            else
                Logging.error("[%s] MotionPathEffectManager API nicht gefunden (loadMotionPathEffects).", MOD_NAME)
            end
            xml:delete()
            return true
        end
        return false
    end

    -- erst versuchen: Container
    if not _tryLoad(EXTRA_MPE_TOP_XML) then
        -- sonst direkt die Unterdatei
        if not _tryLoad(EXTRA_MPE_INNER_XML) then
            Logging.error("[%s] Konnte keine MotionPathEffects-XML öffnen (%s / %s).",
                MOD_NAME, EXTRA_MPE_TOP_XML, EXTRA_MPE_INNER_XML)
        end
    end
end

------------------------- 4) Merge + Fallback (dein alter Block) ---------------
local effectsToMerge = {
    ["COMPOST_SPREADER"] = {
        into = "MANURE_SPREADER",
        fallbackFillTypeStr = "MANURE",
        fallbackEffectMeshIndex = 1
    }
}

local function _findMeshNodeWithMat0(effect)
    if not effect or not effect.effectMeshes then return nil end
    for _, m in ipairs(effect.effectMeshes) do
        local root = m and m.node
        if root and root ~= 0 then
            local queue, qi = { root }, 1
            while qi <= #queue do
                local n = queue[qi]; qi = qi + 1
                if getMaterial and getMaterial(n, 0) ~= nil then
                    return n
                end
                local num = getNumOfChildren and getNumOfChildren(n) or 0
                for i = 0, num - 1 do
                    table.insert(queue, getChildAt(n, i))
                end
            end
        end
    end
    return nil
end

local function _ensureCompostMapping(intoEffect)
    if not intoEffect or not g_fillTypeManager then return end
    local ftCompost = g_fillTypeManager:getFillTypeIndexByName("COMPOST")
    if not ftCompost then return end
    for _, mat in ipairs(intoEffect.effectMaterials or {}) do
        local fts = mat.fillTypes
        if fts then
            for _, ft in ipairs(fts) do
                if ft == ftCompost then return end
            end
        end
    end
    if not intoEffect.effectMaterials or #intoEffect.effectMaterials == 0 then return end
    local m = intoEffect.effectMaterials[1]
    m.fillTypes = m.fillTypes or {}
    local seen = {}
    for _, ft in ipairs(m.fillTypes) do seen[ft] = true end
    if not seen[ftCompost] then table.insert(m.fillTypes, ftCompost) end
end

local function normalizeManureVsCompostFilters()
    if not g_motionPathEffectManager or not g_motionPathEffectManager.effectsByType then return end
    local effects = g_motionPathEffectManager.effectsByType["MANURE_SPREADER"]
    if not effects then return end

    local ftManure  = g_fillTypeManager and g_fillTypeManager:getFillTypeIndexByName("MANURE")  or nil
    local ftCompost = g_fillTypeManager and g_fillTypeManager:getFillTypeIndexByName("COMPOST") or nil
    if not ftManure or not ftCompost then return end

    for _, effect in ipairs(effects) do
        for _, mat in ipairs(effect.effectMaterials or {}) do
            local nodeName = (getName and mat.node and getName(mat.node)) or ""
            local lname = string.lower(nodeName)
            if string.find(lname, "compost") then
                mat.fillTypes = { ftCompost }
            elseif string.find(lname, "manure") then
                mat.fillTypes = { ftManure }
            end
        end
    end
end

local function createFallbackMaterial(intoEffect, intoData)
    if not intoEffect then return end
    local node = _findMeshNodeWithMat0(intoEffect)
    local matId = node and getMaterial and getMaterial(node, 0) or nil

    if not node or not matId then
        local idx  = (intoData and intoData.fallbackEffectMeshIndex) or 1
        local mesh = (intoEffect.effectMeshes and intoEffect.effectMeshes[idx]) or nil
        if mesh and mesh.node and mesh.node ~= 0 then
            node  = node  or mesh.node
            matId = matId or (getMaterial and getMaterial(node, 0))
        end
    end
    if not node then
        wlog("Fallback: Kein Node gefunden (weder Shape noch erstes Mesh).")
        return
    end

    local ftIdx = nil
    if intoData and intoData.fallbackFillTypeStr and g_fillTypeManager then
        ftIdx = g_fillTypeManager:getFillTypeIndexByName(intoData.fallbackFillTypeStr)
    end

    intoEffect.effectMaterials = intoEffect.effectMaterials or {}
    table.insert(intoEffect.effectMaterials, {
        node = node,
        materialId = matId,
        parent = intoEffect,
        lod = {},
        fillTypes = ftIdx and { ftIdx } or {},
        fruitTypes = nil,
        growthStates = nil,
        customShaderMaps = {},
        customShaderParameters = {},
    })

    _ensureCompostMapping(intoEffect)
end

local function mergeEffects()
    if not g_motionPathEffectManager or not g_motionPathEffectManager.effectsByType then return end

    for from, intoData in pairs(effectsToMerge) do
        local intoEffects = g_motionPathEffectManager.effectsByType[(intoData.into or ""):upper()]
        if intoEffects == nil then
            Logging.error("[%s] Effekt-Typ %s fehlt (Map/Mod-Konflikt?)", MOD_NAME, tostring(intoData.into))
        else
            local fromEffects = g_motionPathEffectManager.effectsByType[(from or ""):upper()]
            for _, intoEffect in pairs(intoEffects) do
                if not intoEffect.effectMaterials or #intoEffect.effectMaterials == 0 then
                    createFallbackMaterial(intoEffect, intoData)
                end
                if fromEffects ~= nil then
                    for _, fromEffect in pairs(fromEffects) do
                        if fromEffect.customShaderVariation ~= nil then
                            intoEffect.customShaderVariation = fromEffect.customShaderVariation
                        end
                        for _, m in pairs(fromEffect.effectMeshes or {}) do
                            table.insert(intoEffect.effectMeshes, m)
                        end
                        for _, em in pairs(fromEffect.effectMaterials or {}) do
                            table.insert(intoEffect.effectMaterials, em)
                        end
                    end
                end
                _ensureCompostMapping(intoEffect)
            end
            dlog("Effekte gemerged: %s -> %s", tostring(from), tostring(intoData.into))
        end
    end

    normalizeManureVsCompostFilters()
end

------------------------- 5) Debug verzögert -----------------------------------
local function _runDebugLater()
    if not ENABLE_DEBUG then return end
    local delayMs = 500
    local T = {}
    function T:update(dt)
        delayMs = math.max(0, delayMs - (dt or 0))
        if delayMs == 0 then
            local mgr = g_motionPathEffectManager
            local effects = mgr and mgr.effectsByType and mgr.effectsByType["MANURE_SPREADER"] or nil
            if effects then
                local ftCompost = (g_fillTypeManager and g_fillTypeManager:getFillTypeIndexByName("COMPOST")) or -1
                dlog("[DEBUG] --- MANURE_SPREADER: count=%d ---", #effects)
                for i, e in ipairs(effects) do
                    local mats   = e.effectMaterials or {}
                    local meshes = e.effectMeshes or {}
                    local hasC   = false
                    for _, m in ipairs(mats) do
                        local fts = m.fillTypes
                        if fts then
                            for _, ft in ipairs(fts) do
                                if ft == ftCompost then hasC = true break end
                            end
                        end
                        if hasC then break end
                    end
                    dlog("[DEBUG] #%d materials=%d meshes=%d COMPOST=%s", i, #mats, #meshes, tostring(hasC))
                end
                dlog("[DEBUG] --- Ende ---")
            else
                dlog("[DEBUG] MANURE_SPREADER nicht vorhanden oder Manager nil.")
            end
            removeModEventListener(T)
        end
    end
    addModEventListener(T)
end

dlog("TEST B")

------------------------- 6) Precision Farming Patch ---------------------------

dlog("PF PATCH BLOCK GELADEN")

local pfMapPatchDone = false
local pfAssetsDone = false
local pfHookInstalled = false


local function patchPFNitrogenMap(nitrogenMap)

    if pfMapPatchDone or nitrogenMap == nil then
        return
    end

    local oldGetFillTypeIsFertilizer = nitrogenMap.getFillTypeIsFertilizer
    local oldGetFertilizerUsage = nitrogenMap.getFertilizerUsage

    if oldGetFillTypeIsFertilizer == nil or oldGetFertilizerUsage == nil then
        return
    end

    nitrogenMap.getFillTypeIsFertilizer = function(self, fillTypeIndex)
        local ftCompost = g_fillTypeManager:getFillTypeIndexByName("COMPOST")

        if fillTypeIndex == ftCompost then
            return true
        end

        return oldGetFillTypeIsFertilizer(self, fillTypeIndex)
    end

    nitrogenMap.getFertilizerUsage = function(self, workWidth, lastSpeed, changeValue, fillTypeIndex, dt, sprayAmountAutoMode, fruitType, nActualValue, nitrogenUsageLevelOffset)
        local ftCompost = g_fillTypeManager:getFillTypeIndexByName("COMPOST")
        local ftManure  = g_fillTypeManager:getFillTypeIndexByName("MANURE")

        if fillTypeIndex == ftCompost and ftManure ~= nil then
            fillTypeIndex = ftManure
        end

        return oldGetFertilizerUsage(self, workWidth, lastSpeed, changeValue, fillTypeIndex, dt, sprayAmountAutoMode, fruitType, nActualValue, nitrogenUsageLevelOffset)
    end

    pfMapPatchDone = true

    dlog("PF: COMPOST wird wie MANURE behandelt.")
end


local function installPFPatch()

    if pfHookInstalled then
        return true
    end

    local ES = nil

    if FS25_precisionFarming ~= nil then
        ES = FS25_precisionFarming.ExtendedSprayer
    end

    if ES == nil then
        ES = ExtendedSprayer
    end

    if ES == nil then
        dlog("PF: ExtendedSprayer NICHT gefunden.")
        return false
    end

    dlog("PF: ExtendedSprayer gefunden.")

    pfHookInstalled = true

    ES.onUpdate = Utils.overwrittenFunction(
        ES.onUpdate,
        function(self, superFunc, dt, ...)

            local spec = self[ES.SPEC_TABLE_NAME]

            if spec ~= nil and spec.nitrogenMap ~= nil then

                if not pfAssetsDone then
                    pfAssetsDone = true

                    ensureCompostSprayType()
                    registerExtraMotionPathEffects()
                    mergeEffects()
                    _runDebugLater()
                end

                patchPFNitrogenMap(spec.nitrogenMap)
            end

            return superFunc(self, dt, ...)
        end
    )

    dlog("PF: ExtendedSprayer erfolgreich gepatcht.")

    return true
end


installPFPatch()

------------------------- 7) Compost Effects / Texture Hooks -------------------

-- 1. früh: nur Effekte laden
local function onLoadMapData(self, ...)
    registerExtraMotionPathEffects()
end

-- 2. spät: FillTypes da -> SprayTypes laden -> mergen -> debug
local function onFinishedLoading(self, ...)
    ensureCompostSprayType()
    mergeEffects()
    _runDebugLater()
end

MotionPathEffectManager.loadMapData =
    Utils.appendedFunction(
        MotionPathEffectManager.loadMapData,
        onLoadMapData
    )

FSBaseMission.onFinishedLoading =
    Utils.appendedFunction(
        FSBaseMission.onFinishedLoading,
        onFinishedLoading
    )

------------------------- 8) PF HUD Text Patch ---------------------------------

local compostHudTextPatchDone = false


local function tryPatchCompostHUD()

    if compostHudTextPatchDone then
        return
    end

    local HUD = ExtendedSprayerHUDExtension

    if HUD == nil
        and FS25_precisionFarming ~= nil then

        HUD =
            FS25_precisionFarming
            .ExtendedSprayerHUDExtension
    end

    if HUD == nil then
        return
    end

    compostHudTextPatchDone = true

    HUD.draw =
        Utils.overwrittenFunction(
            HUD.draw,

            function(
                self,
                superFunc,
                inputHelpDisplay,
                posX,
                posY
            )

                local oldText = nil
                local isCompost = false
                local ES = nil

                if FS25_precisionFarming ~= nil then
                    ES =
                        FS25_precisionFarming
                        .ExtendedSprayer
                end

                if ES == nil then
                    ES = ExtendedSprayer
                end

                if self ~= nil
                    and self.vehicle ~= nil
                    and ES ~= nil then

                    local sourceVehicle,
                        fillUnitIndex =
                        ES.getFillTypeSourceVehicle(
                            self.vehicle
                        )

                    if sourceVehicle ~= nil
                        and fillUnitIndex ~= nil then

                        local sprayFillType =
                            sourceVehicle
                            :getFillUnitFillType(
                                fillUnitIndex
                            )

                        local ftCompost =
                            g_fillTypeManager
                            :getFillTypeIndexByName(
                                "COMPOST"
                            )

                        if sprayFillType
                            == ftCompost then

                            isCompost = true
                        end
                    end
                end

                if isCompost
                    and self.texts ~= nil then

                    oldText =
                        self.texts
                        .headline_n_manureSpreader

                    self.texts
                        .headline_n_manureSpreader =
                        g_i18n:getText(
                            "PF_headline_n_compostSpreader",
                            MOD_NAME
                        )
                end

                local result =
                    superFunc(
                        self,
                        inputHelpDisplay,
                        posX,
                        posY
                    )

                if oldText ~= nil
                    and self.texts ~= nil then

                    self.texts
                        .headline_n_manureSpreader =
                        oldText
                end

                return result
            end
        )

    dlog("PF HUD: Kompost Text Patch aktiv.")
end

FSBaseMission.update =
    Utils.appendedFunction(
        FSBaseMission.update,

        function()
            tryPatchCompostHUD()
        end
    )