MudDriveCoordinator = MudDriveCoordinator or {}

local Coordinator = MudDriveCoordinator
Coordinator.states = Coordinator.states or setmetatable({}, {__mode = "k"})

local function getMotor(vehicle)
    if vehicle == nil then return nil end

    if vehicle.getMotor ~= nil then
        local ok, motor = pcall(vehicle.getMotor, vehicle)
        if ok and motor ~= nil then return motor end
    end

    return vehicle.spec_motorized ~= nil and vehicle.spec_motorized.motor or nil
end

local function different(a, b, epsilon)
    if a == nil or b == nil then return a ~= b end
    if a == math.huge or b == math.huge then return a ~= b end
    return math.abs(a - b) > (epsilon or 0.001)
end

local function getState(vehicle)
    local state = Coordinator.states[vehicle]
    if state == nil then
        state = {
            speedSources = {},
            motorLoadSources = {},
            terrainSources = {}
        }
        Coordinator.states[vehicle] = state
    end
    return state
end

function Coordinator:requestTerrainDynamics(vehicle, source, data)
    if vehicle == nil or source == nil then return end
    local state = getState(vehicle)
    if type(data) == "table" and data.active ~= false then
        state.terrainSources[source] = {
            active=true,
            intensity=tonumber(data.intensity) or 0,
            wetness=tonumber(data.wetness) or 0,
            slip=tonumber(data.slip) or 0,
            sink=tonumber(data.sink) or 0,
            time=tonumber(g_time) or 0
        }
    else
        state.terrainSources[source] = nil
    end
end

function Coordinator:getTerrainDynamicsState(vehicle)
    local state = vehicle ~= nil and self.states[vehicle] or nil
    if state == nil then return nil end
    local now = tonumber(g_time) or 0
    local result = {intensity=0, wetness=0, slip=0, sink=0}
    local active = false
    for source, data in pairs(state.terrainSources or {}) do
        if data == nil or now-(data.time or now) > 1500 then
            state.terrainSources[source] = nil
        elseif data.active == true and (data.intensity or 0) > 0.001 then
            active = true
            result.intensity = math.max(result.intensity, tonumber(data.intensity) or 0)
            result.wetness = math.max(result.wetness, tonumber(data.wetness) or 0)
            result.slip = math.max(result.slip, tonumber(data.slip) or 0)
            result.sink = math.max(result.sink, tonumber(data.sink) or 0)
        end
    end
    return active and result or nil
end

function Coordinator:requestSpeedLimit(vehicle, source, limitKph)
    local motor = getMotor(vehicle)
    if motor == nil or motor.setSpeedLimit == nil or source == nil then return end

    local state = getState(vehicle)
    local current = tonumber(motor.speedLimit) or math.huge

    if state.lastSpeedApplied == nil or different(current, state.lastSpeedApplied, 0.02) then
        state.speedBase = current
    elseif state.speedBase == nil then
        state.speedBase = current
    end

    limitKph = tonumber(limitKph)
    if limitKph ~= nil and limitKph > 0 and limitKph < math.huge then
        state.speedSources[source] = limitKph
    else
        state.speedSources[source] = nil
    end

    local target = tonumber(state.speedBase) or math.huge
    local hasSource = false
    for _, value in pairs(state.speedSources) do
        if type(value) == "number" and value > 0 then
            target = math.min(target, value)
            hasSource = true
        end
    end

    if different(current, target, 0.02) then
        motor:setSpeedLimit(target)
    end
    state.lastSpeedApplied = target

    if not hasSource then
        state.speedBase = target
    end
end

function Coordinator:getSpeedLimitBase(vehicle)
    local motor = getMotor(vehicle)
    if motor == nil then return nil end

    local state = self.states[vehicle]
    local base = state ~= nil and tonumber(state.speedBase) or nil
    if base ~= nil and base > 0 and base < math.huge then
        return base
    end

    local current = tonumber(motor.speedLimit)
    if current ~= nil and current > 0 and current < math.huge then
        return current
    end

    if motor.getMaximumForwardSpeed ~= nil then
        local ok, maximum = pcall(motor.getMaximumForwardSpeed, motor)
        if ok and type(maximum) == "number" and maximum > 0 then
            return maximum * 3.6
        end
    end
    return nil
end

function Coordinator:requestMotorLoad(vehicle, source, multiplier)
    local motor = getMotor(vehicle)
    if motor == nil or motor.setExternalTorqueVirtualMultiplicator == nil or source == nil then return end

    local state = getState(vehicle)
    local current = tonumber(motor.externalTorqueVirtualMultiplicator) or 1

    if state.lastMotorLoadApplied == nil or different(current, state.lastMotorLoadApplied, 0.01) then
        state.motorLoadBase = math.max(1, current)
    elseif state.motorLoadBase == nil then
        state.motorLoadBase = math.max(1, current)
    end

    multiplier = tonumber(multiplier)
    if multiplier ~= nil and multiplier > 1.0001 then
        state.motorLoadSources[source] = multiplier
    else
        state.motorLoadSources[source] = nil
    end

    local target = tonumber(state.motorLoadBase) or 1
    local hasSource = false
    for _, value in pairs(state.motorLoadSources) do
        if type(value) == "number" then
            target = math.max(target, value)
            hasSource = true
        end
    end

    if different(current, target, 0.01) then
        motor:setExternalTorqueVirtualMultiplicator(target)
    end
    state.lastMotorLoadApplied = target

    if not hasSource then
        state.motorLoadBase = target
    end
end

function Coordinator:clearSource(vehicle, source)
    if vehicle == nil or source == nil then return end
    self:requestSpeedLimit(vehicle, source, nil)
    self:requestMotorLoad(vehicle, source, nil)
    self:requestTerrainDynamics(vehicle, source, nil)
end

function Coordinator:forgetVehicle(vehicle)
    if vehicle == nil then return end
    vehicle.__mudTransmissionDynamics = nil
    self.states[vehicle] = nil
end

_G.__MudDriveCoordinator = Coordinator

-- Do not intercept VehicleMotor.shiftGear/setGear/shiftGroup/setGearGroup.
-- These functions maintain the base game's target gears, change timers, lock
-- flags and events. Swallowing a rejected upshift can leave powershift and
-- automatic transmissions waiting forever for a change that never completed.
-- Mud influences gearing naturally through wheel resistance, available torque
-- and speed; the transmission itself must remain fully owned by the game.

print("[MudSystemPhysics] shared drive physics coordinator ready")
