-- Подключаемся к глобальной таблице
RadialVehicleSwitcher = RadialVehicleSwitcher or {}

-- [ЛОГ] Инициализация модуля данных
-- print("--> [RVS Debug] Модуль Data.lua успешно загружен")

-- Локальный хелпер локализации (аналог UI.lua)
local function getL10n(key, fallback)
    if g_i18n ~= nil and g_i18n:hasText(key) then
        return g_i18n:getText(key)
    end
    return fallback
end

-- === 1. ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ (ID, КАТЕГОРИИ, ЗАПОЛНЕНИЕ) ===

function RadialVehicleSwitcher:getVehicleId(vehicle)
    if vehicle == nil then
        return nil
    end
    if vehicle.radialId ~= nil then
        return vehicle.radialId
    end
    local config = vehicle.configFileName or "unknown"
    local match = string.match(config, "([^/]+)%.xml$")
    if match then
        config = match
    end
    local count = 1
    local vehiclesList = g_currentMission.vehicleSystem ~= nil and g_currentMission.vehicleSystem.vehicles or
                             g_currentMission.vehicles
    if vehiclesList ~= nil then
        for _, v in pairs(vehiclesList) do
            if v == vehicle then
                break
            end
            if type(v) == "table" and (v.spec_motorized ~= nil or (v.getIsEnterable ~= nil and v:getIsEnterable())) then
                local vConfig = v.configFileName or "unknown"
                local vMatch = string.match(vConfig, "([^/]+)%.xml$")
                if vMatch then
                    vConfig = vMatch
                end
                if vConfig == config then
                    count = count + 1
                end
            end
        end
    end
    vehicle.radialId = config .. "_" .. tostring(count)
    return vehicle.radialId
end

function RadialVehicleSwitcher:getCategoryByIndex(index)
    if self.currentVehicles == nil then
        return {}
    end
    self.currentCategoryIndex = index
    -- Сдвигаем все категории, чтобы "РЯДОМ" было на первом (самом удобном верхнем) месте
    if index == 1 then
        return self.currentVehicles.NEARBY
    end
    if index == 2 then
        return self.currentVehicles.FAVORITES
    end
    if index == 3 then
        return self.currentVehicles.WORKERS
    end
    if index == 4 then
        return self.currentVehicles.TRACTORS
    end
    if index == 5 then
        return self.currentVehicles.COMBINES
    end
    if index == 6 then
        return self.currentVehicles.TRUCKS
    end
    if index == 7 then
        return self.currentVehicles.LOADERS
    end
    if index == 8 then
        return self.currentVehicles.CARS
    end
    if index == 9 then
        return self.currentVehicles.MISC
    end
    return {}
end

function RadialVehicleSwitcher:getVehicleTreeFill(vehicle)
    local tFill, tCap = 0, 0
    local dominantFillType = nil
    local maxFill = -1
    if vehicle == nil then
        return tFill, tCap, dominantFillType
    end
    local function scan(v)
        if v.getFillUnits ~= nil and v.getFillUnitFillLevel ~= nil then
            local units = v:getFillUnits()
            if units ~= nil then
                for i = 1, #units do
                    local isFuel = false
                    local fillType = FillType.UNKNOWN
                    if FillType ~= nil and v.getFillUnitFillType ~= nil then
                        fillType = v:getFillUnitFillType(i)
                        if fillType == FillType.UNKNOWN or fillType == FillType.DIESEL or fillType == FillType.DEF or
                            fillType == FillType.AIR or fillType == FillType.METHANE or fillType ==
                            FillType.ELECTRICCHARGE then
                            isFuel = true
                        end
                    end
                    if not isFuel then
                        local cap = v:getFillUnitCapacity(i) or 0
                        if cap > 50 then
                            local lvl = v:getFillUnitFillLevel(i) or 0
                            tFill = tFill + lvl
                            tCap = tCap + cap
                            if lvl > maxFill and fillType ~= FillType.UNKNOWN then
                                maxFill = lvl
                                dominantFillType = fillType
                            end
                        end
                    end
                end
            end
        end
        if v.getAttachedImplements ~= nil then
            local implements = v:getAttachedImplements()
            if implements ~= nil then
                for _, impl in pairs(implements) do
                    if impl.object ~= nil then
                        scan(impl.object)
                    end
                end
            end
        end
    end
    scan(vehicle)
    return tFill, tCap, dominantFillType
end

function RadialVehicleSwitcher:getVehicleCategory(vehicle)
    if vehicle == nil then
        return "MISC"
    end
    if vehicle.configFileName ~= nil and g_storeManager ~= nil then
        local storeItem = g_storeManager:getItemByXMLFilename(vehicle.configFileName)
        if storeItem ~= nil and storeItem.categoryName ~= nil then
            local cat = storeItem.categoryName:upper()
            if cat:find("TRACTOR") then
                return "TRACTOR"
            end
            if cat:find("TRUCK") then
                return "TRUCK"
            end
            if cat:find("CAR") then
                return "CAR"
            end
            if cat:find("LOADER") or cat == "SKIDSTEERVEHICLES" or cat == "FRONTLOADERVEHICLES" or cat == "FORKLIFTS" then
                return "LOADER"
            end
            if cat:find("HARVESTER") or cat == "BEETVEHICLES" or cat == "POTATOVEHICLES" or cat == "COTTONVEHICLES" or
                cat == "SUGARCANEVEHICLES" or cat == "GRAPEVINEVEHICLES" or cat == "MOWERVEHICLES" then
                return "COMBINE"
            end
            return "MISC"
        end
    end
    return "MISC"
end

function RadialVehicleSwitcher:scanVehicles()
    local categories = {
        NEARBY = {},
        FAVORITES = {},
        WORKERS = {},
        TRACTORS = {},
        COMBINES = {},
        TRUCKS = {},
        CARS = {},
        LOADERS = {},
        MISC = {},
        ALL = {}
    }
    local vehiclesList = g_currentMission.vehicleSystem ~= nil and g_currentMission.vehicleSystem.vehicles or
                             g_currentMission.vehicles
    if vehiclesList == nil then
        return categories
    end
    local playerFarmId = g_currentMission:getFarmId()
    local px, py, pz = 0, 0, 0
    local hasPlayerPos = false
    local currentVehicle = g_currentMission.controlledVehicle or (g_localPlayer and g_localPlayer:getCurrentVehicle())
    if currentVehicle ~= nil then
        px, py, pz = getWorldTranslation(currentVehicle.rootNode or currentVehicle.components[1].node)
        hasPlayerPos = true
    elseif g_localPlayer ~= nil and g_localPlayer.isControlled and g_localPlayer.rootNode ~= nil then
        px, py, pz = getWorldTranslation(g_localPlayer.rootNode)
        hasPlayerPos = true
    end
    for _, vehicle in pairs(vehiclesList) do
        if type(vehicle) == "table" and
            (vehicle.spec_motorized ~= nil or (vehicle.getIsEnterable ~= nil and vehicle:getIsEnterable())) then
            local ownerFarm = vehicle.getOwnerFarmId ~= nil and vehicle:getOwnerFarmId() or 0
            if ownerFarm == playerFarmId then
                local vehicleInfo = {
                    vehicle = vehicle,
                    name = vehicle.getFullName ~= nil and vehicle:getFullName() or "Unknown",
                    category = self:getVehicleCategory(vehicle),
                    id = self:getVehicleId(vehicle),
                    isAI = ((vehicle.getIsAIActive ~= nil and vehicle:getIsAIActive()) or
                        (vehicle.spec_aiFieldWorker ~= nil and vehicle.spec_aiFieldWorker.isActive)) or false,
                    implements = (vehicle.getAttachedImplements ~= nil and self:getTopImplements(vehicle) or {}),
                    distance = 999999
                }
                if hasPlayerPos then
                    local vx, vy, vz = getWorldTranslation(vehicle.rootNode or vehicle.components[1].node)
                    vehicleInfo.distance = math.sqrt((px - vx) ^ 2 + (py - vy) ^ 2 + (pz - vz) ^ 2)
                end
                table.insert(categories.ALL, vehicleInfo)
                if self.favoritesMap and self.favoritesMap[vehicleInfo.id] then
                    table.insert(categories.FAVORITES, vehicleInfo)
                end
                if vehicleInfo.isAI then
                    table.insert(categories.WORKERS, vehicleInfo)
                end
                local cat = vehicleInfo.category
                if categories[cat .. "S"] then
                    table.insert(categories[cat .. "S"], vehicleInfo)
                elseif cat == "TRACTOR" then
                    table.insert(categories.TRACTORS, vehicleInfo)
                else
                    table.insert(categories.MISC, vehicleInfo)
                end
            end
        end
    end
    if hasPlayerPos then
        for _, vInfo in ipairs(categories.ALL) do
            -- Исключаем из РЯДОМ технику, в которой сидим прямо сейчас
            if vInfo.vehicle ~= currentVehicle then
                table.insert(categories.NEARBY, vInfo)
            end
        end
        table.sort(categories.NEARBY, function(a, b)
            return a.distance < b.distance
        end)
        while #categories.NEARBY > 8 do
            table.remove(categories.NEARBY)
        end
    end
    return categories
end

function RadialVehicleSwitcher:getTopImplements(vehicle)
    local result = {}
    local attached = vehicle:getAttachedImplements()

    if attached ~= nil then
        for _, impl in pairs(attached) do
            if impl.object ~= nil then
                local obj = impl.object
                table.insert(result, obj)

                -- print(("========== IMPLEMENT: %s =========="):format(tostring(
                --     obj.getName ~= nil and obj:getName() or obj.configFileName)))

                -- 1) Обычные allowed fill types из fillUnit
                local specFillUnit = obj.spec_fillUnit
                if specFillUnit ~= nil and specFillUnit.fillUnits ~= nil then
                    for fillUnitIndex, fillUnit in ipairs(specFillUnit.fillUnits) do
                        local supported = fillUnit.supportedFillTypes or fillUnit.fillTypes
                        local finalNames = {}
                        local seen = {}

                        if supported ~= nil then
                            for fillTypeIndex, value in pairs(supported) do
                                if value == true or type(value) == "table" then
                                    local name = g_fillTypeManager:getFillTypeNameByIndex(fillTypeIndex)
                                    if name ~= nil and not seen[name] then
                                        seen[name] = true
                                        table.insert(finalNames, name)
                                    end
                                end
                            end
                        end

                        table.sort(finalNames)

                        if #finalNames > 0 then
                            -- print(
                            --     ("FillUnit %d allowed types: %s"):format(fillUnitIndex, table.concat(finalNames, ", ")))
                        else
                            -- print(("FillUnit %d allowed types: none"):format(fillUnitIndex))
                        end
                    end
                else
                    -- print("This implement does not support fill units")
                end

                local specSowingMachine = obj.spec_sowingMachine
                if specSowingMachine ~= nil then
                    obj.rvsSeedPaths = nil -- сбрасываем, будем хранить одну иконку

                    local iconPath = nil
                    local fruitType = nil

                    -- ПОПЫТКА 1: getSowingMachineSeedFillTypeIndex — самый надёжный способ
                    if obj.getSowingMachineSeedFillTypeIndex ~= nil then
                        local fillTypeIndex = obj:getSowingMachineSeedFillTypeIndex()
                        local fillType = g_fillTypeManager:getFillTypeByIndex(fillTypeIndex)
                        if fillType ~= nil and fillType.hudOverlayFilename ~= nil then
                            iconPath = fillType.hudOverlayFilename
                        end
                    end

                    -- ПОПЫТКА 2: через currentSeed слот
                    if iconPath == nil and specSowingMachine.currentSeed ~= nil and specSowingMachine.seeds ~= nil then
                        local fruitTypeIndex = specSowingMachine.seeds[specSowingMachine.currentSeed]
                        fruitType = fruitTypeIndex ~= nil and g_fruitTypeManager:getFruitTypeByIndex(fruitTypeIndex) or
                                        nil
                        if fruitType ~= nil then
                            if fruitType.fillType ~= nil and fruitType.fillType.hudOverlayFilename ~= nil then
                                iconPath = fruitType.fillType.hudOverlayFilename
                            elseif g_fillTypeManager ~= nil then
                                local ft = g_fillTypeManager:getFillTypeByName(fruitType.name)
                                if ft ~= nil and ft.hudOverlayFilename ~= nil then
                                    iconPath = ft.hudOverlayFilename
                                end
                            end
                        end
                    end

                    if iconPath ~= nil and iconPath ~= "" then
                        obj.rvsSeedPaths = iconPath -- одна строка, не таблица
                        -- print(
                        --     ("[RVS] Текущая культура сеялки -> иконка: %s"):format(iconPath))
                    else
                        -- print(
                        --     "[RVS] Не удалось найти иконку текущей культуры сеялки")
                    end
                end

                if #result >= 2 then
                    break
                end
            end
        end
    end

    return result
end

-- === 1. УМНЫЙ ФИЛЬТР ТЕХНИКИ ===
function RadialVehicleSwitcher:getVehicleTasks(vehicle, fieldFruitIndex)
    fieldFruitIndex = fieldFruitIndex or 0

    local tasks = {}
    local isCombine = false
    local hasHarvestingCapability = false

    local function scan(v)
        -- ПЛУГ (реальная пахота с оборотом пласта)
        if v.spec_plow ~= nil then
            tasks["PLOW"] = true
        end

        -- КУЛЬТИВАЦИЯ (подготовка почвы без оборота)
        if v.spec_cultivator ~= nil then
            tasks["CULTIVATE"] = true
        end

        -- КОМБИНИРОВАННЫЕ СЕЯЛКИ (культивируют, сеют, удобряют)
        if v.spec_fertilizingSowingMachine ~= nil then
            tasks["CULTIVATE"] = true -- они готовят почву
            tasks["SOW"] = true -- они сеют
            tasks["FERTILIZE"] = true -- они удобряют
        end

        -- ОБЫЧНАЯ СЕЯЛКА (только сеет, почва должна быть готова)
        if v.spec_sowingMachine ~= nil and v.spec_fertilizingSowingMachine == nil then
            tasks["SOW"] = true
        end

        -- УДОБРЕНИЕ (опрыскиватели, разбрасыватели)
        if v.spec_sprayer ~= nil or v.spec_spreader ~= nil then
            tasks["FERTILIZE"] = true
        end

        -- ПРОПОЛКА
        if v.spec_weeder ~= nil then
            tasks["WEED"] = true
        end

        -- ИЗВЕСТКОВАНИЕ
        if v.spec_lime ~= nil then
            tasks["LIME"] = true
        end

        -- КОМБАЙН (уборка)
        if v.spec_combine ~= nil or v.spec_fruitPreparer ~= nil then
            isCombine = true
        end

        -- Рекурсивно проверяем прицепы
        if v.getAttachedImplements ~= nil then
            local implements = v:getAttachedImplements()
            if implements ~= nil then
                for _, impl in pairs(implements) do
                    if impl.object ~= nil then
                        scan(impl.object)
                    end
                end
            end
        end
    end

    scan(vehicle)

    -- УНИВЕРСАЛЬНАЯ ПРОВЕРКА HARVEST
    if isCombine then
        local fruitDict = self:getHarvestableFruitIds(vehicle)
        if fruitDict then
            if fieldFruitIndex > 0 then
                if fruitDict[fieldFruitIndex] then
                    tasks["HARVEST"] = true
                end
            else
                tasks["HARVEST"] = true
            end
        end
    end

    return tasks
end

function RadialVehicleSwitcher:testVehicleAtPlayerPosition(vehicle, px, pz, rotY)
    if vehicle == nil or g_currentMission.aiJobTypeManager == nil then
        return false
    end
    local jobFieldWork = g_currentMission.aiJobTypeManager:createJob(2)
    if jobFieldWork ~= nil then
        local farmId = vehicle:getOwnerFarmId() or g_currentMission:getFarmId() or 1
        if jobFieldWork.applyCurrentState ~= nil then
            jobFieldWork:applyCurrentState(vehicle, g_currentMission, farmId, false)
        end
        if jobFieldWork.vehicleParameter ~= nil then
            jobFieldWork.vehicleParameter:setVehicle(vehicle)
        end
        if jobFieldWork.positionAngleParameter ~= nil then
            jobFieldWork.positionAngleParameter:setPosition(px, pz)
            jobFieldWork.positionAngleParameter:setAngle(rotY)
        end
        if jobFieldWork.setValues ~= nil then
            jobFieldWork:setValues()
        end
        if jobFieldWork.validate ~= nil then
            local success, _ = jobFieldWork:validate(farmId)
            return success
        end
    end
    return false
end

-- === 3. ОСНОВНАЯ ФУНКЦИЯ ОБНОВЛЕНИЯ КАТЕГОРИЙ COMMAND ===
function RadialVehicleSwitcher:updateCommandCategories()
    -- print("--> [RVS Debug] ВЫЗОВ: updateCommandCategories")

    -- === ДАМП lastFieldData: смотрим что реально приходит из хука ===
    local _raw = RadialVehicleSwitcher.lastFieldData
    if _raw == nil then
        -- print("[RVS DUMP] lastFieldData = nil (хук ни разу не сработал)")
    else
        -- print("[RVS DUMP] lastFieldData keys:")
        for k, v in pairs(_raw) do
            local vt = type(v)
            if vt == "function" then
                -- print(string.format("  [%s] = <function>", tostring(k)))
            elseif vt == "table" then
                -- print(string.format("  [%s] = <table>", tostring(k)))
            else
                -- print(string.format("  [%s] = %s  (%s)", tostring(k), tostring(v), vt))
            end
        end
    end
    -- ============================================================

    local px, py, pz = 0, 0, 0
    local rotY = 0
    local node = (g_currentMission.controlledVehicle and g_currentMission.controlledVehicle.rootNode) or
                     (g_localPlayer and g_localPlayer.rootNode)

    if node ~= nil then
        px, py, pz = getWorldTranslation(node)
        local dx, _, dz = localDirectionToWorld(node, 0, 0, 1)
        rotY = MathUtil.getYRotationFromDirection(dx, dz)
        py = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, px, 0, pz)
    end

    -- === ДАННЫЕ ПОЛЯ ТОЛЬКО ИЗ ХУКА fieldAddFarmland ===
    -- Точно так же как additionalFieldInfo.lua — никаких прямых вызовов FSDensityMapUtil.
    -- data.farmlandId > 0 означает что игрок стоит на поле (любом, даже чужом).
    local raw = RadialVehicleSwitcher.lastFieldData
    local isField = (raw ~= nil and raw.farmlandId ~= nil and raw.farmlandId > 0)

    local d = {
        fertilizerFactor = 0,
        weedFactor = 0,
        fruitIndex = 0,
        growthState = 0,
        isReadyToHarvest = false,
        needsPlowing = false,
        needsSowing = false
    }

    if isField and raw ~= nil then
        -- fruitTypeIndex = текущая культура (0 если поле пустое после уборки)
        d.fruitIndex = raw.fruitTypeIndex or 0
        d.growthState = raw.growthState or 0
        -- sprayLevel: 0=нет, 1=частично, 2=полностью. Нормализуем в 0..1
        d.fertilizerFactor = math.min((raw.sprayLevel or 0) / 2.0, 1.0)
        -- weedFactor уже 0..1
        d.weedFactor = raw.weedFactor or 0
        -- plowLevel: 0=не вспахано (нужна вспашка), 1=вспахано
        d.needsPlowing = ((raw.plowLevel or 1) == 0)
        d.needsSowing = (d.fruitIndex == 0)

        -- ИСПРАВЛЕННАЯ ЛОГИКА: Готовность к уборке
        d.isReadyToHarvest = false
        -- Если культуры нет совсем (fruitIndex==0) — поле считается убранным/пустым
        d.isHarvested = (d.fruitIndex == 0)

        if d.fruitIndex > 0 and d.growthState > 0 and g_fruitTypeManager ~= nil then
            local fType = g_fruitTypeManager:getFruitTypeByIndex(d.fruitIndex)
            if fType ~= nil and fType.numGrowthStates ~= nil then
                local maxState = fType.numGrowthStates

                -- Если стадия БОЛЬШЕ максимальной - поле уже убрано
                if d.growthState > maxState then
                    d.isHarvested = true
                    d.isReadyToHarvest = false
                    -- print(string.format("[RVS] ПОЛЕ УБРАНО: стадия %d > %d", d.growthState, maxState))

                    -- Если стадия РАВНА максимальной - готово к уборке
                elseif d.growthState == maxState then
                    -- Дополнительно проверяем growthStateToName для надежности
                    if fType.growthStateToName and fType.growthStateToName[d.growthState] == "harvestReady" then
                        d.isReadyToHarvest = true
                        -- print(string.format("[RVS] ГОТОВО К УБОРКЕ: стадия %d == %d", d.growthState,
                        --     maxState))
                    else
                        -- Если нет growthStateToName, но стадия максимальная - считаем готовым
                        d.isReadyToHarvest = true
                        -- print(string.format("[RVS] ГОТОВО К УБОРКЕ (по стадиям): %d == %d",
                        --     d.growthState, maxState))
                    end
                else
                    -- print(string.format("[RVS] РАСТЁТ: стадия %d < %d", d.growthState, maxState))
                end
            end
        end
    end

    local fruitName = "Нет / Неизвестно"
    if d.fruitIndex > 0 and g_fruitTypeManager ~= nil then
        local fType = g_fruitTypeManager:getFruitTypeByIndex(d.fruitIndex)
        if fType ~= nil then
            fruitName = fType.name
            if g_i18n:hasText(fruitName) then
                fruitName = g_i18n:getText(fruitName)
            end
        end
    end

    -- print("==== RVS: СТАТУС ПОЛЯ ====")
    -- print(string.format("Находимся на поле: %s", tostring(isField)))
    -- print(string.format("Культура: %s", fruitName))
    -- print(string.format("Готово к уборке: %s", tostring(d.isReadyToHarvest)))
    -- print(string.format("Удобрение: %d%%", math.floor(d.fertilizerFactor * 100 + 0.5)))
    -- print(string.format("Сорняки: %d%%", math.floor(d.weedFactor * 100 + 0.5)))
    -- Расшифровка groundType для лога
    local groundTypeNames = {
        [0] = "пусто",
        [1] = "вспахано",
        [2] = "культивировано",
        [3] = "засеяно",
        [4] = "прорастает",
        [5] = "растёт (1)",
        [6] = "растёт (2)",
        [7] = "растёт (3)",
        [8] = "растёт (4)",
        [9] = "растёт (5)",
        [10] = "растёт (6)",
        [11] = "растёт (7)",
        [12] = "растёт (8)",
        [13] = "готово к уборке",
        [14] = "убрано/стерня"
    }
    local gtRaw = raw and raw.groundType
    local gtName = (gtRaw ~= nil and groundTypeNames[gtRaw]) or ("неизвестно (" .. tostring(gtRaw) .. ")")
    local sprayDesc = "нет"
    if raw and raw.sprayLevel == 1 then
        sprayDesc = "частично"
    elseif raw and raw.sprayLevel == 2 then
        sprayDesc = "полностью"
    end
    -- print(string.format(
    --     "Нужна пахота: %s | Нужен посев: %s | Удобрение: %s | Состояние: %s | Стадия роста: %s",
    --     tostring(d.needsPlowing), tostring(d.needsSowing), sprayDesc, gtName, tostring(d.growthState)))
    -- print("==========================")

    local tasks = {{
        id = "PLOW",
        name = getL10n("RVS_TASK_PLOW", "ВСПАХАТЬ"),
        color = {0.6, 0.4, 0.2}
    }, {
        id = "CULTIVATE",
        name = getL10n("RVS_TASK_CULTIVATE", "КУЛЬТИВИРОВАТЬ"),
        color = {0.8, 0.6, 0.2}
    }, {
        id = "SOW",
        name = getL10n("RVS_TASK_SOW", "ЗАСЕЯТЬ"),
        color = {0.2, 0.8, 0.2}
    }, {
        id = "FERTILIZE",
        name = getL10n("RVS_TASK_FERTILIZE", "УДОБРИТЬ"),
        color = {0.2, 0.6, 1.0}
    }, {
        id = "WEED",
        name = getL10n("RVS_TASK_WEED", "ПРОПОЛОТЬ"),
        color = {0.8, 0.2, 0.2}
    }, {
        id = "LIME",
        name = getL10n("RVS_TASK_LIME", "ИЗВЕСТКОВАТЬ"),
        color = {0.9, 0.9, 0.9}
    }, {
        id = "HARVEST",
        name = getL10n("RVS_TASK_HARVEST", "СОБРАТЬ"),
        color = {1.0, 0.8, 0.1}
    }}

    self.commandCategories = {}
    local allVehicles = (self.currentVehicles and self.currentVehicles.ALL) or {}
    self.validationCache = {}

    for _, taskData in ipairs(tasks) do
        local validVehicles = {}

        for _, vInfo in ipairs(allVehicles) do
            local rootVehicle = vInfo.vehicle.getRootVehicle ~= nil and vInfo.vehicle:getRootVehicle() or vInfo.vehicle

            local vTasks = self:getVehicleTasks(rootVehicle, d.fruitIndex)

            if vTasks[taskData.id] then
                local vId = vInfo.id or tostring(rootVehicle)
                local isValidNow = false
                if self.validationCache[vId] ~= nil then
                    isValidNow = self.validationCache[vId]
                else
                    isValidNow = self:testVehicleAtPlayerPosition(rootVehicle, px, pz, rotY)
                    self.validationCache[vId] = isValidNow
                end
                if isValidNow then
                    table.insert(validVehicles, vInfo)
                end
            end
        end

        local isAvailable = (#validVehicles > 0)

        if not isField then
            -- Вырубаем все кнопки, если игрок не на поле
            isAvailable = false
        else
            -- === HARVEST: только если поле реально готово к уборке ===
            if taskData.id == "HARVEST" then
                if not d.isReadyToHarvest then
                    isAvailable = false
                end
            end

            -- === CULTIVATE: доступно если поле убрано (стадия > максимальной) ===
            if taskData.id == "CULTIVATE" then
                if not d.isHarvested then
                    -- Если поле не убрано - культивировать нельзя
                    isAvailable = false
                    -- print("[RVS] CULTIVATE: поле не убрано, отключаем")
                else
                    -- print("[RVS] CULTIVATE: поле убрано, можно культивировать")
                end
            end

            -- === SOW: только если нет культуры ===
            if taskData.id == "SOW" then
                if d.fruitIndex > 0 then
                    isAvailable = false
                end
            end

            -- === PLOW: всегда доступно на поле (игрок сам решает когда пахать) ===
            -- Не блокируем — техника есть, поле есть, значит кнопка активна

            -- === FERTILIZE: отключаем, если уже удобрено ===
            if taskData.id == "FERTILIZE" and d.fertilizerFactor >= 0.99 then
                isAvailable = false
            end

            -- === WEED: только если есть сорняки ===
            if taskData.id == "WEED" and d.weedFactor <= 0.01 then
                isAvailable = false
            end

            -- === LIME: известкование обычно не зависит от культуры, оставляем как есть ===
        end

        table.insert(self.commandCategories, {
            id = taskData.id,
            name = taskData.name,
            count = #validVehicles,
            color = taskData.color,
            vehicles = validVehicles,
            isAvailable = isAvailable
        })
    end
end

-- Добавь эту функцию в RadialVehicleSwitcher_Data.lua (в секцию вспомогательных функций)
function RadialVehicleSwitcher:getHarvestableFruitIds(vehicle)
    local fruitDict = nil

    -- 1. СНАЧАЛА ПРОВЕРЯЕМ САМ КОМБАЙН (для спецтехники как OXBO EPD540E)
    if vehicle.spec_combine then
        -- У некоторых комбайнов есть встроенная жатка
        if vehicle.spec_combine.fruitTypeIndices then
            fruitDict = {}
            for _, fruitId in ipairs(vehicle.spec_combine.fruitTypeIndices) do
                fruitDict[fruitId] = true
            end
            return fruitDict
        end

        -- Или через spec_cutter прямо в комбайне
        if vehicle.spec_cutter and vehicle.spec_cutter.fruitTypeIndices then
            fruitDict = {}
            for _, fruitId in ipairs(vehicle.spec_cutter.fruitTypeIndices) do
                fruitDict[fruitId] = true
            end
            return fruitDict
        end
    end

    -- 2. ЕСЛИ НЕТ ВСТРОЕННОЙ, ИЩЕМ ПРИЦЕПНУЮ ЖАТКУ
    local attachedImplements = nil
    if vehicle.getAttachedImplements ~= nil then
        attachedImplements = vehicle:getAttachedImplements()
    end

    if attachedImplements and #attachedImplements > 0 then
        for _, implement in pairs(attachedImplements) do
            if implement.object then
                -- Проверяем жатку в прицепе
                if implement.object.spec_cutter and implement.object.spec_cutter.fruitTypeIndices then
                    fruitDict = {}
                    for _, fruitId in ipairs(implement.object.spec_cutter.fruitTypeIndices) do
                        fruitDict[fruitId] = true
                    end
                    return fruitDict
                end

                -- Проверяем сам прицеп (на случай если прицеп - это спецкомбайн)
                if implement.object.spec_combine and implement.object.spec_combine.fruitTypeIndices then
                    fruitDict = {}
                    for _, fruitId in ipairs(implement.object.spec_combine.fruitTypeIndices) do
                        fruitDict[fruitId] = true
                    end
                    return fruitDict
                end
            end
        end
    end

    return nil
end

function RadialVehicleSwitcher:onCycleSeed()
    -- Только если меню открыто, режим COMMAND, уровень 2, категория SOW
    if not self.isMenuOpen then
        return
    end
    if self.activeMode ~= "COMMAND" then
        return
    end
    if self.menuLevel ~= 2 then
        return
    end
    if self.selectedCategoryId ~= "SOW" then
        return
    end
    if self.hoveredIndex == nil or self.hoveredIndex == 0 then
        return
    end

    local actualIndex = (self.currentPage - 1) * self.maxItemsPerPage + self.hoveredIndex
    local vInfo = self.selectedCategoryVehicles[actualIndex]
    if vInfo == nil then
        return
    end

    local function findAndCycle(v)
        if v.spec_sowingMachine ~= nil then
            local spec = v.spec_sowingMachine
            if spec.seeds ~= nil and #spec.seeds > 1 then
                local next = (spec.currentSeed or 1) % #spec.seeds + 1
                local before = spec.currentSeed
                v:changeSeedIndex(1)
                local after = spec.currentSeed
                -- print(("[RVS] передали=%d было=%d стало=%d"):format(next, before, after))
                v._rvsSeedOverlayPath = nil
                v._rvsSeedOverlay = nil
                return true
            end
        end
        if v.getAttachedImplements ~= nil then
            for _, impl in pairs(v:getAttachedImplements()) do
                if impl.object ~= nil and findAndCycle(impl.object) then
                    return true
                end
            end
        end
        return false
    end

    findAndCycle(vInfo.vehicle)
end

function RadialVehicleSwitcher:isSeedPlantableNow(fruitTypeIndex)
    local growthMode = g_currentMission.missionInfo ~= nil and g_currentMission.missionInfo.growthMode or nil
    -- Режим 2 (выключен) — всегда можно сажать
    if growthMode == 2 then
        return true
    end
    local fruitType = g_fruitTypeManager:getFruitTypeByIndex(fruitTypeIndex)
    if fruitType == nil then
        return true
    end
    local month = g_currentMission.environment ~= nil and g_currentMission.environment.currentPeriod or nil
    if month == nil then
        return true
    end
    if fruitType.getIsPlantableInPeriod ~= nil then
        return fruitType:getIsPlantableInPeriod(1, month)
    end
    return true
end
