NUiObjectives = {}

local sw
local px1, py1, px2, pcx

local FRAME_R, FRAME_G, FRAME_B = 7, 39, 1
local POPUP_DURATION   = 3.0
local POPUP_FADE_START = 2.5

local SLIDE_IN    = 0.25
local SLIDE_HOLD  = 5.0
local SLIDE_OUT   = 0.25
local SLIDE_TOTAL = SLIDE_IN + SLIDE_HOLD + SLIDE_OUT

local TEXT_GOLD_START = 1.0
local TEXT_GOLD_DUR   = 0.3

local seenDirectives      = {}
local completedDirectives = {}
local slideQueue          = {}
local currentSlide        = nil
local shownOrder          = {}
local queue               = {}
local current             = nil

local function drawCentered(text, cx, y)
    local w = gr.getStringWidth(text)
    gr.drawString(text, cx - w / 2, y)
end

function NUiObjectives:SetupUI()
    require("nui-hud")
    self:ParseConfig("nui-hud.tbl")
end

function NUiObjectives:ParseConfig(file)
    NUi:ParseGeneralSettings(file, self)
    NUi:ParseConfigSection(file, "#Objectives Settings", function() self:ConfigObjectives() end)
end

function NUiObjectives:ConfigObjectives()
    local sf = self.ScaleFactor

    local screenW = gr.getScreenWidth()
    local screenH = gr.getScreenHeight()

    self.GaugeX = screenW * 0.5 + (-300 * sf)
    self.GaugeY = screenH * 0.0 + (70 * sf)

    self.TopBgH = 25 * sf
    self.EntryH = 25 * sf

    if parse.optionalString("$Header Image:") then
        local name = parse.getString()
        self.HeaderImg = name
        self.HeaderW   = gr.getImageWidth(name) * sf
        self.HeaderH   = gr.getImageHeight(name) * sf
    end

    if parse.optionalString("$Header Offset:") then
        local o = NUi:GetCSV(parse.getString())
        self.HeaderOffset = { tonumber(o[1]) * sf, tonumber(o[2]) * sf }
    else
        self.HeaderOffset = { 0, 0 }
    end

    if parse.optionalString("$Middle Image:") then
        local name = parse.getString()
        self.MiddleImg = name
        self.MiddleW   = gr.getImageWidth(name) * sf
        self.MiddleH   = gr.getImageHeight(name) * sf
    end

    if parse.optionalString("$Bottom Image:") then
        local name = parse.getString()
        self.BottomImg = name
        self.BottomW   = gr.getImageWidth(name) * sf
        self.BottomH   = gr.getImageHeight(name) * sf
    end

    if parse.optionalString("$Bottom Offset:") then
        local o = NUi:GetCSV(parse.getString())
        self.BottomOffset = { tonumber(o[1]) * sf, tonumber(o[2]) * sf }
    else
        self.BottomOffset = { 0, 0 }
    end

    if parse.optionalString("$Frame Color:") then
        local c = NUi:GetCSV(parse.getString())
        self.FrameColor = { tonumber(c[1]), tonumber(c[2]), tonumber(c[3]), tonumber(c[4]) or 255 }
    else
        self.FrameColor = { 7, 39, 1, 217 }
    end

    if parse.optionalString("$Label Color:") then
        local c = NUi:GetCSV(parse.getString())
        self.LabelColor = { tonumber(c[1]), tonumber(c[2]), tonumber(c[3]), tonumber(c[4]) or 255 }
    else
        self.LabelColor = { 255, 255, 255, 255 }
    end

    if parse.optionalString("$Label Font:") then
        local fontName = parse.getString()
        for i = 1, #gr.Fonts do
            if gr.Fonts[i].Name == fontName then
                self.LabelFont = gr.Fonts[i]
                break
            end
        end
    end

    if parse.optionalString("$Label Offset:") then
        local o = NUi:GetCSV(parse.getString())
        self.LabelOffset = { tonumber(o[1]) * sf, tonumber(o[2]) * sf }
    else
        self.LabelOffset = { 0, 0 }
    end

    if parse.optionalString("$Complete Frame:") then
        local name = parse.getString()
        self.CompleteFrame = name
        self.CompleteW     = gr.getImageWidth(name) * sf
        self.CompleteH     = gr.getImageHeight(name) * sf
    end

    if parse.optionalString("$Complete Label:") then
        self.CompleteLabel = parse.getString()
    end

    if parse.optionalString("$Complete Label Color:") then
        local c = NUi:GetCSV(parse.getString())
        self.CompleteLabelColor = { tonumber(c[1]), tonumber(c[2]), tonumber(c[3]), tonumber(c[4]) or 255 }
    else
        self.CompleteLabelColor = { 255, 255, 255, 220 }
    end

    if parse.optionalString("$Complete Divider:") then
        self.CompleteDivider = parse.getString()
    end

    if parse.optionalString("$Complete Divider Color:") then
        local c = NUi:GetCSV(parse.getString())
        self.CompleteDividerColor = { tonumber(c[1]), tonumber(c[2]), tonumber(c[3]), tonumber(c[4]) or 255 }
    else
        self.CompleteDividerColor = { 127, 127, 127, 255 }
    end

    if parse.optionalString("$Complete Text Font:") then
        local fontName = parse.getString()
        for i = 1, #gr.Fonts do
            if gr.Fonts[i].Name == fontName then
                self.CompleteTextFont = gr.Fonts[i]
                break
            end
        end
    end

    if parse.optionalString("$Complete Offset:") then
        local o = NUi:GetCSV(parse.getString())
        self.CompleteOffset = { tonumber(o[1]) * sf, tonumber(o[2]) * sf }
    else
        self.CompleteOffset = { 0, 0 }
    end

    if parse.optionalString("$Complete Text Padding:") then
        self.CompleteTextPadding = parse.getFloat() * sf
    else
        self.CompleteTextPadding = 10 * sf
    end

    if parse.optionalString("$Complete Text Offset:") then
        local o = NUi:GetCSV(parse.getString())
        self.CompleteTextOffset = { tonumber(o[1]) * sf, tonumber(o[2]) * sf }
    else
        self.CompleteTextOffset = { 0, 0 }
    end

    if parse.optionalString("$Complete Angle:") then
        self.CompleteAngle = parse.getFloat() * math.pi / 180
    else
        self.CompleteAngle = 0
    end

    if parse.optionalString("$Complete Text Gold Color:") then
        local c = NUi:GetCSV(parse.getString())
        self.CompleteTextGoldColor = { tonumber(c[1]), tonumber(c[2]), tonumber(c[3]), tonumber(c[4]) or 255 }
    else
        self.CompleteTextGoldColor = { 255, 200, 50, 255 }
    end

    parse.requiredString("#End")
    self.Active = true
    NDebugger:log("NUiObj", "ConfigObjectives() complete. Active set to true.")
end

function NUiObjectives:Setup()
    NDebugger:log("NUiObj", "Setup() called. Active=" .. tostring(self.Active))
    sw = gr.getScreenWidth()

    local popupW = sw * 0.5
    px1 = (sw - popupW) / 2
    py1 = 100
    px2 = px1 + popupW
    pcx = (px1 + px2) / 2

    seenDirectives      = {}
    completedDirectives = {}
    slideQueue          = {}
    currentSlide        = nil
    shownOrder          = {}
    queue               = {}
    current             = nil

end

function NUiObjectives:Poll()
    local now = mn.getMissionTime()
    if now < 0 then return end

    local activeNow = {}
    for i = 1, #mn.Events do
        local ev = mn.Events[i]
        local text = ev.DirectiveText
        if text ~= "" then
            activeNow[text] = true
            if not seenDirectives[text] then
                seenDirectives[text] = true
                queue[#queue + 1] = text
            end
            if not completedDirectives[text] and ev.RepeatCount == 0 then
                completedDirectives[text] = true
                slideQueue[#slideQueue + 1] = text
            end
        end
    end

    for text in pairs(seenDirectives) do
        if not activeNow[text] and not completedDirectives[text] then
            completedDirectives[text] = true
        end
    end

    if not current and #queue > 0 then
        current = { text = queue[1], startTime = now }
        table.remove(queue, 1)
    end

    if current and now >= current.startTime + POPUP_DURATION then
        shownOrder[#shownOrder + 1] = current.text
        current = nil
        if #queue > 0 then
            current = { text = queue[1], startTime = now }
            table.remove(queue, 1)
        end
    end
end

function NUiObjectives:Draw()
    if not self.Active then return end

    local now       = mn.getMissionTime()
    local lineCount = hu.getDirectiveLines()
    local x         = self.GaugeX
    local y         = self.GaugeY
    local fc        = self.FrameColor

    if lineCount > 0 then
        local entryStartY = y + self.TopBgH

        local hox, hoy = self.HeaderOffset[1], self.HeaderOffset[2]
        local headerY = entryStartY - self.HeaderH + hoy
        gr.setColor(fc[1], fc[2], fc[3], fc[4])
        gr.drawImage(self.HeaderImg, x + hox, headerY, x + hox + self.HeaderW, headerY + self.HeaderH, 0, 0, 1, 1, 1, true)

        if self.LabelFont then
            gr.CurrentFont = self.LabelFont
            local label = "OBJECTIVES"
            local tw = gr.getStringWidth(label)
            local tx = x + hox + (self.HeaderW - tw) / 2 + self.LabelOffset[1]
            local ty = headerY + (self.HeaderH - self.LabelFont.Height) / 2 + 1 + self.LabelOffset[2]
            local lc = self.LabelColor
            gr.setColor(lc[1], lc[2], lc[3], lc[4])
            gr.drawString(label, tx, ty)
        end

        for i = 1, lineCount do
            local ey = entryStartY + (i - 1) * self.EntryH
            gr.setColor(fc[1], fc[2], fc[3], fc[4])
            gr.drawImage(self.MiddleImg, x, ey, x + self.MiddleW, ey + self.MiddleH, 0, 0, 1, 1, 1, true)
        end

        local box, boy = self.BottomOffset[1], self.BottomOffset[2]
        local bottomY = entryStartY + lineCount * self.EntryH + boy
        gr.setColor(fc[1], fc[2], fc[3], fc[4])
        gr.drawImage(self.BottomImg, x + box, bottomY, x + box + self.BottomW, bottomY + self.BottomH, 0, 0, 1, 1, 1, true)

        if current then
            local elapsed = now - current.startTime
            local fadeT   = math.max(0, (elapsed - POPUP_FADE_START) / (POPUP_DURATION - POPUP_FADE_START))
            local alpha   = math.floor(220 * (1 - fadeT))
            local popupH  = 100

            gr.setColor(FRAME_R, FRAME_G, FRAME_B, alpha)
            gr.drawRectangle(px1, py1, px2, py1 + popupH, true)
            gr.setColor(255, 255, 255, alpha)
            drawCentered("NEW OBJECTIVE", pcx, py1 + 14)
            drawCentered(current.text, pcx, py1 + 58)
        end
    end

    -- completion slide anchored dynamically below weapons panel
    local restY = NUiShipWeapons.WeaponPanelBottomY + self.CompleteOffset[2]

    if not currentSlide and #slideQueue > 0 then
        currentSlide = { text = slideQueue[1], startTime = now }
        table.remove(slideQueue, 1)
    end

    if currentSlide then
        local elapsed = now - currentSlide.startTime
        if elapsed >= SLIDE_TOTAL then
            currentSlide = nil
        else
            local imgW = self.CompleteW or (451 * self.ScaleFactor)
            local imgH = self.CompleteH or (71 * self.ScaleFactor)
            local angle = self.CompleteAngle
            local cosA  = math.cos(angle)
            local sinA  = math.sin(angle)
            -- travel distance so the x component exactly clears imgW off-screen
            local travelDist = imgW / math.max(math.abs(cosA), 0.001)

            local progress
            if elapsed < SLIDE_IN then
                local t = elapsed / SLIDE_IN
                progress = 1 - (1 - t) * (1 - t) * (1 - t)   -- easeOutCubic 0→1
            elseif elapsed < SLIDE_IN + SLIDE_HOLD then
                progress = 1.0
            else
                local t = (elapsed - SLIDE_IN - SLIDE_HOLD) / SLIDE_OUT
                progress = 1 - t * t * t                       -- easeInCubic 1→0
            end

            local drawX = (sw - imgW) + (1 - progress) * travelDist * cosA
            local drawY = restY        + (1 - progress) * travelDist * sinA

            if self.CompleteFrame then
                gr.setColor(fc[1], fc[2], fc[3], 220)
                gr.drawImage(self.CompleteFrame, drawX, drawY, drawX + imgW, drawY + imgH, 0, 0, 1, 1, 1, true)
            end
            if self.CompleteLabel then
                local lc = self.CompleteLabelColor
                gr.setColor(lc[1], lc[2], lc[3], lc[4])
                gr.drawImage(self.CompleteLabel, drawX, drawY, drawX + imgW, drawY + imgH, 0, 0, 1, 1, 1, true)
            end
            if self.CompleteDivider then
                local dc = self.CompleteDividerColor
                gr.setColor(dc[1], dc[2], dc[3], dc[4])
                gr.drawImage(self.CompleteDivider, drawX, drawY, drawX + imgW, drawY + imgH, 0, 0, 1, 1, 1, true)
            end

            -- lazily render directive text to texture so it can be drawn with rotation
            if self.CompleteTextFont and not currentSlide.textTex then
                gr.CurrentFont = self.CompleteTextFont
                local textPad = self.CompleteTextPadding
                local maxTW   = imgW - textPad * 2
                local dispText = currentSlide.text
                local ew = gr.getStringWidth("...")
                if gr.getStringWidth(dispText) > maxTW then
                    while #dispText > 0 and gr.getStringWidth(dispText) + ew > maxTW do
                        dispText = dispText:sub(1, -2)
                    end
                    dispText = dispText .. "..."
                end
                local tw = math.max(1, gr.getStringWidth(dispText))
                local th = self.CompleteTextFont.Height
                local tp = math.max(2, math.floor(th * 0.5))
                local tex = gr.createTexture(tw + tp * 2, th + tp * 2, TEXTURE_STATIC)
                gr.CurrentRenderTarget = tex
                gr.setColor(0, 0, 0, 255)
                gr.drawRectangle(0, 0, tw + tp * 2, th + tp * 2, true)
                gr.setColor(255, 255, 255, 255)
                gr.CurrentFont = self.CompleteTextFont
                gr.drawString(dispText, tp, tp)
                gr.CurrentRenderTarget = nil
                tex:destroyRenderTarget()
                currentSlide.textTex = tex
                currentSlide.textTW  = tw
                currentSlide.textTH  = th
                currentSlide.textTP  = tp
            end

            if currentSlide.textTex then
                local tw   = currentSlide.textTW
                local th   = currentSlide.textTH
                local tp   = currentSlide.textTP
                local texW = tw + tp * 2
                local texH = th + tp * 2
                local cx   = drawX + imgW / 2 + self.CompleteTextOffset[1]
                local cy   = drawY + imgH * 0.75 + self.CompleteTextOffset[2]
                local gc   = self.CompleteTextGoldColor
                local tr, tg, tb, ta = 255, 255, 255, 220
                if elapsed >= TEXT_GOLD_START + TEXT_GOLD_DUR then
                    tr, tg, tb, ta = gc[1], gc[2], gc[3], gc[4]
                elseif elapsed > TEXT_GOLD_START then
                    local lt = (elapsed - TEXT_GOLD_START) / TEXT_GOLD_DUR
                    tr = math.floor(255 + (gc[1] - 255) * lt)
                    tg = math.floor(255 + (gc[2] - 255) * lt)
                    tb = math.floor(255 + (gc[3] - 255) * lt)
                    ta = math.floor(255 + (gc[4] - 255) * lt)
                end
                gr.setColor(tr, tg, tb, ta)
                gr.drawImage(currentSlide.textTex, cx - texW / 2, cy - texH / 2, cx + texW / 2, cy + texH / 2, 0, 0, 1, 1, 1, true, angle)
            end
        end
    end
end
