-- REAwheels_fixed.lua
-- FS25 REA Wheels Physiksystem
-- Schlupf, Gripverlust, Bodenverformung (T0/T2/T3)

REAwheels = {}

-- Neutral reference pressure ("bar-like") used by EV virtual tire pressure.
-- EV typically operates in ~0.8 .. 2.2.
local REA_TP_NEUTRAL = 1.6

local function clamp(v, mn, mx)
    if v < mn then return mn end
    if v > mx then return mx end
    return v
end

-- Reads axle pressure coming from EnhancedVehicle (EV).
-- If EV is not present, returns neutral.
local function getAxlePressure(vehicle, isFront)
    local pSpec = vehicle.spec_reaPlusVirtualPressure
    if pSpec ~= nil then
        if isFront and pSpec.tpFront ~= nil then
            return pSpec.tpFront
        elseif (not isFront) and pSpec.tpRear ~= nil then
            return pSpec.tpRear
        end
    end
    return REA_TP_NEUTRAL
end

-- Converts pressure into a normalized factor around 1.0.
local function pressureNorm(p)
    return clamp(p / REA_TP_NEUTRAL, 0.5, 1.6)
end

local function wPrint(msg)
    print(("REAwheels: %s"):format(tostring(msg)))
end

function REAwheels.prerequisitesPresent(specializations)
    return true
end

function REAwheels.registerEventListeners(vehicleType)
    SpecializationUtil.registerEventListener(vehicleType, "onLoad", REAwheels)
    SpecializationUtil.registerEventListener(vehicleType, "onUpdate", REAwheels)
    SpecializationUtil.registerEventListener(vehicleType, "onDelete", REAwheels)
end

function REAwheels:onLoad(savegame)
    self.spec_REAwheels = {
        sink = 0,
        slip = 0,
        active = true,
        deformMode = 0 -- 0=T0, 2=T2, 3=T3
    }

    -- STRG + NUM7 zum Umschalten
    local _, eventId = InputBinding.registerActionEvent(
        self,
        "REA_SWITCH_DEFORM",
        InputBinding.IMPLEMENT_EXTRA3,
        true, true, false, true
    )
    if eventId then
        InputBinding.setActionEventText(eventId, "REA Bodenmodus wechseln (T0/T2/T3)")
    end

    wPrint("Aktiv für " .. (self.getName and self:getName() or "<vehicle>"))
end

function REAwheels:onDelete()
    self.spec_REAwheels = nil
end

function REAwheels:REA_SWITCH_DEFORM()
    local spec = self.spec_REAwheels
    if spec == nil then return end

    if spec.deformMode == 0 then
        spec.deformMode = 2
        wPrint("REA: Bodenmodus T2 aktiv (2.0x temporäre Verformung)")
    elseif spec.deformMode == 2 then
        spec.deformMode = 3
        wPrint("REA: Bodenmodus T3 aktiv (2.5x permanente Verformung, nur schwere Maschinen)")
    else
        spec.deformMode = 0
        wPrint("REA: Bodenmodus deaktiviert (T0)")
    end
end

function REAwheels:onUpdate(dt)
    local spec = self.spec_REAwheels
    if spec == nil or not spec.active then return end

    local wheels = self.spec_wheels and self.spec_wheels.wheels
    if wheels == nil then return end

    local maxSlip, maxSink = 0, 0
    local mass = self.getTotalMass and self:getTotalMass() or 8000
    local wheelCount = #wheels

    -- We store a rolling resistance multiplier for other REA modules (engine load).
    local rrSum, rrN = 0, 0

    local deformFactor = 1.0 -- default

    for _, wheel in ipairs(wheels) do
        if wheel ~= nil and wheel.lastSlip ~= nil then
            local slip = math.abs(wheel.lastSlip or 0)
            maxSlip = math.max(maxSlip, slip)

            if wheel.isDriveWheel then
                -- Bodentyp bestimmen
                local x, y, z = getWorldTranslation(wheel.node)
                local terrainType = getTerrainSurfaceTypeAtWorldPos(x, y, z)

                local allowT3 = (
                       terrainType == TerrainUtil.SURFACE_TYPES.DIRT
                    or terrainType == TerrainUtil.SURFACE_TYPES.FIELD_DIRT
                    or terrainType == TerrainUtil.SURFACE_TYPES.FIELD_LOOSE
                    or terrainType == TerrainUtil.SURFACE_TYPES.FIELD_PLOWED
                    or terrainType == TerrainUtil.SURFACE_TYPES.FIELD_CULTIVATED
                    or terrainType == TerrainUtil.SURFACE_TYPES.FOREST
                    or terrainType == TerrainUtil.SURFACE_TYPES.GRASS_EDGE
                )

                if spec.deformMode == 2 then
                    deformFactor = 2.0 -- T2
                elseif spec.deformMode == 3 and mass > 8000 and allowT3 then
                    deformFactor = 2.5 -- T3 für schwere Fahrzeuge auf erlaubtem Terrain
                else
                    deformFactor = 1.0 -- T0
                end

                local wet = wheel.wetnessMultiplier or 0
                local baseSinkAdd = slip * 0.012 * (wet + 0.2)
                local sinkAdd = baseSinkAdd * deformFactor

                --------------------------------------------------------------------
                -- EV "virtual tire pressure" (axle-specific) -> more realistic effect
                -- Key realism points:
                --  * Pressure influences footprint mainly under higher wheel loads.
                --  * Low pressure reduces sink on soft ground, but increases rolling resistance.
                --------------------------------------------------------------------

                local isFront = (wheel.positionZ or 0) > 0
                local p = getAxlePressure(self, isFront)
                local pN = pressureNorm(p) -- normalized around 1.0

                -- Approx. wheel load normalization (simple & stable):
                -- heavier vehicles / fewer wheels -> stronger pressure effect.
                local loadPerWheel = mass / math.max(wheelCount, 1)
                local loadN = clamp(loadPerWheel / 2500, 0.3, 1.3) -- ~2.5t per wheel neutral

                -- Footprint/contact factor: low pressure + high load => noticeably larger footprint.
                local contact = clamp(1.0 + (1.0 - pN) * 0.9 * loadN, 0.7, 1.6)

                -- Sink multiplier: larger footprint => less sink. Higher pressure => more sink.
                local sinkMul = clamp(1.15 - (contact - 1.0) * 0.75, 0.6, 1.4)
                sinkAdd = sinkAdd * sinkMul

                -- Rolling resistance ("walk" losses): low pressure costs more on road/speed.
                -- We compute a speed-dependent multiplier and store it on the vehicle.
                local speedKmh = (self.getLastSpeed and (self:getLastSpeed() or 0) * 3.6) or 0
                local speedN = clamp(speedKmh / 50, 0, 1)
                local rollMul = clamp(1.0 + (1.0 - pN) * (0.25 + 0.55 * speedN), 0.9, 1.8)
                rrSum = rrSum + rollMul
                rrN = rrN + 1

                wheel.sink = math.min((wheel.sink or 0) + sinkAdd, 0.15) -- MAX 15cm
                maxSink = math.max(maxSink, wheel.sink)
            else
                wheel.sink = wheel.sink and math.max(wheel.sink - 0.002, 0) or 0
            end
        end
    end

    spec.slip = maxSlip
    spec.sink = maxSink

    -- Expose rolling resistance for other modules (e.g. engine realism).
    if rrN > 0 then
        self.reaPressureRollMul = rrSum / rrN
    else
        self.reaPressureRollMul = 1.0
    end
end
