TirePressureHUD = {}
TirePressureHUD.name = g_currentModName or "TirePressureHUD"
TirePressureHUD.path = g_currentModDirectory or ""

TirePressureHUD.enabled = true
TirePressureHUD.iconRel = "gui/tirePressure.dds"
TirePressureHUD.iconPath = TirePressureHUD.path .. TirePressureHUD.iconRel

TirePressureHUD.offsetXPx = 1996
TirePressureHUD.offsetYPx = -3
TirePressureHUD.gapAboveGameInfoPx = 6

TirePressureHUD.iconSizePx = 18
TirePressureHUD.textSizePx = 13
TirePressureHUD.bgPadXPx = 6
TirePressureHUD.bgPadYPx = 4
TirePressureHUD.bgCapWidthPx = 10
TirePressureHUD.bgAlpha = 0.45
TirePressureHUD.screenSafeMarginPx = 6

TirePressureHUD.dragging = false
TirePressureHUD.dragOffsetX = 0
TirePressureHUD.dragOffsetY = 0

local function tpHudClamp(v, mn, mx)
    return math.max(mn, math.min(mx, tonumber(v) or mn))
end

local function tpHudSetOverlay(overlay, x, y, w, h)
    if overlay == nil then
        return
    end

    overlay:setPosition(x, y)
    overlay:setDimension(w, h)
    overlay:render()
end

local function tpHudIsHudVisible(hud)
    if hud == nil then
        return false
    end

    if hud.getIsVisible ~= nil then
        return hud:getIsVisible()
    end

    if hud.isVisible ~= nil then
        return hud.isVisible == true
    end

    return true
end

function TirePressureHUD:loadMap()
    self:_rebuildOverlays()
    print("[MudSystemPhysics] Tire pressure mini HUD loaded")
end

function TirePressureHUD:deleteMap()
    self:_deleteOverlays()
    self.dragging = false
end

function TirePressureHUD:_deleteOverlays()
    if self.icon ~= nil then self.icon:delete(); self.icon = nil end
    if self.bgLeft ~= nil then self.bgLeft:delete(); self.bgLeft = nil end
    if self.bgMiddle ~= nil then self.bgMiddle:delete(); self.bgMiddle = nil end
    if self.bgRight ~= nil then self.bgRight:delete(); self.bgRight = nil end
    if self.bgOverlay ~= nil then self.bgOverlay:delete(); self.bgOverlay = nil end
end

function TirePressureHUD:_rebuildOverlays()
    self:_deleteOverlays()

    if fileExists == nil or fileExists(self.iconPath) then
        self.icon = Overlay.new(self.iconPath, 0, 0, 0.01, 0.01)
    end

    if g_overlayManager ~= nil and g_overlayManager.createOverlay ~= nil then
        self.bgLeft = g_overlayManager:createOverlay("gui.gameInfo_left", 0, 0, 0, 0)
        self.bgMiddle = g_overlayManager:createOverlay("gui.gameInfo_middle", 0, 0, 0, 0)
        self.bgRight = g_overlayManager:createOverlay("gui.gameInfo_right", 0, 0, 0, 0)

        local alpha = self.bgAlpha or 0.45
        self.bgLeft:setColor(0, 0, 0, alpha)
        self.bgMiddle:setColor(0, 0, 0, alpha)
        self.bgRight:setColor(0, 0, 0, alpha)
    else
        self.bgOverlay = Overlay.new(nil, 0, 0, 0.01, 0.01)
        self.bgOverlay:setColor(0, 0, 0, self.bgAlpha or 0.45)
    end
end

function TirePressureHUD:onSettingsChanged()
    self.offsetXPx = tpHudClamp(self.offsetXPx, -2600, 2600)
    self.offsetYPx = tpHudClamp(self.offsetYPx, -2400, 2400)
end

function TirePressureHUD:update(dt)
    if self.dragging and (g_inputBinding == nil or g_inputBinding.getShowMouseCursor == nil or not g_inputBinding:getShowMouseCursor()) then
        self.dragging = false
    end
end

function TirePressureHUD:getCanDragWithMouse()
    if not self.enabled or g_currentMission == nil then
        return false
    end

    if g_inputBinding == nil or g_inputBinding.getShowMouseCursor == nil then
        return false
    end

    return g_inputBinding:getShowMouseCursor() == true
end

function TirePressureHUD:getVehicle()
    if TirePressureSystem == nil or TirePressureSystem.enabled ~= true then
        return nil
    end

    if TirePressureSystem.getControlledVehicle ~= nil then
        local controlledVehicle = TirePressureSystem:getControlledVehicle()
        if TirePressureSystem.getSelectedPressureVehicle ~= nil then
            return TirePressureSystem:getSelectedPressureVehicle(
                controlledVehicle
            )
        end
        return controlledVehicle
    end

    local mission = g_currentMission
    return mission ~= nil and (mission.controlledVehicle or mission.currentVehicle) or nil
end

function TirePressureHUD:_getLayoutMetrics()
    if not self.enabled or g_currentMission == nil or g_currentMission.hud == nil then
        return nil
    end

    local hud = g_currentMission.hud
    if not tpHudIsHudVisible(hud) then
        return nil
    end

    local vehicle = self:getVehicle()
    if vehicle == nil or vehicle.spec_wheels == nil then
        return nil
    end

    local gid = hud.gameInfoDisplay
    if gid == nil or gid.getPosition == nil then
        return nil
    end

    if gid.getVisible ~= nil and not gid:getVisible() then
        return nil
    end

    local posX, posY = gid:getPosition()

    local sW = gid.scalePixelToScreenWidth ~= nil
        and function(px) return gid:scalePixelToScreenWidth(px) end
        or function(px) return px / 1920 end

    local sH = gid.scalePixelToScreenHeight ~= nil
        and function(px) return gid:scalePixelToScreenHeight(px) end
        or function(px) return px / 1080 end

    local currentPressure = TirePressureSystem:getVehicleWheelPressureAverage(vehicle)

    local text = string.format("%.1f", currentPressure or TirePressureSystem.roadPressure or 2.4)
    local textSize = sH(self.textSizePx)
    local iconW = sW(self.iconSizePx)
    local iconH = sH(self.iconSizePx)
    local padX = sW(self.bgPadXPx)
    local padY = sH(self.bgPadYPx)
    local gapX = sW(5)
    local textW = getTextWidth(textSize, text)
    local contentW = iconW + gapX + textW
    local contentH = math.max(iconH, textSize)
    local blockW = contentW + padX * 2
    local blockH = contentH + padY * 2
    local xRight = posX - sW(self.offsetXPx)
    local x0 = xRight - blockW
    local y0 = (posY + sH(self.gapAboveGameInfoPx)) + sH(self.offsetYPx)
    local safeX = sW(self.screenSafeMarginPx or 6)
    local safeY = sH(self.screenSafeMarginPx or 6)

    x0 = tpHudClamp(x0, safeX, 1 - blockW - safeX)
    y0 = tpHudClamp(y0, safeY, 1 - blockH - safeY)

    return {
        vehicle = vehicle,
        gid = gid,
        posX = posX,
        posY = posY,
        sW = sW,
        sH = sH,
        sW1 = sW(1),
        sH1 = sH(1),
        gapY = sH(self.gapAboveGameInfoPx),
        x0 = x0,
        y0 = y0,
        blockW = blockW,
        blockH = blockH,
        padX = padX,
        padY = padY,
        iconW = iconW,
        iconH = iconH,
        text = text,
        textSize = textSize
    }
end

function TirePressureHUD:_isMouseOverHud(posX, posY, layout)
    return layout ~= nil
        and posX >= layout.x0 and posX <= layout.x0 + layout.blockW
        and posY >= layout.y0 and posY <= layout.y0 + layout.blockH
end

function TirePressureHUD:_applyDraggedPosition(newX0, newY0, layout, saveNow)
    local pxPerUnitX = layout.sW1 ~= 0 and layout.sW1 or (1 / 1920)
    local pxPerUnitY = layout.sH1 ~= 0 and layout.sH1 or (1 / 1080)

    local newOffsetXPx = math.floor(((layout.posX - (newX0 + layout.blockW)) / pxPerUnitX) + 0.5)
    local newOffsetYPx = math.floor(((newY0 - (layout.posY + layout.gapY)) / pxPerUnitY) + 0.5)

    self.offsetXPx = tpHudClamp(newOffsetXPx, -2600, 2600)
    self.offsetYPx = tpHudClamp(newOffsetYPx, -2400, 2400)

    if saveNow and MudSystemSettings ~= nil and type(MudSystemSettings.writeSettings) == "function" then
        MudSystemSettings:writeSettings()
    end
end

function TirePressureHUD:mouseEvent(posX, posY, isDown, isUp, button)
    if TirePressureSystem ~= nil and TirePressureSystem.panelOpen == true then
        return
    end

    local layout = self:_getLayoutMetrics()
    if layout == nil then
        self.dragging = false
        return
    end

    local canDrag = self:getCanDragWithMouse()

    if isDown then
        if Input ~= nil and button ~= Input.MOUSE_BUTTON_LEFT then
            return
        end

        if not canDrag or not self:_isMouseOverHud(posX, posY, layout) then
            return
        end

        self.dragging = true
        self.dragOffsetX = posX - layout.x0
        self.dragOffsetY = posY - layout.y0
        return
    end

    if isUp then
        if self.dragging then
            self:_applyDraggedPosition(posX - self.dragOffsetX, posY - self.dragOffsetY, layout, true)
        end
        self.dragging = false
        return
    end

    if self.dragging then
        if not canDrag then
            self.dragging = false
            return
        end

        self:_applyDraggedPosition(posX - self.dragOffsetX, posY - self.dragOffsetY, layout, false)
    end
end

function TirePressureHUD:draw()
    local layout = self:_getLayoutMetrics()
    if layout == nil then
        return
    end

    local x = layout.x0
    local y = layout.y0
    local w = layout.blockW
    local h = layout.blockH

    if self.bgLeft ~= nil and self.bgMiddle ~= nil and self.bgRight ~= nil then
        local capW = math.min(layout.sW(self.bgCapWidthPx), w * 0.35)
        tpHudSetOverlay(self.bgLeft, x, y, capW, h)
        tpHudSetOverlay(self.bgMiddle, x + capW, y, math.max(0, w - capW * 2), h)
        tpHudSetOverlay(self.bgRight, x + w - capW, y, capW, h)
    elseif self.bgOverlay ~= nil then
        tpHudSetOverlay(self.bgOverlay, x, y, w, h)
    else
        drawFilledRect(x, y, w, h, 0, 0, 0, self.bgAlpha or 0.45)
    end

    local iconX = x + layout.padX
    local iconY = y + (h - layout.iconH) * 0.5
    if self.icon ~= nil then
        self.icon:setPosition(iconX, iconY)
        self.icon:setDimension(layout.iconW, layout.iconH)
        self.icon:render()
    end

    local textX = iconX + layout.iconW + layout.sW(5)
    local textY = y + (h - layout.textSize) * 0.5
    setTextAlignment(RenderText.ALIGN_LEFT)
    setTextBold(true)
    setTextColor(1, 1, 1, 0.96)
    renderText(textX, textY, layout.textSize, layout.text)

    setTextColor(1, 1, 1, 1)
    setTextBold(false)
end

addModEventListener(TirePressureHUD)
