-- Radial menu extension for MultiSlot Wrecker
-- Targeted loading (choose Slot 1..N) + per-slot unloading
-- No legacy filtering needed (part name ATAMultiSlotWrecker prevents original ATA menu from appearing)

if not MSW_ISVehicleMenu then MSW_ISVehicleMenu = {} end

-- Optional requires (no-op if paths not present)
pcall(function() require "TimedActions/MSW_ISLoadVehicle" end)
pcall(function() require "TimedActions/MSW_ISLaunchVehicle" end)

-- ============================================================================
-- VEHICLE BLACKLIST: Vehicles that cannot be loaded onto the MSW trailer
-- Add vehicle script names here to prevent them from being loadable
-- Examples: "Base.Trailer", "Base.TrailerM416", modded vehicles, etc.
-- ============================================================================
MSW_ISVehicleMenu.VehicleBlacklist = {
   
    -- Semi Truck trailers (prevent trailer-on-trailer exploits)
    ["Base.SemiTrailerLowBed"] = true,
    ["Base.SemiTrailerVan"] = true,
    ["Base.SemiTrailerVanCattle"] = true,
    ["Base.SemiTrailerCistern"] = true,
    ["Base.TrailerTSMega"] = true,
    ["Base.TrailerTSMegaAnimal"] = true,
        
    -- Add more blacklisted vehicles here as needed
    -- ["ModName.VehicleScript"] = true,
}

local vec = Vector3f and Vector3f.new and Vector3f.new() or { x = function() return 0 end, y = function() return 0 end }

local function getMultiWreckerPart(vehicle)
    if not vehicle then return nil end
    return vehicle:getPartById("ATAMultiSlotWrecker")
end

local function clampSlots(n)
    local minS = (rawget(_G, 'MSW_Config') and tonumber(MSW_Config.MinSlots)) or 1
    local maxS = (rawget(_G, 'MSW_Config') and tonumber(MSW_Config.MaxSlots)) or 6
    if not n then return nil end
    if n < minS then n = minS end
    if n > maxS then n = maxS end
    return n
end

local function getGlobalSlotsOverride()
    if rawget(_G, 'MSW_Config') and MSW_Config and MSW_Config.Slots ~= nil then
        local s = 3
        return clampSlots(s)
    end
    return nil
end

local function getMultiWreckerConfig(part)
    -- Priority: Global config override -> Part table -> defaults
    local globalSlots = getGlobalSlotsOverride()
    local t = part and part.getTable and part:getTable("ATAMultiSlotWrecker") or nil
    local slots = globalSlots or (t and tonumber(t.slots)) or 3
    return {
        slots = clampSlots(slots) or 3,
        spawnDist = (t and tonumber(t.spawnDist)) or 4,
        spawnSize = (t and tonumber(t.spawnSize)) or 3
    }
end

local function getOrInitMSW(part)
    local md = part:getModData()
    md.msw = md.msw or {}
    md.msw.slots = md.msw.slots or {}
    return md.msw
end

-- Resolve a nice display name for a saved slot
local function MSW_GetSlotVehicleName(slotData)
    if not slotData then return "Unbekannt" end
    local scriptName = slotData.scriptName or slotData.script
    if not scriptName then return "Unbekannt" end
    if getScriptManager then
        local ok, vehScript = pcall(function() return getScriptManager():getVehicle(scriptName) end)
        if ok and vehScript then
            local key = "IGUI_VehicleName" .. vehScript:getName()
            if getText then
                local txt = getText(key)
                if txt and txt ~= key then
                    return txt
                end
            end
            return vehScript:getName() or scriptName
        end
    end
    return scriptName
end

-- Strict type check: only real BaseVehicles
local function isRealVehicle(obj)
    if not obj then return false end
    if obj.isRemovedFromWorld and obj:isRemovedFromWorld() then return false end
    if instanceof then return instanceof(obj, "BaseVehicle") == true end
    return type(obj.getScript) == "function" and type(obj.getId) == "function"
end

-- Check if vehicle is on the blacklist
local function isVehicleBlacklisted(vehicle)
    if not vehicle or not vehicle.getScript then return true end
    local script = vehicle:getScript()
    if not script then return true end
    
    local fullName = script:getFullName()
    if MSW_ISVehicleMenu.VehicleBlacklist[fullName] then
        return true
    end
    
    local name = script:getName()
    if MSW_ISVehicleMenu.VehicleBlacklist[name] then
        return true
    end
    
    return false
end

-- Strict scan: skip the trailer itself and start slightly behind it to avoid self-hit
function MSW_ISVehicleMenu.getVehicleAtRearOfTrailer(trailer)
    if not trailer or not trailer.getWorldPos then return nil end
    local shape = trailer:getScript():getPhysicsChassisShape()
    local zHalf = (shape and shape:z()) and shape:z() / 2 or 0
    local startBehind = 1.0 -- push the first sample 1 tile behind the rear bumper
    for i = 0, 8, 0.5 do
        local point = trailer:getWorldPos(0, 0, -zHalf - startBehind - i, vec)
        local gx = math.floor(point:x() + 0.5)
        local gy = math.floor(point:y() + 0.5)
        local sq = getCell() and getCell():getGridSquare(gx, gy, trailer:getZ() or 0) or nil
        if sq then
            local veh = sq:getVehicleContainer()
            if isRealVehicle(veh) and veh ~= trailer then
                if not (veh.getId and trailer.getId and veh:getId() == trailer:getId()) then
                    -- Check blacklist before returning vehicle
                    if not isVehicleBlacklisted(veh) then
                        return veh
                    end
                end
            end
        end
    end
    return nil
end

local function canLaunchHere(trailer, spawnDist, spawnSize)
    if not trailer or not trailer.getScript then return false end
    local chassisZ = trailer:getScript():getPhysicsChassisShape():z()
    if not chassisZ then return false end
    local point = trailer:getWorldPos(0, 0, -chassisZ/2 - (spawnDist or 4), vec)
    local vehicleSqr = trailer:getSquare()
    for i = 0, (spawnSize or 3) do
        for j = 0, (spawnSize or 3) do
            local tempSqr = getCell():getGridSquare(point:x() + i, point:y() + j, 0)
            if tempSqr == nil or not tempSqr:isFree(true) or tempSqr:isVehicleIntersecting() or tempSqr:isSomethingTo(vehicleSqr) then
                return false
            end
        end
    end
    return true
end

local function safeAddSlice(menu, label, texPath, func, ...)
    if not menu or not label then return end
    local tex = getTexture and getTexture(texPath) or (getTexture and getTexture("media/ui/commonlibrary/no.png")) or nil
    local cb = func or function() end
    menu:addSlice(label, tex, cb, ...)
end

function MSW_ISVehicleMenu.loadOntoTrailer(playerObj, trailer, vehicle, slotIndex)
    -- Final guard: must be a real different vehicle (not the trailer itself)
    if not isRealVehicle(vehicle) then return end
    if vehicle == trailer or (vehicle.getId and trailer.getId and vehicle:getId() == trailer:getId()) then return end
    
    -- Blacklist check: prevent loading blacklisted vehicles
    if isVehicleBlacklisted(vehicle) then
        if playerObj and playerObj.Say then
            playerObj:Say(getText("IGUI_MSW_VehicleBlacklisted"))
        end
        return
    end
    
    if luautils and luautils.walkAdj and luautils.walkAdj(playerObj, trailer:getSquare()) then
        ISTimedActionQueue.add(MSW_ISLoadVehicle:new(playerObj, trailer, vehicle, slotIndex))
    end
end

function MSW_ISVehicleMenu.launchVehicle(playerObj, trailer, slotIndex, spawnDist)
    local chassisZ = trailer:getScript():getPhysicsChassisShape():z()
    local point = trailer:getWorldPos(0, 0, -chassisZ/2 - (spawnDist or 4), vec)
    local sq = getCell():getGridSquare(point:x(), point:y(), 0)
    if not sq then return end
    if luautils and luautils.walkAdj and luautils.walkAdj(playerObj, trailer:getSquare()) then
        ISTimedActionQueue.add(MSW_ISLaunchVehicle:new(playerObj, trailer, sq, slotIndex))
    end
end

-- Wrap only once to avoid recursion/conflicts with other mods
if not MSW_ISVehicleMenu._wrapped then
    MSW_ISVehicleMenu.old_showRadialMenuOutside = ISVehicleMenu.showRadialMenuOutside

    function ISVehicleMenu.showRadialMenuOutside(playerObj)
        -- Let original/other mods render first
        MSW_ISVehicleMenu.old_showRadialMenuOutside(playerObj)

        local ok, err = pcall(function()
            if not playerObj or playerObj:getVehicle() then return end
            local playerIndex = playerObj:getPlayerNum()
            local menu = getPlayerRadialMenu(playerIndex)
            if not menu then return end

            local trailer = ISVehicleMenu.getVehicleToInteractWith(playerObj)
            if not trailer then return end

            -- Only render our items when our part is present
            local msPart = getMultiWreckerPart(trailer)
            if not msPart then return end

            local cfg = getMultiWreckerConfig(msPart)
            local msw = getOrInitMSW(msPart)
            local vehicleBehind = MSW_ISVehicleMenu.getVehicleAtRearOfTrailer(trailer)
            local spawnOK = canLaunchHere(trailer, cfg.spawnDist, cfg.spawnSize)

            -- Single per-slot loop to keep order stable: 1..N
            for i=1, cfg.slots do
                if msw.slots[i] then
                    -- Occupied -> show unload
                     local label = getText("IGUI_VehicleSlot_" .. tostring(i)) .. getText("IGUI_Unload") .. MSW_GetSlotVehicleName(msw.slots[i])
                    if spawnOK then
                        safeAddSlice(menu, label, "media/ui/ata/ata_unload_from_trailer.png",
                            MSW_ISVehicleMenu.launchVehicle, playerObj, trailer, i, cfg.spawnDist)
                    else
                        safeAddSlice(menu, label .. " (kein Platz)", "media/ui/commonlibrary/no.png", nil)
                    end
                else
                    -- Empty -> show load
                    if vehicleBehind then
                        safeAddSlice(menu, getText("IGUI_VehicleSlot_" .. tostring(i)), "media/ui/ata/ata_load_on_trailer.png",
                            MSW_ISVehicleMenu.loadOntoTrailer, playerObj, trailer, vehicleBehind, i)
                        else
                            safeAddSlice(menu, getText("IGUI_VehicleSlot_" .. tostring(i)) .. getText("IGUI_NoVehicle"), "media/ui/commonlibrary/no.png", nil)
                    end
                end
            end
        end)
        if not ok then
            -- print("[MSW] Error extending radial menu: " .. tostring(err))
        end
    end

    MSW_ISVehicleMenu._wrapped = true
end