-- =============================================================
-- Real Fuel Consumption — RFCModeHUD.lua
-- Icon-based HUD: engine mode (Eco/Normal/Power) + live fuel
-- consumption, anchored to the game's own speedometer cluster so
-- they read as part of the native HUD instead of floating text.
--
-- POSITIONING METHOD — confirmed against Real Vehicle Breakdowns
-- (a published, 300k+ download mod) which anchors its own custom HUD
-- icons the exact same way:
--   local x, y = g_currentMission.hud.speedMeter.speedBg:getPosition()
--   self.icon:setPosition(x + offsetX, y + offsetY)
-- We do the same, anchoring our mode icon to speedBg and our fuel
-- readout directly to the game's own existing fuelIcon position — so
-- our text sits right next to the vanilla fuel gauge, not somewhere
-- disconnected on screen.
--
-- SAFETY: this whole file runs inside Mission00.draw, which draws the
-- ENTIRE game HUD every frame — not just ours. An unguarded error here
-- would be far worse than a normal mod bug (matches the class of issue
-- that broke telehandler arm controls earlier in this mod's history).
-- Every new code path below is pcall-wrapped, and if the icon/atlas
-- approach fails for any reason, we fall back to the old plain-text
-- rendering that has been reliable since v1.1.0.0 — the HUD never goes
-- fully silent and never risks taking the rest of the game's HUD with it.
-- =============================================================

RFCModeHUD = {}

-- ---- Icon atlas (lazy, one-time, pcall-protected) ----
local atlasTried = false
local icons = { eco = nil, normal = nil, power = nil, fuel = nil }

local function ensureAtlasLoaded()
    if atlasTried then return end
    atlasTried = true
    if g_overlayManager == nil or RealFuelConsumption == nil then return end

    pcall(function()
        g_overlayManager:addTextureConfigFile(
            Utils.getFilename("menu/rfcgui.xml", RealFuelConsumption.MOD_DIR), "rfcgui")
        icons.eco    = g_overlayManager:createOverlay("rfcgui.eco",    0, 0, 0, 0)
        icons.normal = g_overlayManager:createOverlay("rfcgui.normal", 0, 0, 0, 0)
        icons.power  = g_overlayManager:createOverlay("rfcgui.power",  0, 0, 0, 0)
        icons.fuel   = g_overlayManager:createOverlay("rfcgui.fuel",   0, 0, 0, 0)
    end)
end

-- ---- Old, simple circle overlay — the proven fallback ----
local circleOverlay = nil
local circleTried    = false

-- ---- Vehicle lookup (unchanged from prior versions) ----
local function getControlledModeSpec()
    if g_currentMission == nil then return nil end
    local vehicle = nil
    if g_localPlayer ~= nil and g_localPlayer.getCurrentVehicle ~= nil then
        vehicle = g_localPlayer:getCurrentVehicle()
    end
    if vehicle == nil then
        vehicle = g_currentMission.controlledVehicle
    end
    if vehicle == nil then return nil end
    if vehicle.spec_rfcEngineModes == nil and vehicle.getRootVehicle ~= nil then
        vehicle = vehicle:getRootVehicle()
    end
    return vehicle ~= nil and vehicle.spec_rfcEngineModes or nil, vehicle
end

-- ---- Anchors: read the game's own speedometer HUD positions ----
local function getSpeedBgAnchor()
    if g_currentMission == nil or g_currentMission.hud == nil then return nil end
    local sm = g_currentMission.hud.speedMeter
    if sm == nil or sm.speedBg == nil then return nil end
    local ok, x, y = pcall(function() return sm.speedBg:getPosition() end)
    if not ok or x == nil then return nil end
    return x, y
end

local function getFuelIconAnchor()
    if g_currentMission == nil or g_currentMission.hud == nil then return nil end
    local sm = g_currentMission.hud.speedMeter
    if sm == nil or sm.fuelIcon == nil then return nil end
    local ok, x, y = pcall(function() return sm.fuelIcon:getPosition() end)
    if not ok or x == nil then return nil end
    return x, y
end

-- Screen-fraction helper. CONFIRMED against the real Overlay class source
-- (GDN LuaDoc): Overlay:setPosition(x, y) and setDimension(w, h) both take
-- plain screenspace fractions in [0..1] — the same convention our own
-- fallback circle already uses successfully (width as a fixed fraction,
-- height scaled by g_screenAspectRatio to stay visually square). No pixel-
-- to-normalized conversion function is needed at all, so we don't depend
-- on any unconfirmed global for this — one less uncertain API in the mix.
-- DIMENSION helper: width/height need the aspect-ratio compensation so a
-- square icon actually looks square (screen X and Y units aren't the
-- same physical size otherwise).
local function iconSize(frac)
    return frac, frac * (g_screenAspectRatio or 1.0)
end

-- POSITION-OFFSET helper: a plain pixel-style delta from an anchor point,
-- with NO aspect-ratio multiplication — that correction belongs to shape,
-- not to how far something is placed from another point. Calibrated
-- against Real Vehicle Breakdowns' own proven offsets for icons around
-- this exact same speedBg anchor (34, -16), (-50, -16), (-50, 5), (34, 5)
-- pixels, assuming their reference resolution is the common 1920x1080.
local function pixelDelta(px, py)
    return px / 1920, py / 1080
end

-- 3-letter mode labels shown under the icon — deliberately NOT run through
-- l10n: these are meant as stylized dashboard-style abbreviations (like
-- real cars use "ECO" / "SPORT" / "PWR" on the instrument cluster itself,
-- unrelated to the vehicle's menu language), not translated UI text.
local MODE_ABBR = { eco = "ECO", normal = "STD", power = "PWR" }

-- =============================================================
-- MODE ICON — anchored to the vanilla speed gauge background
-- =============================================================
local function drawModeIconNew()
    if not RealFuelConsumption.settings.showModeHud then return true end -- nothing to draw, but "handled"

    local spec, vehicle = getControlledModeSpec()
    if spec == nil then return true end

    local modeData = RealFuelConsumption.ENGINE_MODES[spec.currentMode]
    if modeData == nil then return true end

    ensureAtlasLoaded()
    local overlay = icons[modeData.name]
    if overlay == nil or overlay == 0 then return false end -- fall back

    local anchorX, anchorY = getSpeedBgAnchor()
    if anchorX == nil then return false end -- fall back

    -- TEMP DIAGNOSTIC: log the real anchor coordinates once, so the next
    -- log.txt tells us exactly where speedBg sits instead of guessing.
    -- Safe to leave in — prints only one time per game session.
    if not RFCModeHUD._loggedAnchor then
        RFCModeHUD._loggedAnchor = true
        print(string.format("[RFC] speedBg anchor = %.4f, %.4f  (screen aspect %.3f)",
            anchorX, anchorY, g_screenAspectRatio or -1))
    end

    -- Size: kept small, same as before (this part was already right —
    -- 0.020 was too big, 0.009 rendered at a sane size in-game).
    local w, h = iconSize(0.009)
    -- Offset: previously used a distance-scale far smaller than any
    -- proven value. RVB's own icons sit roughly 15-50px from this same
    -- speedBg anchor — we use a comparable ~40px vertical offset now.
    -- SIGN CONFIRMED IN-GAME (v1.2.0.9 test): -40 pushed the icon DOWN
    -- and far off the intended spot, not up. Positive Y is "up" in this
    -- coordinate system — flipped accordingly.
    -- Moved right per direct visual feedback: the icon was rendering
    -- clearly outside/left of the speedometer cluster. The math from the
    -- confirmed anchor (0.8141, 0.0278) doesn't cleanly reconcile with
    -- what appeared on screen, so this value is calibrated primarily
    -- from the player's own screenshot report rather than pure math —
    -- flagged honestly rather than pretending precision we don't have.
    -- FINAL FINE-TUNE (v1.2.1.6): calibrated by comparing our computed
    -- render position against a red dot the player marked directly on a
    -- screenshot. Math check: previous (70, 40) rendered at image-pixel
    -- (1633, 1001); the mark was at (1616, 1003) — only 17px left, 2px
    -- down needed. Confirms the anchor math was correct all along.
    -- MICRO FINE-TUNE (v1.2.1.7): 4px right per second marked screenshot.
    -- Vertical was already spot-on (0px difference).
    -- v1.2.1.8: still needed "a bit more" to the right per feedback after
    -- the 4px micro-tune — bumping further in the same direction.
    local ox, oy = pixelDelta(65, 38)

    local ok = pcall(function()
        overlay:setPosition(anchorX + ox - w * 0.5, anchorY + oy)
        overlay:setDimension(w, h)
        overlay:setColor(1, 1, 1, 0.95)
        overlay:render()

        -- 3-letter label centered under the icon, in the mode's own color
        -- (green/amber/red) for a third layer of distinction beyond shape
        -- and icon color alone — same spirit as a real dashboard readout.
        local abbr = MODE_ABBR[modeData.name]
        if abbr ~= nil then
            local c = modeData.color
            local _, labelDy = pixelDelta(0, 14) -- only the second return value matters here
            local labelY = anchorY + oy - labelDy
            setTextAlignment(RenderText.ALIGN_CENTER)
            setTextBold(true)
            setTextColor(c[1], c[2], c[3], 1.0)
            renderText(anchorX + ox, labelY, 0.009, abbr)
            setTextAlignment(RenderText.ALIGN_LEFT)
            setTextBold(false)
            setTextColor(1, 1, 1, 1)
        end
    end)
    return ok
end

local function drawModeCircleFallback()
    if not RealFuelConsumption.settings.showModeHud then return end

    local spec, vehicle = getControlledModeSpec()
    if spec == nil then return end
    local modeData = RealFuelConsumption.ENGINE_MODES[spec.currentMode]
    if modeData == nil then return end

    if not circleTried then
        circleTried = true
        if createImageOverlay ~= nil then
            circleOverlay = createImageOverlay("dataS/menu/hud/fillTypes/hud_fill_unknown.dds")
        end
    end
    if circleOverlay == nil or circleOverlay == 0 then return end

    local w, h = 0.018, 0.018 * g_screenAspectRatio
    local x = 0.5 - w * 0.5
    local y = 0.045
    local c = modeData.color
    setOverlayColor(circleOverlay, c[1], c[2], c[3], 0.95)
    renderOverlay(circleOverlay, x, y, w, h)

    local modeName = g_i18n:getText("rfc_mode_" .. modeData.name)
    setTextAlignment(RenderText.ALIGN_CENTER)
    setTextBold(true)
    setTextColor(c[1], c[2], c[3], 1.0)
    renderText(0.5, y - 0.018, 0.011, modeName)
    setTextAlignment(RenderText.ALIGN_LEFT)
    setTextBold(false)
    setTextColor(1, 1, 1, 1)
end

-- =============================================================
-- FUEL READOUT — anchored to the game's own existing fuel icon
-- =============================================================
local function drawFuelReadoutNew()
    if not RealFuelConsumption.settings.showFuelHud then return true end

    local rfcSpec, vehicle = getControlledModeSpec()
    if vehicle == nil or vehicle.spec_motorized == nil then return true end
    local mSpec = vehicle.spec_motorized
    local lph = mSpec.lastFuelUsage or 0

    -- CHANGED (per in-game test feedback): the game already has its own
    -- fuel pump icon right here (speedMeter.fuelIcon) — no need to draw
    -- another one on top of it. We only place OUR number next to the
    -- existing icon, nothing else.
    local anchorX, anchorY = getFuelIconAnchor()
    if anchorX == nil then return false end

    -- TEMP DIAGNOSTIC: log the real anchor once so the next log.txt gives
    -- us exact numbers to calibrate the text offset against.
    if not RFCModeHUD._loggedFuelAnchor then
        RFCModeHUD._loggedFuelAnchor = true
        print(string.format("[RFC] fuelIcon anchor = %.4f, %.4f", anchorX, anchorY))
    end

    local ok = pcall(function()
        setTextAlignment(RenderText.ALIGN_LEFT)
        setTextBold(false)
        setTextColor(1, 1, 1, 0.95)
        -- v1.2.1.0 used 24px right / 6px up — still touching the icon
        -- from below per in-game feedback. Bumped both a bit further:
        -- more to the right, and noticeably more upward so it sits
        -- level with the icon instead of hugging its bottom edge.
        -- FINAL FINE-TUNE (v1.2.1.6): calibrated from a marked screenshot.
        -- Previous (20,14) rendered at image-pixel (1873, 845); the mark
        -- was at (1891, 842) — 18px right, 3px up needed.
        -- MICRO FINE-TUNE (v1.2.1.7): 4px left, 4px down per second
        -- marked screenshot.
        -- v1.2.1.8: still needed "a bit more" to the left per feedback.
        local dx = 26 / 1920
        local dy = 13 / 1080
        renderText(anchorX + dx, anchorY + dy, 0.012, string.format("%.1f l/h", lph))
        setTextColor(1, 1, 1, 1)
    end)
    return ok
end

local function drawFuelReadoutFallback()
    if not RealFuelConsumption.settings.showFuelHud then return end
    local rfcSpec, vehicle = getControlledModeSpec()
    if vehicle == nil or vehicle.spec_motorized == nil then return end
    local mSpec = vehicle.spec_motorized
    local lph = mSpec.lastFuelUsage or 0

    setTextAlignment(RenderText.ALIGN_CENTER)
    setTextBold(false)
    setTextColor(1, 1, 1, 0.95)
    renderText(0.5, 0.078, 0.0135, string.format("Diesel: %.1f l/h", lph))
    setTextAlignment(RenderText.ALIGN_LEFT)
    setTextColor(1, 1, 1, 1)
end

-- =============================================================
-- DRAW HOOK — tries the new anchored-icon approach; falls back to the
-- proven plain-text/circle approach if anything about it fails. Both
-- paths are individually pcall-guarded so a failure in one never stops
-- the other from drawing, and never propagates into the base game's
-- own HUD draw call.
-- =============================================================
Mission00.draw = Utils.appendedFunction(Mission00.draw, function(self)
    if g_dedicatedServer ~= nil then return end
    if RealFuelConsumption == nil or RealFuelConsumption.settings == nil then return end
    if g_gui ~= nil and g_gui.currentGui ~= nil then return end

    local modeOk = false
    pcall(function() modeOk = drawModeIconNew() end)
    if not modeOk then
        pcall(drawModeCircleFallback)
    end

    local fuelOk = false
    pcall(function() fuelOk = drawFuelReadoutNew() end)
    if not fuelOk then
        pcall(drawFuelReadoutFallback)
    end
end)
