-- FS25 Working Width Options - JD v1.0.0.1
-- Direct raw-key F2 toggle. Does not depend on vehicle specialization injection.

JDWidthGuideF2 = JDWidthGuideF2 or {}
JDWidthGuideF2.f2Down = false
JDWidthGuideF2.guideNames = {
    jdWidthGuideLeft = true,
    jdWidthGuideRight = true,
    jdWidthGuidesRoot = true
}

local function jdWWValidNode(node)
    return node ~= nil and node ~= 0 and entityExists(node)
end

local function jdWWCollectGuideNodes(node, nodes, seenNodes)
    if not jdWWValidNode(node) or seenNodes[node] then
        return
    end

    seenNodes[node] = true
    local nodeName = getName(node)
    if JDWidthGuideF2.guideNames[nodeName] then
        nodes[#nodes + 1] = node
    end

    local childCount = getNumOfChildren(node)
    for i = 0, childCount - 1 do
        jdWWCollectGuideNodes(getChildAt(node, i), nodes, seenNodes)
    end
end

local function jdWWAddVehicle(vehicle, vehicles, seenVehicles)
    if vehicle == nil or seenVehicles[vehicle] then
        return
    end

    seenVehicles[vehicle] = true
    vehicles[#vehicles + 1] = vehicle

    if vehicle.getAttachedImplements ~= nil then
        local implements = vehicle:getAttachedImplements()
        if implements ~= nil then
            for _, implement in pairs(implements) do
                if implement ~= nil and implement.object ~= nil then
                    jdWWAddVehicle(implement.object, vehicles, seenVehicles)
                end
            end
        end
    end

    if vehicle.childVehicles ~= nil then
        for _, childVehicle in pairs(vehicle.childVehicles) do
            jdWWAddVehicle(childVehicle, vehicles, seenVehicles)
        end
    end
end

function JDWidthGuideF2.getGuideNodes(vehicle)
    local nodes = {}
    local seenNodes = {}
    local vehicles = {}

    if vehicle == nil then
        return nodes
    end

    jdWWAddVehicle(vehicle.rootVehicle or vehicle, vehicles, {})

    for _, v in ipairs(vehicles) do
        if jdWWValidNode(v.rootNode) then
            jdWWCollectGuideNodes(v.rootNode, nodes, seenNodes)
        end
        if v.components ~= nil then
            for _, component in ipairs(v.components) do
                if component ~= nil and jdWWValidNode(component.node) then
                    jdWWCollectGuideNodes(component.node, nodes, seenNodes)
                end
            end
        end
    end

    return nodes
end

function JDWidthGuideF2.getWorkingWidthVehicles(vehicle)
    local result = {}
    local vehicles = {}

    if vehicle == nil then
        return result
    end

    jdWWAddVehicle(vehicle.rootVehicle or vehicle, vehicles, {})

    for _, v in ipairs(vehicles) do
        if v ~= nil and (v.jdWorkingWidthCurrentWidth ~= nil or v.jdWorkingWidthHasGuides or v.jdWorkingWidthGuideLeft ~= nil or v.jdWorkingWidthGuideRight ~= nil) then
            result[#result + 1] = v
        end
    end

    return result
end

function JDWidthGuideF2.getHudVehicle(vehicle)
    local vehicles = JDWidthGuideF2.getWorkingWidthVehicles(vehicle)

    -- Prefer the active mower over balers, windrowers or other WorkArea tools in
    -- the same vehicle train. This keeps the F1 number tied to the actual mower
    -- cutting width instead of whichever implement happened to load first.
    for _, v in ipairs(vehicles) do
        if v ~= nil and v.spec_mower ~= nil and (v.jdWorkingWidthHasGuides or v.jdWorkingWidthCurrentWidth ~= nil) then
            return v
        end
    end

    for _, v in ipairs(vehicles) do
        if v ~= nil and (v.jdWorkingWidthHasGuides or v.jdWorkingWidthCurrentWidth ~= nil) then
            return v
        end
    end
    return nil
end

function JDWidthGuideF2.getGuideVisibilityForVehicle(vehicle)
    if vehicle == nil then
        return false
    end

    local left = vehicle.jdWorkingWidthGuideLeft
    local right = vehicle.jdWorkingWidthGuideRight
    if jdWWValidNode(left) then
        return getVisibility(left)
    end
    if jdWWValidNode(right) then
        return getVisibility(right)
    end
    if vehicle.jdWorkingWidthGuidesVisible ~= nil then
        return vehicle.jdWorkingWidthGuidesVisible
    end
    return false
end

function JDWidthGuideF2.toggleForCurrentVehicle()
    if g_currentMission == nil or g_localPlayer == nil or g_localPlayer.getCurrentVehicle == nil then
        return false
    end

    local vehicle = g_localPlayer:getCurrentVehicle()
    if vehicle == nil then
        return false
    end

    local nodes = JDWidthGuideF2.getGuideNodes(vehicle)
    if #nodes == 0 then
        Logging.info('[JDWorkingWidth] F2 pressed - no working-width guide nodes found on current vehicle')
        return false
    end

    local anyVisible = false
    for _, node in ipairs(nodes) do
        if jdWWValidNode(node) and getVisibility(node) then
            anyVisible = true
            break
        end
    end

    local newVisibility = not anyVisible
    for _, node in ipairs(nodes) do
        if jdWWValidNode(node) then
            setVisibility(node, newVisibility)
        end
    end

    for _, v in ipairs(JDWidthGuideF2.getWorkingWidthVehicles(vehicle)) do
        v.jdWorkingWidthGuidesVisible = newVisibility
    end

    Logging.info('[JDWorkingWidth] RAW F2 guides %s (%d nodes)', newVisibility and 'ON' or 'OFF', #nodes)

    if g_currentMission.addExtraPrintText ~= nil then
        g_currentMission:addExtraPrintText(string.format('Working Width Guides: %s', newVisibility and 'ON' or 'OFF'))
    end

    local rootVehicle = vehicle.rootVehicle or vehicle
    if rootVehicle ~= nil and rootVehicle.requestActionEventUpdate ~= nil then
        rootVehicle:requestActionEventUpdate()
    end

    return true
end

function JDWidthGuideF2:keyEvent(unicode, sym, modifier, isDown)
    if sym ~= Input.KEY_f2 then
        return
    end

    if isDown then
        if not self.f2Down then
            self.f2Down = true
            JDWidthGuideF2.toggleForCurrentVehicle()
        end
    else
        self.f2Down = false
    end
end


function JDWidthGuideF2:loadMap(mapName)
    self.f2Down = false
    Logging.info('[JDWorkingWidth] v1.0.0.1 RAW F2 listener active')
end

function JDWidthGuideF2:deleteMap()
    self.f2Down = false
end

addModEventListener(JDWidthGuideF2)
Logging.info('[JDWorkingWidth] v1.0.0.1 F2Toggle.lua loaded')
