require "ISUI/ISToolTip"

MS_IndicatorToolTip = ISToolTip:derive("MS_IndicatorToolTip")

local FONT_HGT_SMALL = getTextManager():getFontHeight(UIFont.Small)
local FONT_HGT_MEDIUM = getTextManager():getFontHeight(UIFont.Medium)
local DETAIL_LABEL_COLOR = { r = 0.39, g = 0.58, b = 0.93 }
local DETAIL_SUBLABEL_COLOR = { r = 0.58, g = 0.80, b = 0.92 }
local DETAIL_VALUE_COLOR = { r = 1.0, g = 1.0, b = 1.0 }
local DETAIL_NOTE_COLOR = { r = 0.78, g = 0.78, b = 0.70 }
local DETAIL_DANGER_COLOR = { r = 1.0, g = 0.3, b = 0.3 }
local DETAIL_GAP_X = 24
local DETAIL_SECTION_GAP_Y = 4

function MS_IndicatorToolTip:initialise()
    ISToolTip.initialise(self)
end

function MS_IndicatorToolTip:new()
    local o = ISToolTip:new()
    setmetatable(o, self)
    self.__index = self
    o.Type = "MS_IndicatorToolTip"
    o.opacityPercent = 100
    o.alignedDetailRows = nil
    o._alignedDetailsLayout = nil
    return o
end

function MS_IndicatorToolTip:setAlignedDetailRows(rows)
    self.alignedDetailRows = rows
    self._alignedDetailsLayout = nil
end

function MS_IndicatorToolTip:setOpacityPercent(opacityPercent)
    local value = tonumber(opacityPercent) or 100
    if value < 0 then
        value = 0
    elseif value > 100 then
        value = 100
    end

    -- This setting controls the tooltip background opacity only.
    -- Text, icons and description content stay fully readable.
    self.opacityPercent = value
    self.backgroundColor.a = 0
    if self.descriptionPanel and self.descriptionPanel.setContentTransparency then
        self.descriptionPanel:setContentTransparency(1)
    elseif self.descriptionPanel then
        self.descriptionPanel.contentTransparency = 1
    end
end

function MS_IndicatorToolTip:getOpacityAlpha()
    return (tonumber(self.opacityPercent) or 100) / 100
end

local function getTooltipFont()
    if ISToolTip.GetFont then
        return ISToolTip.GetFont()
    end
    return UIFont.NewSmall
end

local function getLineHeight(font)
    local fontObject = getTextManager():getFontFromEnum(font)
    if fontObject and fontObject.getLineHeight then
        return fontObject:getLineHeight()
    end
    return getTextManager():getFontHeight(font)
end

local function hasAlignedRows(self)
    return self.alignedDetailRows and #self.alignedDetailRows > 0
end

function MS_IndicatorToolTip:layoutContents(x, y, myWidth)
    local textWidth, textHeight = ISToolTip.layoutContents(self, x, y, myWidth)

    if not hasAlignedRows(self) then
        self._alignedDetailsLayout = nil
        return textWidth, textHeight
    end

    local font = getTooltipFont()
    local lineHeight = getLineHeight(font)
    if lineHeight < 10 then
        lineHeight = 10
    end

    local labelWidth = 0
    local valueWidth = 0
    local leftOnlyWidth = 0
    local rows = self.alignedDetailRows or {}

    for _, row in ipairs(rows) do
        local label = tostring(row.label or "")
        local value = tostring(row.value or "")
        local measuredLabel = getTextManager():MeasureStringX(font, label)
        if row.leftOnly then
            -- Left-only rows are narrative notes: they can widen the tooltip,
            -- but they must not move the aligned value column used by rows such
            -- as "Stress/min" and "Unhappiness/min".
            local measuredWidth = measuredLabel
            if value ~= "" then
                measuredWidth = measuredWidth + 4 + getTextManager():MeasureStringX(font, value)
            end
            if measuredWidth > leftOnlyWidth then
                leftOnlyWidth = measuredWidth
            end
        else
            if measuredLabel > labelWidth then
                labelWidth = measuredLabel
            end
            if value ~= "" then
                local measuredValue = getTextManager():MeasureStringX(font, value)
                if measuredValue > valueWidth then
                    valueWidth = measuredValue
                end
            end
        end
    end

    local alignedWidth = labelWidth
    if valueWidth > 0 then
        alignedWidth = labelWidth + DETAIL_GAP_X + valueWidth
    end
    local detailWidth = math.max(alignedWidth, leftOnlyWidth)

    local topGap = textHeight > 0 and DETAIL_SECTION_GAP_Y or 0
    local detailHeight = topGap + (#rows * lineHeight)

    self._alignedDetailsLayout = {
        x = x,
        y = y + textHeight + topGap,
        font = font,
        lineHeight = lineHeight,
        labelWidth = labelWidth,
        valueRightX = x + alignedWidth,
        width = detailWidth,
        height = detailHeight,
    }

    return math.max(textWidth, detailWidth), textHeight + detailHeight
end

local function drawTextSegments(tooltip, segments, x, y, defaultColor, alpha, font)
    local cursorX = x
    for _, segment in ipairs(segments or {}) do
        local text = tostring(segment.text or "")
        if text ~= "" then
            local color = segment.color or defaultColor
            tooltip:drawText(text, cursorX, y, color.r, color.g, color.b, alpha, font)
            cursorX = cursorX + getTextManager():MeasureStringX(font, text)
        end
    end
    return cursorX
end

function MS_IndicatorToolTip:renderAlignedDetailRows(alpha)
    local layout = self._alignedDetailsLayout
    if not layout or not hasAlignedRows(self) then
        return
    end

    local y = layout.y
    local font = layout.font or getTooltipFont()
    local lineHeight = layout.lineHeight or getLineHeight(font)

    for _, row in ipairs(self.alignedDetailRows or {}) do
        local label = tostring(row.label or "")
        local value = row.value ~= nil and tostring(row.value) or nil
        local color = row.labelColor or (row.sub and DETAIL_SUBLABEL_COLOR or DETAIL_LABEL_COLOR)
        if row.note and not row.labelColor then
            color = DETAIL_NOTE_COLOR
        end
        if row.danger and not row.labelColor then
            color = DETAIL_DANGER_COLOR
        end

        local labelEndX = layout.x
        if label ~= "" then
            if row.labelSegments then
                labelEndX = drawTextSegments(self, row.labelSegments, layout.x, y, color, alpha, font)
            else
                self:drawText(label, layout.x, y, color.r, color.g, color.b, alpha, font)
                labelEndX = layout.x + getTextManager():MeasureStringX(font, label)
            end
        end

        if value and value ~= "" then
            local valueColor = row.valueColor or (row.danger and DETAIL_DANGER_COLOR or DETAIL_VALUE_COLOR)
            if row.leftOnly then
                self:drawText(value, labelEndX + 4, y, valueColor.r, valueColor.g, valueColor.b, alpha, font)
            else
                self:drawTextRight(value, layout.valueRightX, y, valueColor.r, valueColor.g, valueColor.b, alpha, font)
            end
        end

        y = y + lineHeight
    end
end

function MS_IndicatorToolTip:renderContents()
    if self.description ~= "" then
        if self.texture then
            self.descriptionPanel:setX(self:getAbsoluteX() + 8 + self.texture:getWidth() + 10)
        else
            self.descriptionPanel:setX(self:getAbsoluteX() + 10)
        end
        local y = 0
        if self.name then
            y = FONT_HGT_MEDIUM
        end
        self.descriptionPanel:setY(self:getAbsoluteY() + y)
        self.descriptionPanel:prerender()
        self.descriptionPanel:render()
    end

    self:renderAlignedDetailRows(1)
end

function MS_IndicatorToolTip:render()
    local mx = getMouseX() + 32
    local my = getMouseY() + 10
    if not self.followMouse then
        mx = self:getX()
        my = self:getY()
    end
    if self.desiredX and self.desiredY then
        mx = self.desiredX
        my = self.desiredY
    end
    self:setX(mx)
    self:setY(my)

    if self.contextMenu and self.contextMenu.joyfocus then
        local playerNum = self.contextMenu.player
        self:setX(getPlayerScreenLeft(playerNum) + 60)
        self:setY(getPlayerScreenTop(playerNum) + 60)
    elseif self.contextMenu and self.contextMenu.currentOptionRect then
        if self.contextMenu.currentOptionRect.height > 32 then
            self:setY(my + self.contextMenu.currentOptionRect.height)
        end
        self:adjustPositionToAvoidOverlap(self.contextMenu.currentOptionRect)
    elseif self.owner and self.owner.isButton then
        local ownerRect = { x = self.owner:getAbsoluteX(), y = self.owner:getAbsoluteY(), width = self.owner.width, height = self.owner.height }
        self:adjustPositionToAvoidOverlap(ownerRect)
    elseif self.owner and self.owner.Type == "ISSkillProgressBar" then
        local ownerRect = { x = self.owner:getAbsoluteX(), y = self.owner:getAbsoluteY(), width = self.owner.width, height = self.owner.height }
        self:adjustPositionToAvoidOverlap(ownerRect)
    end

    local backgroundAlpha = self:getOpacityAlpha()
    local contentAlpha = 1

    -- Background opacity now maps directly to the configured percentage:
    -- 100 = fully opaque background, 0 = transparent background.
    self:drawRect(0, 0, self.width, self.height, backgroundAlpha, 0.05, 0.05, 0.05)
    self:drawRectBorder(0, 0, self.width, self.height, 0.5 * backgroundAlpha, 0.9, 0.9, 1)

    if self.texture then
        local widthTexture = self.texture:getWidth()
        local heightTexture = self.texture:getHeight()
        local textureY = self.name and 35 or 5
        self:drawTextureScaled(self.texture, 8, textureY, widthTexture, heightTexture, contentAlpha, 1, 1, 1)
    end

    if self.name then
        self:drawText(self.name, 8, 5, 1, 1, 1, contentAlpha, UIFont.Medium)
    end

    if self.descriptionPanel and self.descriptionPanel.setContentTransparency then
        self.descriptionPanel:setContentTransparency(contentAlpha)
    elseif self.descriptionPanel then
        self.descriptionPanel.contentTransparency = contentAlpha
    end
    self:renderContents()

    if self.footNote then
        self:drawTextCentre(self.footNote, self:getWidth() / 2, self:getHeight() - FONT_HGT_SMALL - 4, 1, 1, 1, contentAlpha, UIFont.Small)
    end
end

return MS_IndicatorToolTip
