local tplusUtils = require("textplus/tplusutils")
local textplus = require("textplus")
local textFiles = require("scripts/textFiles")
local utf8 = require("scripts/utf8")
local repl = require("game/repl")

local onlinePlay,battleGeneral,battlePlayer,onlineLogs,battleMenu

local onlineChat = {}


onlineChat.active = false
onlineChat.disabled = false

onlineChat.messages = {}
onlineChat.messageCount = 0
onlineChat.totalMessagesHeight = 0

onlineChat.messagesScrollDistance = 0

onlineChat.typingText = nil
onlineChat.typingLayout = nil
onlineChat.typingCursorPos = 0

onlineChat.warningMainLayout = nil
onlineChat.warningMainText = nil
onlineChat.warningMainX = nil
onlineChat.warningPrefixLayout = nil
onlineChat.warningPrefixText = nil
onlineChat.warningPrefixX = nil



onlineChat.gameMessageColor = Color(1,1,0.25)
onlineChat.warningMessageColor = Color(1,0.25,0.25)
onlineChat.placeholderTextColor = Color(0.5,0.5,0.5)


onlineChat.textFont = textplus.loadFont("resources/font/mainFont.ini")
onlineChat.textScale = 2

onlineChat.cursorImage = Graphics.loadImageResolved("resources/font/textCursor.png")

onlineChat.priority = 6.5

onlineChat.headGap = 30
onlineChat.nameGap = 6

onlineChat.boxColor = Color.black.. 0.75
onlineChat.boxMarginX = 8
onlineChat.boxMarginY = 4

onlineChat.textMaxWidthPortion = 2/3

onlineChat.messageLifetime = lunatime.toTicks(60*30) -- messages last for 30 minutes

onlineChat.messageOpaqueTime = 160
onlineChat.messageFadeTime = 48

onlineChat.cursorBlinkTimer = 0

onlineChat.maxOnScreenMessages = 6
onlineChat.messageMaxLines = 6


onlineChat.distance = 32

onlineChat.typeGap = 32
onlineChat.warningGap = 32


onlineChat.scrollBarBackColor = Color.fromHexRGB(0x1B1B1B)
onlineChat.scrollBarMainColor = Color.white
onlineChat.scrollBarWidth = 4
onlineChat.scrollBarOffsetX = 2
onlineChat.scrollBarOffsetY = 2


onlineChat.kickButtonImage = Graphics.loadImageResolved("resources/kickButton.png")
onlineChat.kickButtonGap = 8

onlineChat.userInfoLayouts = {}
onlineChat.userInfoWidth = 0
onlineChat.userInfoHeight = 0

onlineChat.userInfoUserGap = 0
onlineChat.userInfoPingNumberWidth = 88
onlineChat.userInfoPingColor = Color.lightgrey
onlineChat.userInfoWarningColor = Color(1,0.25,0.25)


local mainMessageBuffer = Graphics.CaptureBuffer()

local history = GameData.onlinePlay.messageHistory

local messageCommand



local function getMaxWidth()
    local screenWidth,screenHeight = battleGeneral.getScreenSize()

    return math.floor(screenWidth*onlineChat.textMaxWidthPortion*0.5 + 0.5)*2
end

local function getMainMessageAreaHeight()
    local screenWidth,screenHeight = battleGeneral.getScreenSize()
    local height = screenHeight - onlineChat.distance

    if onlineChat.active then
        height = height - onlineChat.typingLayout.height - onlineChat.boxMarginY*2 - onlineChat.typeGap
    end

    if onlineChat.warningPrefixLayout ~= nil and onlineChat.warningMainLayout ~= nil then
        height = height - math.max(onlineChat.warningPrefixLayout.height,onlineChat.warningMainLayout.height) - onlineChat.boxMarginY*2 - onlineChat.warningGap
    end

    return height
end

local function getMaximumMessageScrollDistance()
    local messageAreaHeight = getMainMessageAreaHeight()

    if onlineChat.totalMessagesHeight <= messageAreaHeight then
        return 0
    end

    return onlineChat.totalMessagesHeight - 256
end

local function addMessage(playerIndex,text,timer,fadeTimer)
    local maxWidth = getMaxWidth() - onlineChat.boxMarginX

    if playerIndex > 0 then
        nameText = battlePlayer.getName(playerIndex)

        if timer == nil then
            onlineLogs.add(nameText.. ": ".. text)
        end
    else
        nameText = textFiles.chat.gamePrefix

        if timer == nil then
            onlineLogs.add("<Game> ".. text)
        end
    end

    local message = {}

    message.playerIndex = playerIndex
    message.text = text


    message.nameX = onlineChat.boxMarginX

    if playerIndex > 0 then
        message.headX = message.nameX
        message.nameX = message.nameX + onlineChat.headGap
    end

    message.nameLayout = textplus.layout(nameText,maxWidth*0.5 - message.nameX,{font = onlineChat.textFont,xscale = onlineChat.textScale,yscale = onlineChat.textScale,plaintext = true})
    message.mainX = message.nameX + message.nameLayout.width + onlineChat.nameGap

    message.mainLayout = textplus.layout(text,maxWidth - message.mainX,{font = onlineChat.textFont,xscale = onlineChat.textScale,yscale = onlineChat.textScale,plaintext = true})

    
    message.width = message.nameLayout.width + message.mainLayout.width + onlineChat.nameGap + onlineChat.boxMarginX*2
    message.height = math.max(message.nameLayout.height,message.mainLayout.height) + onlineChat.boxMarginY*2

    message.fadeTimer = fadeTimer or (onlineChat.messageOpaqueTime + onlineChat.messageFadeTime)
    message.timer = timer or (onlineChat.messageLifetime)

    message.opacity = 1

    if onlineChat.messagesScrollDistance > 0 then
        onlineChat.messagesScrollDistance = onlineChat.messagesScrollDistance + message.height
    end

    onlineChat.totalMessagesHeight = onlineChat.totalMessagesHeight + message.height

    onlineChat.messageCount = onlineChat.messageCount + 1
    onlineChat.messages[onlineChat.messageCount] = message
end

local function setTypingText(text)
    local maxWidth = getMaxWidth() - onlineChat.boxMarginX*2

    -- If there's no text, use the placeholder
    if text == "" then
        local placeholderText = textFiles.chat.typingTextPlaceholder
        if onlineChat.disabled then
            placeholderText = textFiles.chat.disabledText
        end

        onlineChat.typingLayout = textplus.layout(placeholderText,maxWidth,{color = onlineChat.placeholderTextColor,font = onlineChat.textFont,xscale = onlineChat.textScale,yscale = onlineChat.textScale})
        onlineChat.typingText = text

        onlineChat.cursorBlinkTimer = 0

        return true
    end

    -- Check if there's any invalid characters
    local font = onlineChat.textFont
    local codes = tplusUtils.strToCodes(text)

    for _,char in ipairs(codes) do
        if font.glyphs[char] == nil and char ~= string.byte("\n") then
            return false
        end
    end

    -- Make a layout and check if it fits
    local formattedText = {codes}
    formattedText[1].fmt = {font = font,xscale = onlineChat.textScale,yscale = onlineChat.textScale,plaintext = true}

    local newLayout = textplus.layout(formattedText,maxWidth)

    if #newLayout <= onlineChat.messageMaxLines then
        onlineChat.typingLayout = newLayout
        onlineChat.typingText = text

        onlineChat.cursorBlinkTimer = 0

        return true
    else
        return false
    end
end

local function addText(addedText,pos)
    local newText = utf8.sub(onlineChat.typingText,1,pos).. addedText.. utf8.sub(onlineChat.typingText,pos + 1)
    local successful = setTypingText(newText)

    if successful then
        if onlineChat.typingCursorPos >= pos then
            onlineChat.typingCursorPos = onlineChat.typingCursorPos + utf8.len(addedText)
        end
    else
        -- This text can't fit, so try removing characters until it does
        local textLength = utf8.len(addedText)

        if textLength > 1 then
            local newText = utf8.sub(addedText,1,textLength - 1)

            addText(newText,pos)
        end
    end
end


function onlineChat.onInputUpdate()
    if onlineChat.active then
        if onlinePlay.currentMode == onlinePlay.MODE_OFFLINE or battleMenu.currentMenu ~= nil then
            onlineChat.active = false
        end

        if Misc.GetKeyState(VK_UP) then
            onlineChat.messagesScrollDistance = math.clamp(onlineChat.messagesScrollDistance + 4,0,getMaximumMessageScrollDistance())
        elseif Misc.GetKeyState(VK_DOWN) then
            onlineChat.messagesScrollDistance = math.clamp(onlineChat.messagesScrollDistance - 4,0,getMaximumMessageScrollDistance())
        end

        onlineChat.cursorBlinkTimer = onlineChat.cursorBlinkTimer + 1
    else
        onlineChat.messagesScrollDistance = 0
    end

    for i = onlineChat.messageCount, 1, -1 do
        local message = onlineChat.messages[i]

        message.timer = message.timer - 1

        if message.timer > 0 then
            message.fadeTimer = math.max(0,message.fadeTimer - 1)

            if onlineChat.active then
                message.opacity = 1
            elseif i >= (onlineChat.messageCount - onlineChat.maxOnScreenMessages) then
                message.opacity = math.min(1,message.fadeTimer/onlineChat.messageFadeTime)
            else
                message.opacity = 0
            end
        else
            onlineChat.totalMessagesHeight = onlineChat.totalMessagesHeight - message.height
            onlineChat.messageCount = onlineChat.messageCount - 1
            table.remove(onlineChat.messages,i)
        end
    end
end


function onlineChat.onExitLevel()
    for i = 1,#history do
        history[i] = nil
    end
    
    for i,message in ipairs(onlineChat.messages) do
        history[i] = {message.playerIndex,message.text,message.timer,message.fadeTimer}
    end
end

function onlineChat.onStart()
    for _,messageData in ipairs(history) do
        addMessage(messageData[1],messageData[2],messageData[3],messageData[4])
    end
end


function onlineChat.onKeyboardPressDirect(vk,repeated,char)
    if onlinePlay.currentMode == onlinePlay.MODE_OFFLINE or onlinePlay.isReconnecting then
        return
    end

    if battleMenu.currentMenu ~= nil or repl.active then
        return
    end

    if not onlineChat.active then
        if vk == VK_RETURN and not repeated then
            setTypingText("")
            onlineChat.typingCursorPos = 0

            onlineChat.active = true
        end

        return
    end

    if vk == VK_RETURN then
        if Misc.GetKeyState(VK_SHIFT) then
            addText("\n",onlineChat.typingCursorPos)
        elseif not repeated then
            if onlineChat.typingText ~= "" then
                messageCommand:send(0, onlineChat.typingText)
                addMessage(onlinePlay.playerIdx,onlineChat.typingText)

                setTypingText("")
            end

            onlineChat.active = false
        end
    elseif vk == VK_ESCAPE then
        onlineChat.active = false
    end

    if onlineChat.disabled then
        setTypingText("")
        return
    end
    
    if vk == VK_BACK then
        if onlineChat.typingCursorPos > 0 then
            local newText = utf8.sub(onlineChat.typingText,1,onlineChat.typingCursorPos - 1).. utf8.sub(onlineChat.typingText,onlineChat.typingCursorPos + 1)
            local successful = setTypingText(newText)

            if successful then
                onlineChat.typingCursorPos = onlineChat.typingCursorPos - 1
                onlineChat.cursorBlinkTimer = 0
            end
        end

        return
    end

    if vk == VK_HOME then
        onlineChat.typingCursorPos = 0
        onlineChat.cursorBlinkTimer = 0
        return
    elseif vk == VK_END then
        onlineChat.typingCursorPos = utf8.len(onlineChat.typingText)
        onlineChat.cursorBlinkTimer = 0
        return
    end

    if vk == VK_PRIOR then -- page up
        onlineChat.messagesScrollDistance = math.clamp(onlineChat.messagesScrollDistance + 128,0,getMaximumMessageScrollDistance())
        return
    elseif vk == VK_NEXT then -- page down
        onlineChat.messagesScrollDistance = math.clamp(onlineChat.messagesScrollDistance - 128,0,getMaximumMessageScrollDistance())
        return
    end

    if vk == VK_LEFT then
        onlineChat.typingCursorPos = math.max(0,onlineChat.typingCursorPos - 1)
        onlineChat.cursorBlinkTimer = 0
        return
    elseif vk == VK_RIGHT then
        onlineChat.typingCursorPos = math.min(utf8.len(onlineChat.typingText),onlineChat.typingCursorPos + 1)
        onlineChat.cursorBlinkTimer = 0
        return
    end

    if char ~= nil then
        addText(char,onlineChat.typingCursorPos)
        return
    end
end

function onlineChat.onPasteText(text)
    if onlineChat.active then
        addText(text,onlineChat.typingCursorPos)
    end
end


local function getWarningMessage()
    local text = textFiles.chat.warning

    local timeSinceReceived = (onlinePlay.localTime - onlinePlay.lastResponseTime)

    if timeSinceReceived >= 3 and not onlinePlay.isReconnecting and onlinePlay.getUserCount() > 1 then
        local time = string.format("%.2f",timeSinceReceived)

        if onlinePlay.currentMode == onlinePlay.MODE_CLIENT then
            return textFiles.funcs.replace(text.unresponsiveAsClient,{TIME = time})
        elseif onlinePlay.currentMode == onlinePlay.MODE_HOST then
            return textFiles.funcs.replace(text.unresponsiveAsHost,{TIME = time})
        end
    end

    return nil
end


local function getPingText(idx)
    if idx == onlinePlay.hostPlayerIdx then
        return textFiles.chat.pingHost,onlineChat.pingValueColor
    end

    local user = onlinePlay.getUserByPlayer(idx)

    if user.isUnresponsive then
        return textFiles.chat.pingWarning,onlineChat.userInfoWarningColor
    end

    if user.pingValue ~= nil and user.pingValue >= 0 then
        local ms = math.min(999,math.floor(user.pingValue*1000 + 0.5))

        return textFiles.funcs.replace(textFiles.chat.pingFormat,{PING = ms}),onlineChat.userInfoPingColor
    end

    return textFiles.chat.pingWaiting,onlineChat.userInfoPingColor
end


local function setUpUserLayout(user)
    if onlineChat.userInfoLayouts[user.playerIdx] == nil then
        onlineChat.userInfoLayouts[user.playerIdx] = {}
    end

    local layout = onlineChat.userInfoLayouts[user.playerIdx]

    local leftX = onlineChat.boxMarginX
    local rightX = -onlineChat.boxMarginX

    -- Head
    layout.headX = leftX
    leftX = leftX + onlineChat.headGap

    -- Name
    local newName = battlePlayer.getName(user.playerIdx)

    layout.nameX = leftX
    layout.nameY = 0

    if layout.name ~= newName then
        layout.nameLayout = textplus.layout(newName,nil,{font = onlineChat.textFont,xscale = onlineChat.textScale,yscale = onlineChat.textScale,plaintext = true})
        layout.name = newName
    end

    layout.headY = layout.nameY + layout.nameLayout.height*0.5

    leftX = leftX + layout.nameLayout.width-- + onlineChat.nameGap

    -- Kick button
    if onlinePlay.currentMode == onlinePlay.MODE_HOST and not user.isHost then
        layout.kickWidth = onlineChat.kickButtonImage.width
        layout.kickHeight = onlineChat.kickButtonImage.height

        rightX = rightX - layout.kickWidth

        layout.kickX = rightX
        layout.kickY = 0

        rightX = rightX - onlineChat.kickButtonGap
    else
        layout.kickX = nil
        layout.kickY = nil
    end
    
    -- Ping
    local newPingText,newPingColor = getPingText(user.playerIdx)

    if layout.pingText ~= newPingText or layout.pingColor ~= newPingColor then
        layout.pingLayout = textplus.layout(newPingText,nil,{font = onlineChat.textFont,color = newPingColor,xscale = onlineChat.textScale,yscale = onlineChat.textScale})
        layout.pingText = newPingText
        layout.pingColor = newPingColor
    end

    layout.pingX = rightX - layout.pingLayout.width
    layout.pingY = 0

    rightX = rightX - onlineChat.userInfoPingNumberWidth

    -- Finish it off
    leftX = leftX + onlineChat.boxMarginX

    layout.width = leftX - rightX
    layout.height = math.max(layout.nameLayout.height,layout.pingLayout.height)
end

local function setUpUserInfoLayouts()
    onlineChat.userInfoWidth = 0
    onlineChat.userInfoHeight = onlineChat.boxMarginY*2

    -- Update layouts for each user
    local count = onlinePlay.getUserCount()

    for userIndex,user in ipairs(onlinePlay.getUsers()) do
        setUpUserLayout(user)

        onlineChat.userInfoHeight = onlineChat.userInfoHeight + onlineChat.userInfoLayouts[user.playerIdx].height
        onlineChat.userInfoWidth = math.max(onlineChat.userInfoLayouts[user.playerIdx].width,onlineChat.userInfoWidth)

        if userIndex < count then
            onlineChat.userInfoHeight = onlineChat.userInfoHeight + onlineChat.userInfoUserGap
        end
    end
end

local function getHoveredKickUser(mouseX,mouseY)
    if not onlineChat.active then
        return
    end

    local screenWidth,screenHeight = battleGeneral.getScreenSize()
    local y = 0

    for _,user in ipairs(onlinePlay.getUsers()) do
        local layout = onlineChat.userInfoLayouts[user.playerIdx]

        if layout ~= nil then
            if layout.kickX ~= nil then
                local kickX = screenWidth + layout.kickX
                local kickY = y + layout.kickY

                if mouseX > kickX and mouseY > kickY and mouseX < (kickX + layout.kickWidth) and mouseY < (kickY + layout.kickHeight) then
                    return user
                end
            end

            y = y + layout.height + onlineChat.userInfoUserGap
        end
    end
end

local function drawUserInfo()
    local screenWidth,screenHeight = battleGeneral.getScreenSize()

    if onlineChat.userInfoWidth <= 0 or onlineChat.userInfoHeight <= 0 then
        return
    end

    local hoveredKickUser
    if Misc.getCursorPosition ~= nil then
        local mouseX,mouseY = Misc.getCursorPosition()

        hoveredKickUser = getHoveredKickUser(mouseX,mouseY)
    end

    local rightX = screenWidth
    local leftX = rightX - onlineChat.userInfoWidth
    local y = 0

    -- Background
    Graphics.drawBox{
        color = onlineChat.boxColor,priority = onlineChat.priority,
        x = leftX,y = y,width = onlineChat.userInfoWidth,height = onlineChat.userInfoHeight,
    }

    y = y + onlineChat.boxMarginY

    -- Users
    for _,user in ipairs(onlinePlay.getUsers()) do
        local layout = onlineChat.userInfoLayouts[user.playerIdx]

        if layout ~= nil then
            -- Name/head
            local image = battlePlayer.getPlayerHead(Player(user.playerIdx))
            local nameColor = battlePlayer.getColor(user.playerIdx)

            Graphics.drawBox{
                texture = image,priority = onlineChat.priority,
                x = leftX + layout.headX,y = y + layout.headY - image.height*0.5,
            }

            textplus.render{
                layout = layout.nameLayout,priority = onlineChat.priority,
                color = nameColor,
                x = leftX + layout.nameX,y = y + layout.nameY,
            }

            -- Kick button
            if layout.kickX ~= nil then
                local kickColor = (user == hoveredKickUser and onlineChat.userInfoWarningColor) or onlineChat.userInfoPingColor

                Graphics.drawBox{
                    texture = onlineChat.kickButtonImage,priority = onlineChat.priority,
                    color = kickColor,
                    x = rightX + layout.kickX,y = y + layout.kickY,
                }
            end

            -- Ping value
            textplus.render{
                layout = layout.pingLayout,priority = onlineChat.priority,
                x = rightX + layout.pingX,y = y + layout.pingY,
            }

            y = y + layout.height + onlineChat.userInfoUserGap
        end
    end
end


function onlineChat.onDraw()
    local screenWidth,screenHeight = battleGeneral.getScreenSize()
    local maxWidth = getMaxWidth()

    local x = 0
    local y = screenHeight - onlineChat.distance


    -- Text being typed
    if onlineChat.active then
        local height = onlineChat.typingLayout.height + onlineChat.boxMarginY*2

        y = y - height

        Graphics.drawBox{
            color = onlineChat.boxColor,priority = onlineChat.priority,
            x = x,y = y,width = maxWidth,height = height,
        }

        local mainX = x + onlineChat.boxMarginX
        local mainY = y + onlineChat.boxMarginY

        textplus.render{
            layout = onlineChat.typingLayout,priority = onlineChat.priority,
            x = mainX,y = mainY,
        }

        -- Draw cursor
        if not onlineChat.disabled and onlineChat.cursorBlinkTimer%64 < 32 then
            local cursorX,cursorY = battleMenu.getCursorPosition(onlineChat.typingLayout,onlineChat.typingCursorPos)

            Graphics.drawBox{
                texture = onlineChat.cursorImage,priority = onlineChat.priority,
                x = mainX + cursorX,y = mainY + cursorY,
            }
        end
        

        y = y - onlineChat.typeGap
    end


    -- Warning messages
    local warningMessage = getWarningMessage()

    if warningMessage ~= nil then
        local warningPrefix = textFiles.chat.warningPrefix

        if onlineChat.warningText ~= warningMessage or onlineChat.warningPrefixText ~= warningPrefix then
            local fmt = {color = onlineChat.warningMessageColor,font = onlineChat.textFont,xscale = onlineChat.textScale,yscale = onlineChat.textScale}

            onlineChat.warningPrefixX = onlineChat.boxMarginX
            onlineChat.warningPrefixLayout = textplus.layout(warningPrefix,nil,fmt)
            onlineChat.warningPrefixText = warningPrefix

            onlineChat.warningMainX = onlineChat.warningPrefixX + onlineChat.warningPrefixLayout.width + onlineChat.nameGap
            onlineChat.warningMainLayout = textplus.layout(warningMessage,maxWidth - onlineChat.boxMarginX - onlineChat.warningMainX,fmt)
            onlineChat.warningMainText = warningMessage
        end

        local height = math.max(onlineChat.warningPrefixLayout.height,onlineChat.warningMainLayout.height) + onlineChat.boxMarginY*2

        y = y - height

        Graphics.drawBox{
            color = onlineChat.boxColor,priority = onlineChat.priority,
            x = x,y = y,width = maxWidth,height = height,
        }

        textplus.render{
            layout = onlineChat.warningPrefixLayout,priority = onlineChat.priority,
            x = x + onlineChat.warningPrefixX,
            y = y + onlineChat.boxMarginY,
        }

        textplus.render{
            layout = onlineChat.warningMainLayout,priority = onlineChat.priority,
            x = x + onlineChat.warningMainX,
            y = y + onlineChat.boxMarginY,
        }

        y = y - onlineChat.warningGap
    else
        onlineChat.warningPrefixLayout = nil
        onlineChat.warningPrefixText = nil

        onlineChat.warningMainLayout = nil
        onlineChat.warningMainText = nil
    end


    -- Current messages
    local messageAreaHeight = getMainMessageAreaHeight()
    local usedMessageHeight = 0
    local messageX = 0
    local messageY = mainMessageBuffer.height + onlineChat.messagesScrollDistance
    local messageCutoffY = mainMessageBuffer.height - messageAreaHeight

    mainMessageBuffer:clear(-100)
    --Graphics.drawScreen{target = mainMessageBuffer,color = Color.red,priority = -100}

    for i = onlineChat.messageCount, 1, -1 do
        local message = onlineChat.messages[i]

        if messageY <= messageCutoffY or message.opacity <= 0 then
            break
        end

        usedMessageHeight = math.min(messageAreaHeight,usedMessageHeight + message.height)
        messageY = messageY - message.height

        if messageY <= mainMessageBuffer.height then
            local nameColor,mainColor

            if message.playerIndex == 0 then
                mainColor = onlineChat.gameMessageColor
                nameColor = mainColor
            elseif onlinePlay.isConnected(message.playerIndex) then
                nameColor = battlePlayer.getColor(message.playerIndex)
                mainColor = Color.white
            else
                nameColor = Color.white
                mainColor = Color.white
            end

            Graphics.drawBox{
                color = onlineChat.boxColor.. message.opacity*onlineChat.boxColor.a,priority = onlineChat.priority,
                target = mainMessageBuffer,
                x = messageX,y = messageY,width = maxWidth,height = message.height,
            }

            textplus.render{
                layout = message.nameLayout,priority = onlineChat.priority,
                color = nameColor*message.opacity,
                target = mainMessageBuffer,
                x = messageX + message.nameX,
                y = messageY + onlineChat.boxMarginY,
            }

            textplus.render{
                layout = message.mainLayout,priority = onlineChat.priority,
                color = mainColor*message.opacity,
                target = mainMessageBuffer,
                x = messageX + message.mainX,
                y = messageY + onlineChat.boxMarginY,
            }

            -- Character head
            if message.headX ~= nil and message.playerIndex >= 1 and message.playerIndex <= Player.count() and onlinePlay.isConnected(message.playerIndex) then
                local p = Player(message.playerIndex)

                local image = battlePlayer.getPlayerHead(p)

                Graphics.drawBox{
                    texture = image,priority = onlineChat.priority,
                    target = mainMessageBuffer,
                    color = Color.white.. message.opacity,
                    x = messageX + message.headX,
                    y = messageY + onlineChat.boxMarginY + (message.nameLayout.height - image.height)*0.5,
                }
            end
        end
    end

    if usedMessageHeight > 0 then
        Graphics.drawBox{
            texture = mainMessageBuffer,priority = onlineChat.priority,
            x = x,y = y - usedMessageHeight,
            sourceWidth = maxWidth,
            sourceHeight = usedMessageHeight,
            sourceY = mainMessageBuffer.height - usedMessageHeight,
        }
        
        -- Scroll bar
        if onlineChat.active and onlineChat.totalMessagesHeight > messageAreaHeight then
            local backWidth = onlineChat.scrollBarWidth
            local backHeight = messageAreaHeight - onlineChat.scrollBarOffsetY*2
            local backX = x + maxWidth - backWidth - onlineChat.scrollBarOffsetX
            local backY = y - backHeight - onlineChat.scrollBarOffsetY

            Graphics.drawBox{
                color = onlineChat.scrollBarBackColor,priority = onlineChat.priority,
                width = backWidth,height = backHeight,
                x = backX,y = backY,
            }

            local mainWidth = backWidth
            local mainHeight = messageAreaHeight*backHeight/onlineChat.totalMessagesHeight
            local mainX = backX
            local mainY = backY + backHeight - mainHeight - (backHeight)*(onlineChat.messagesScrollDistance/onlineChat.totalMessagesHeight)

            if mainY < backY then
                mainHeight = mainHeight - (backY - mainY)
                mainY = backY
            end

            Graphics.drawBox{
                color = onlineChat.scrollBarMainColor,priority = onlineChat.priority,
                width = mainWidth,height = mainHeight,
                x = mainX,y = mainY,
            }
        end
    end

    -- Pings
    if onlineChat.active then
        setUpUserInfoLayouts()
        drawUserInfo()
    end
end


function onlineChat.onMouseButtonEvent(button,state,mouseX,mouseY)
    local hoveredKickUser = getHoveredKickUser(mouseX,mouseY)

    if hoveredKickUser ~= nil and button == 0 and state == 1 then
        hoveredKickUser:disconnect()
    end
end

function onlineChat.onMouseWheelEvent(axis,delta,mouseX,mouseY)
    if not onlineChat.active then
        return
    end

    if delta == 0 or mouseX ~= mouseX or mouseY ~= mouseY then
        return
    end

    if axis == 0 then
        if mouseX <= getMaxWidth() and mouseY <= getMainMessageAreaHeight() then
            onlineChat.messagesScrollDistance = math.clamp(onlineChat.messagesScrollDistance + delta*0.15,0,getMaximumMessageScrollDistance())
        end
    end
end


function onlineChat.onFramebufferResize(width,height)
    mainMessageBuffer = Graphics.CaptureBuffer()
end


function onlineChat.onInitAPI()
    registerEvent(onlineChat,"onInputUpdate")
    registerEvent(onlineChat,"onDraw")
    registerEvent(onlineChat,"onExitLevel")
    registerEvent(onlineChat,"onStart")

    registerEvent(onlineChat,"onKeyboardPressDirect")
    registerEvent(onlineChat,"onPasteText")
    
    registerEvent(onlineChat,"onMouseButtonEvent")
    registerEvent(onlineChat,"onMouseWheelEvent")

    registerEvent(onlineChat,"onFramebufferResize")

    battleGeneral = require("scripts/battleGeneral")
    battlePlayer = require("scripts/battlePlayer")
    onlinePlay = require("scripts/onlinePlay")
    onlineLogs = require("scripts/onlineLogs")
    battleMenu = require("scripts/battleMenu")

    -- Custom events
    messageCommand = onlinePlay.createCommand("_chatMessage",onlinePlay.IMPORTANCE_MAJOR)

    function messageCommand.onReceive(sourcePlayerIdx, message)
        addMessage(sourcePlayerIdx,message)
    end


    function onlinePlay.onConnect(playerIndex)
        local name = battlePlayer.getName(playerIndex)

        if playerIndex == onlinePlay.playerIdx then
            if onlinePlay.currentMode == onlinePlay.MODE_HOST then
                addMessage(0,textFiles.funcs.replace(textFiles.chat.hosting,{NAME = name}))
            else
                local hostName = battlePlayer.getName(onlinePlay.hostPlayerIdx)

                addMessage(0,textFiles.funcs.replace(textFiles.chat.joinHost,{NAME = name,HOST = hostName}))
            end
        else
            addMessage(0,textFiles.funcs.replace(textFiles.chat.connected,{NAME = name}))
        end
    end

    function onlinePlay.onDisconnect(playerIndex)
        if playerIndex == onlinePlay.playerIdx then
            if onlinePlay.currentMode == onlinePlay.MODE_HOST then
                addMessage(0,textFiles.chat.stopHosting)
            else
                addMessage(0,textFiles.chat.leave)
            end
        else
            local name = battlePlayer.getName(playerIndex)

            addMessage(0,textFiles.funcs.replace(textFiles.chat.disconnected,{NAME = name}))
        end
    end
end


return onlineChat