local _, addon = ...
local messages = {}

local GAP = 4
local queueCounter = 0

local Padding = 10
local Spacing = 5 -- between Title and Message

local function ShowSpeech(Frame, offsetY)
    Frame:ClearAllPoints()
    Frame:SetPoint("BOTTOMLEFT", addon.SpeechFrame, "BOTTOMLEFT", 0, offsetY)

    -- Temporary workaround how to avoid ellipse
    Frame.Message:SetWidth(Frame.Message:GetWidth() - 5)

    local titleHeight = Frame.Title:GetStringHeight()
    local messageHeight = Frame.Message:GetStringHeight()

    local totalHeight = Padding + titleHeight + Spacing + math.ceil(messageHeight) + Padding
    Frame:SetHeight(totalHeight)

    Frame.Title:SetPoint("TOPLEFT", Frame, "TOPLEFT", Padding, -Padding)
    Frame.Title:SetPoint("TOPRIGHT", Frame, "TOPRIGHT", -Padding, -Padding)

    Frame.Message:SetPoint("TOPLEFT", Frame, "TOPLEFT", Padding, -Padding - titleHeight - Spacing)
    Frame.Message:SetPoint("TOPRIGHT", Frame, "TOPRIGHT", -Padding, -Padding - titleHeight - Spacing)

    Frame:Show()
    return Frame
end

local function ReflowMessages(requery)
    local offsetY = 0
    local waitingForHeights = false

    if #messages == 0 then
        addon.SpeechFrame:Hide()
        return
    end

    for i = #messages, 1, -1 do
        local Frame = messages[i]
        ShowSpeech(Frame, offsetY)
        local height = Frame.Message:GetStringHeight()
        if height == 0 then
            waitingForHeights = true
        end
        offsetY = offsetY + math.max(Frame:GetHeight(), 10) + GAP
    end

    if waitingForHeights and not requery then
        C_Timer.After(0.05, function()
            ReflowMessages(true)
        end)
    end
end

local function RemoveMessage(queueId)
    for _, fs in ipairs(messages) do
        if fs.queueId == queueId and not fs._removing then
            fs._removing = true
            if fs:IsShown() and (fs:GetAlpha() or 1) > 0.01 then
                local animation = fs:CreateAnimationGroup()
                local fadeOut = animation:CreateAnimation("Alpha")
                fadeOut:SetFromAlpha(fs:GetAlpha() or 1)
                fadeOut:SetToAlpha(0)
                fadeOut:SetDuration(1)
                animation:SetScript("OnFinished", function()
                    fs:Hide()
                    for idx, msg in ipairs(messages) do
                        if msg.queueId == fs.queueId then
                            table.remove(messages, idx)
                            break
                        end
                    end
                    C_Timer.After(0, function() ReflowMessages() end)
                end)
                animation:Play()
            else
                fs:Hide()
                for idx, msg in ipairs(messages) do
                    if msg.queueId == fs.queueId then
                        table.remove(messages, idx)
                        break
                    end
                end
                C_Timer.After(0, function() ReflowMessages() end)
            end
            break
        end
    end
end

local function AddMessage(sender, message)
    addon.SpeechFrame:Show()

    local title = sender
    local text = message

    if string.sub(text, 1, #"%s") == "%s" then
        return
    end

    local MessageFrame = addon.SpeechFrame:CreateMessage()
    MessageFrame.Title:SetText(title)
    MessageFrame.Message:SetText(text)

    queueCounter = queueCounter + 1
    MessageFrame.queueId = queueCounter
    table.insert(messages, MessageFrame)

    C_Timer.After(0.1, function()
        ReflowMessages()
    end)

    -- Temporary workaround how to avoid ellipse
    C_Timer.After(0.1, function()
        ReflowMessages()
    end)

    C_Timer.After(15, function()
        RemoveMessage(MessageFrame.queueId)
    end)
end
addon.API.AddSpeechMessage = AddMessage

local function ShowSpeechTranslation(sender, message)
    -- Avoid to error when NPC speaks in instance
    if not message or type(message) ~= "string" then
        return
    end

    local playerName = UnitName("player")
    if sender == playerName then
        return
    end

    local text = CzechQuestsAddon:GetData("speech", sender .. "_" .. message)
    if text == nil and CzechQuestsAddon_Store.config.SPEECH_ORIGINAL_WHEN_MISSING then
        text = message
    end
    if text ~= nil then
        AddMessage(sender, text)
    end
end

local function InitSpeeches()
    local frame = addon.SpeechFrame

    -- Register Speech events
    frame:RegisterEvent("PLAYER_LOGIN")
    frame:RegisterEvent("CHAT_MSG_MONSTER_SAY")
    frame:RegisterEvent("CHAT_MSG_MONSTER_YELL")
    frame:RegisterEvent("CHAT_MSG_MONSTER_EMOTE")
    frame:RegisterEvent("CHAT_MSG_MONSTER_PARTY")
    frame:RegisterEvent("CHAT_MSG_MONSTER_WHISPER")

    frame:SetScript("OnEvent", function(self, event, message, sender)
        if not CzechQuestsAddon_Store.config.SPEECH_ENABLED then
            return
        end

        if event == "PLAYER_LOGIN" then
            frame:Init()
        end

        if event == "CHAT_MSG_MONSTER_SAY"
                or event == "CHAT_MSG_MONSTER_YELL"
                or event == "CHAT_MSG_MONSTER_EMOTE"
                or event == "CHAT_MSG_MONSTER_PARTY"
                or event == "CHAT_MSG_MONSTER_WHISPER" then
            ShowSpeechTranslation(sender, message)
        end
    end)
end
addon.API.InitSpeeches = InitSpeeches