local gearT = { [-1]="R",[0]="N",[1]="1",[2]="2",[3]="3",[4]="4",[5]="5",[6]="6",[7]="7",[8]="8",[9]="9"  }
local white = rgb(1, 1, 1)

-- RANGE-CALC SETTINGS
local fuelKmsPerLitre = 9       -- km/L for range calculation

-- NEEDLE SETTINGS (unchanged)
local needleTex    = "./cluster/needle_centered_reworked.png"
local needleSize   = vec2(900, 900)
local needlePivot  = vec2(399, 1000)
local needleMinDeg = 133
local needleMaxDeg = -133
local needleMaxVal = 360

-- RPM NEEDLE SETTINGS (unchanged)
local rpmNeedleTex    = "./cluster/needle_centered_reworked.png"
local rpmNeedleSize   = vec2(900, 900)
local rpmNeedlePivot  = vec2(1655, 1000)
local rpmNeedleMinDeg = -0
local rpmNeedleMaxDeg = -170
local rpmNeedleMaxVal = 8000

-- BAR AREAS
-- RPM bar uses “reveal” mode: we draw a dark overlay then erase it left→right
local BAR_RPM  = { pos = vec2(259.6, 1715),  size = vec2(1295, 71) }
-- Oil & Boost (was Fuel) are smooth color bars
local BAR_OIL  = { pos = vec2(91.4, 1261),   size = vec2(660, 10),  bg = rgbm(0,0,0,0), fill = rgb(1,1,1) }
local BAR_BOOST= { pos = vec2(1531, 1260.7), size = vec2(329, 8),  bg = rgbm(0,0,0,0), fill = rgb(1,1,1) }

-- Moving-edge styling per bar
local RPM_EDGE   = { width = 6, color = rgbm(1.0, 1.0, 1.0, 0.95) }
local OIL_EDGE   = { width = 6, color = rgbm(1, 0, 0, 0.95) }
local BOOST_EDGE = { width = 4, color = rgbm(1.0, 0., 0, 0.95) }

-- Value ranges for bars
local OIL_MIN_C = 50     -- 0% at 50°C
local OIL_MAX_C = 130    -- 100% at 130°C

-- Smoothing
local SMOOTH_HZ = 10
local smoothRPM, smoothOil, smoothBoost = 0, 0, 0

-- AVERAGE SPEED (moving-time only)
local movingTimeSec = 0.0

-- SETTINGS (existing)
local debugMode       = false
local debugDistance   = false
local useMph          = true
local simulatedSpeedKmh = 0

local REFRESH = 0.2
local refreshTimer = 0

local simulatedSessionKm = 0
local simulatedTotalKm   = 0
local cached = {}

-- HELPERS
local function v(str) local x,y=str:match("([^,]+),%s*([^,]+)"); return vec2(tonumber(x), tonumber(y)) end
local function clamp(x,a,b) return (x<a) and a or (x>b) and b or x end
local function lerp(a,b,t) return a + (b - a) * t end
local function ema(prev, target, hz, dt) dt = dt or 0.016; local k = math.exp(-hz * dt); return target + (prev - target) * k end

local function rect(x,y,w,h,c,o)
  display.rect{ pos=vec2(x,y), size=vec2(w,h), color=c, opacity=o or 1 }
end

-- Robust max-RPM getter
local function getMaxRPM()
  local v =  car.rpmLimiter
         or car.rpmLimit
         or car.limiter
         or car.redlineRPM
         or car.maxRPM
         or car.engineMaxRPM
         or car.revLimiterRPM
         or car.revLimit
         or rpmNeedleMaxVal
         or 8000
  if v and v < 1000 then v = 8000 end
  return v
end

-- 🔰 RPM “reveal” bar
local function drawRpmRevealBar(pos, size, fraction)
  fraction = clamp(fraction or 0, 0, 1)
  local unfilledX = pos.x + size.x * fraction
  local unfilledW = size.x * (1 - fraction)
  if unfilledW > 0.5 then
    display.rect{
      pos   = vec2(unfilledX, pos.y),
      size  = vec2(unfilledW, size.y),
      color = rgbm(0, 0, 0, 0.60)
    }
  end
end

-- Moving edge
local function drawEdge(pos, size, fraction, edge)
  if not edge or (edge.width or 0) <= 0 then return end
  local f = clamp(fraction or 0, 0, 1)
  if f <= 0 or f >= 1 then return end
  local x = pos.x + size.x * f - (edge.width * 0.5)
  display.rect{
    pos     = vec2(math.floor(x + 0.5), pos.y),
    size    = vec2(edge.width, size.y),
    color   = edge.color,
    opacity = 1
  }
end

-- Standard bar draw + rounded/slanted ends (corner)
local function drawBar(bar, fraction, edge)
  local f = clamp(fraction or 0, 0, 1)

  -- background
  display.rect{
    pos    = bar.pos,
    size   = bar.size,
    color  = bar.bg,
    opacity= 1,
    corner = 3   -- rounded/slanted edges
  }

  -- fill
  if f > 0 then
    display.rect{
      pos    = bar.pos,
      size   = vec2(math.floor(bar.size.x * f + 0.5), bar.size.y),
      color  = bar.fill,
      opacity= 1,
      corner = 3   -- same rounded/slanted edges
    }
  end

  drawEdge(bar.pos, bar.size, f, edge)
end

-- NEEDLE HELPERS (existing)
local function needleAngle(val) local t = math.min(math.max(val / needleMaxVal, 0), 1); return lerp(needleMinDeg, needleMaxDeg, t) end
local function rpmNeedleAngle(val) local t = math.min(math.max(val / rpmNeedleMaxVal, 0), 1); return lerp(rpmNeedleMinDeg, rpmNeedleMaxDeg, t) end

function getSpeedKmh() return debugMode and simulatedSpeedKmh or car.speedKmh end

--------------------------------------------------------------------------------
--  SIMULATED TORQUE / POWER (same logic as strip version)
--------------------------------------------------------------------------------
local IDLE_RPM        = 1000
local PEAK_TORQUE_NM  = 4200
local PEAK_TORQUE_RPM = 4250
local PLATEAU_END_RPM = 4500
local REDLINE_RPM     = 7000
local TORQUE_AT_REDLINE_FRACTION = 0.85
local THROTTLE_EFFECT = 1

local SMOOTH_HZ_POWER = 10

local function simulatedTorqueNm(rpm, throttle)
  rpm = clamp(rpm or 0, IDLE_RPM, REDLINE_RPM)

  local tIdle    = PEAK_TORQUE_NM * 0.35
  local tPeak    = PEAK_TORQUE_NM
  local tRedline = PEAK_TORQUE_NM * TORQUE_AT_REDLINE_FRACTION

  local base
  if rpm <= PEAK_TORQUE_RPM then
    local t = (rpm - IDLE_RPM) / math.max(1, (PEAK_TORQUE_RPM - IDLE_RPM))
    base = lerp(tIdle, tPeak, clamp(t,0,1))
  elseif rpm <= PLATEAU_END_RPM then
    base = tPeak
  else
    local t = (rpm - PLATEAU_END_RPM) / math.max(1, (REDLINE_RPM - PLATEAU_END_RPM))
    base = lerp(tPeak, tRedline, clamp(t,0,1))
  end

  local gas = clamp(throttle or 0, 0, 1)
  base = base * (gas ^ THROTTLE_EFFECT) -- goes to zero when gas=0

  return math.max(0, base)
end

local function powerKWfromNm(torqueNm, rpm)
  return (torqueNm or 0) * (rpm or 0) / 9549.0
end

-- precompute max power at WOT
local PRECOMP_MAX_TORQUE = PEAK_TORQUE_NM
local PRECOMP_MAX_POWER_KW = (function()
  local best = 0
  for r = IDLE_RPM, REDLINE_RPM, 50 do
    local tNm = simulatedTorqueNm(r, 1.0)
    local pKW = powerKWfromNm(tNm, r)
    if pKW > best then best = pKW end
  end
  return math.max(1, best)
end)()

local smoothTorqueNm, smoothPowerKW = 0, 0

--------------------------------------------------------------------------------
--  TURBO / BOOST FRACTION (from cluster V5 style)
--------------------------------------------------------------------------------
local function getTurboFrac()
  local f = 0
  if car then
    if car.turboBoost ~= nil then
      f = car.turboBoost
    elseif car.boostRatio ~= nil then
      f = car.boostRatio
    elseif car.boostPressure ~= nil and car.maxBoostPressure ~= nil then
      f = (car.boostPressure) / math.max(0.001, car.maxBoostPressure)
    end
  end
  return clamp(f, 0, 1)
end

--------------------------------------------------------------------------------
--  G-FORCE CIRCLE (based on cluster V5, but circular area)
--------------------------------------------------------------------------------
local DOT_TEXTURE = "cluster/g_force_dot_red.png"
local DOT_SIZE    = vec2(65.5, 65.5)

local GF_CENTER   = vec2(278, 1032)   -- center of the g-meter
local G_RADIUS    = 73                  -- radius of circular clamp (px)
local MAX_G       = 2.0                 -- 1.0 = 1g, 2.0 = full radius
local EMA_HZ_G    = 7.5

local GF_SmoothedAccel = { x = 0, z = 0 }

local function gforce_update(dt)
  -- smooth accel in car coordinates
  GF_SmoothedAccel.x = ema(GF_SmoothedAccel.x, car.acceleration.x or 0, EMA_HZ_G, dt)
  GF_SmoothedAccel.z = ema(GF_SmoothedAccel.z, car.acceleration.z or 0, EMA_HZ_G, dt)

  local gx, gz = GF_SmoothedAccel.x, GF_SmoothedAccel.z

  -- angle = direction of accel vector, mag = normalized g-force
  local ang = math.atan2(gz, gx)
  local mag = math.sqrt(gx*gx + gz*gz) / math.max(0.001, MAX_G)
  mag = clamp(mag, 0, 1)

  local dir = vec2(math.cos(ang), math.sin(ang))
  local pos = GF_CENTER + dir * (G_RADIUS * mag)

  -- draw dot
  display.image({
    image = DOT_TEXTURE,
    pos   = vec2(pos.x - DOT_SIZE.x * 0.5, pos.y - DOT_SIZE.y * 0.5),
    size  = DOT_SIZE
  })

  -- numeric readouts (same style as V5)
  display.text({
    text      = string.format("%.1f", math.max(GF_SmoothedAccel.x, 0)),  -- right
    pos       = vec2(380, 1012),
    letter    = vec2(30, 60),
    font      = "audi_vln_hd",
    width     = 46,
    alignment = 1,
    spacing   = -10,
    color     = rgbm(1, 0.2, 0.25, 1)
  })
  display.text({
    text      = string.format("%.1f", math.max(-GF_SmoothedAccel.x, 0)), -- left
    pos       = vec2(100, 1012),
    letter    = vec2(30, 60),
    font      = "audi_vln_hd",
    width     = 46,
    alignment = 1,
    spacing   = -10,
    color     = rgbm(1, 0.2, 0.25, 1)
  })
  display.text({
    text      = string.format("%.1f", math.max(-GF_SmoothedAccel.z, 0)), -- braking
    pos       = vec2(240, 895),
    letter    = vec2(30, 60),
    font      = "audi_vln_hd",
    width     = 46,
    alignment = 1,
    spacing   = -10,
    color     = rgbm(1, 0.2, 0.25, 1)
  })
  display.text({
    text      = string.format("%.1f", math.max(GF_SmoothedAccel.z, 0)),  -- accel
    pos       = vec2(240, 1130),
    letter    = vec2(30, 60),
    font      = "audi_vln_hd",
    width     = 46,
    alignment = 1,
    spacing   = -10,
    color     = rgbm(1, 0.2, 0.25, 1)
  })
end

--------------------------------------------------------------------------------
--  CLUSTER UPDATE (original logic + power/torque texts + boost bar)
--------------------------------------------------------------------------------
local function cluster_update(dt)
  local speedKmh = getSpeedKmh()
  local speed = useMph and (speedKmh / 1.609344) or speedKmh

  -- Average speed (moving time only)
  if speedKmh > 0.5 then movingTimeSec = movingTimeSec + dt end

  if debugDistance then
    local distKm = (speedKmh / 3600) * dt
    simulatedSessionKm = simulatedSessionKm + distKm
    simulatedTotalKm   = simulatedTotalKm + distKm
  end

  -- Cached UI values
  refreshTimer = refreshTimer - dt
  if refreshTimer <= 0 then
    refreshTimer = REFRESH
    cached.time     = string.format("%02d:%02d", sim.timeHours, sim.timeMinutes)
    cached.speed    = math.floor(speed)
    cached.temp     = math.floor(car.waterTemperature)
    cached.fuel     = math.floor(car.fuel)
    cached.gear     = gearT[car.gear]
    cached.oilPress = math.floor(car.oilPressure / 0.14504)
    cached.rangeKm  = math.floor(car.fuel * fuelKmsPerLitre / 1.609344)
    if debugDistance then
      cached.totalKm   = string.format("%1d", simulatedTotalKm)
      cached.sessionKm = string.format("%.1f", simulatedSessionKm)
    else
      cached.totalKm   = string.format("%1d", car.distanceDrivenTotalKm/ 1.609344)
      cached.sessionKm = string.format("%1d", car.distanceDrivenSessionKm/ 1.609344)
    end
  end

  --──── texts (original) ────
  display.text({ text=cached.time,      pos=v("606.3, 1322"),      letter=v("40, 50"), font='b', color=white, alignment=1,   width=250, spacing=-15 })
  display.text({ text=cached.speed,     pos=v("835, 1124"),     letter=v("80, 120"),font='Seat_Leon_CUP_big', color=white, alignment=0.5, width=285, spacing=-0 })
  display.text({ text=cached.totalKm,   pos=v("957, 1269"), letter=v("30, 100"),  font='rs62',    color=white, alignment=1, width=250, spacing=0 })
  display.text({ text=cached.sessionKm, pos=v("595, 1269"), letter=v("30, 100"),  font='rs62',    color=white, alignment=1, width=250, spacing=0 })
  display.text({ text=cached.temp,      pos=v("40, 1277"),     letter=v("20, 35"),  font='Seat_Leon_CUP_big', color=white, alignment=1,   width=250, spacing=-1 })
  display.text({ text=cached.oilPress,  pos=v("0, 2955"),       letter=v("60, 120"), font='c7_big', color=white, alignment=1,   width=250, spacing=-10 })
  display.text({ text=cached.rangeKm,   pos=v("495, 694.7"),    letter=v("32, 40"),  font='b', color=white, alignment=0.75,   width=255,  spacing=-15 })
  display.text({ text=cached.gear,      pos=v("940, 976"),      letter=v("70, 110"),font='Seat_Leon_CUP_big',color=white, alignment=0.5, width=0,   spacing=1 })

  if car.handbrake < 0.1 then rect(12894.2, 1306.6, 85, 85, rgb(0,0,0), 1) end
  if car.headlightsActive then rect(22250.3, 668.3, 65, 45, rgb(0,0,0), 1) end

  --──── BARS (RPM / OIL / BOOST) ────
  local rpmFrac  = clamp((car.rpm or 0) / getMaxRPM(), 0, 1)
  local oilC     = car.oilTemperature or 0
  local oilFrac  = clamp((oilC - OIL_MIN_C) / math.max(1, (OIL_MAX_C - OIL_MIN_C)), 0, 1)

  -- BOOST fraction instead of fuel
  local boostFrac = getTurboFrac()

  -- Smooth
  smoothRPM   = ema(smoothRPM,   rpmFrac,   SMOOTH_HZ, dt)
  smoothOil   = ema(smoothOil,   oilFrac,   SMOOTH_HZ, dt)
  smoothBoost = ema(smoothBoost, boostFrac, SMOOTH_HZ, dt)

  -- RPM reveal bar
  drawRpmRevealBar(BAR_RPM.pos, BAR_RPM.size, smoothRPM)
  drawEdge(BAR_RPM.pos, BAR_RPM.size, smoothRPM, RPM_EDGE)

  -- Oil & Boost as regular bars (with rounded edges)
  drawBar(BAR_OIL,   smoothOil,   OIL_EDGE)
  drawBar(BAR_BOOST, smoothBoost, BOOST_EDGE)

  --──── Power / Torque simulation + 0–100% texts ────
  local rpm     = car.rpm or 0
  local gas     = car.gas or car.throttle or 0
  local tNm, pKW = 0, 0

  if rpm > (IDLE_RPM + 50) and gas > 0.001 then
    tNm = simulatedTorqueNm(rpm, gas)
    pKW = powerKWfromNm(tNm, rpm)
  end

  smoothTorqueNm = ema(smoothTorqueNm, tNm, SMOOTH_HZ_POWER, dt)
  smoothPowerKW  = ema(smoothPowerKW,  pKW, SMOOTH_HZ_POWER, dt)

  local tqFrac = clamp(smoothTorqueNm / PRECOMP_MAX_TORQUE,   0, 1)
  local pwFrac = clamp(smoothPowerKW  / PRECOMP_MAX_POWER_KW, 0, 1)

  local tqPct = math.floor(tqFrac * 100 + 0.5)
  local pwPct = math.floor(pwFrac * 100 + 0.5)

  -- Torque % text (left)
  display.text({
    text      = tostring(tqPct),
    pos       = v("1758, 1044"),     -- adjust if needed
    letter    = v("60, 160"),
    font      = 'rs62',
    color     = white,
    alignment = .5,
    width     = 200,
    spacing   = -5
  })


  -- Power % text (right)
  display.text({
    text      = tostring(pwPct),
    pos       = v("1471, 1044"),     -- adjust if needed
    letter    = v("60, 160"),
    font      = 'rs62',
    color     = white,
    alignment = .5,
    width     = 200,
    spacing   = -5
  })

  -- Average speed (session, moving-time only)
  local sessionKm = debugDistance and simulatedSessionKm or (car.distanceDrivenSessionKm or 0)
  local avgKmh = (movingTimeSec > 0.01) and (sessionKm / (movingTimeSec / 3600.0)) or 0
  local avgDisplay = useMph and (avgKmh / 1.609344) or avgKmh
  display.text({
    text      = string.format("%d", math.floor(avgDisplay + 0.5)),
    pos       = v("12724.7, 1157.7"),
    letter    = v("60, 120"),
    font      = "c7_big",
    color     = white,
    alignment = 0.5,
    width     = 0,
    spacing   = -10
  })
end

--------------------------------------------------------------------------------
--  MAIN UPDATE
--------------------------------------------------------------------------------
function update(dt)
  cluster_update(dt)
  gforce_update(dt)
end
