local battlePlayer = require("scripts/battlePlayer")
local battleCamera = require("scripts/battleCamera")
local battleMenu = require("scripts/battleMenu")

local onlinePlay = require("scripts/onlinePlay")

local textFiles = require("scripts/textFiles")
local textplus = require("textplus")
local easing = require("ext/easing")

local maskCollection,menus,hud

local startBlock = {}


startBlock.id = 65


startBlock.uiPriority = 4

startBlock.screenClampMargin = 8

startBlock.boxImage = Graphics.loadImageResolved("maskCollection/resources/menu/box.png")
startBlock.boxMarginX = 12
startBlock.boxMarginY = 8

startBlock.topTextGap = 8

startBlock.playerHeadGapX = 28
startBlock.playerHeadGapY = 20
startBlock.playerHeadMarginX = 20
startBlock.playerHeadMarginY = 12


startBlock.statusUINormalPosition = vector(-178672 + 64,-179872 - 16)
startBlock.statusUINormalPivot = vector(0.5,1)

startBlock.statusUIOutsidePosition = vector(-200176 + 32,-200608 + 96 + 16)
startBlock.statusUIOutsidePivot = vector(0.5,0)


startBlock.statusUI = {}


local textFormat = {
    font = textplus.loadFont("resources/font/mainFont.ini"),
    xscale = 2,yscale = 2,
}

local statusTextFormat = table.clone(textFormat)
statusTextFormat.color = Color(0.9,0.9,0.9)

local waveTextFormat = table.clone(textFormat)
local waveTextX

function waveTextFormat.posFilter(x,y, fmt,glyph, width,height)
    local hopTimer = math.max(0,math.min(1,((lunatime.tick() - 64 - (x - waveTextX)*0.35) % 256)/16))
    local hopOffset = math.abs(math.sin(hopTimer*math.pi))*4

    return x,y - hopOffset
end


function startBlock.statusUI:updateWaveIndex()
    local saveFile = maskCollection.getSaveFile()

    if saveFile ~= nil then
        self.waveIndex = saveFile.wave
    else
        self.waveIndex = 0
    end
end

function startBlock.statusUI:init()
    self:updateWaveIndex()

    self.opacity = (self:shouldBeVisible() and 1) or 0
    self.bigFactor = 0

    self:initialisePlayerHeads()

    self:updatePlayerHeadsLayout()
end

function startBlock.statusUI:shouldBeVisible()
    if self.waveIndex == 0 then
        return false
    end

    if maskCollection.activeOnClientside then
        return (maskCollection.gameState == maskCollection.GAME_STATE.WAITING_ROOM)
    else
        return true
    end
end

function startBlock.statusUI:update()
    if #self.playerHeads > 0 then
        self.bigFactor = math.min(1,self.bigFactor + 1/16)
    else
        self.bigFactor = math.max(0,self.bigFactor - 1/16)
    end

    if self:shouldBeVisible() then
        self.opacity = math.min(1,self.opacity + 0.1)
    else
        self.opacity = math.max(0,self.opacity - 0.1)
    end

    -- Update player heads
    local headIndex = 1

    while (self.playerHeads[headIndex] ~= nil) do
        local playerHead = self.playerHeads[headIndex]

        if playerHead.moveTimer > 0 then
            playerHead.moveTimer = math.max(0,playerHead.moveTimer - 1/12)

            local easedMoveTime = easing.inOutQuad(playerHead.moveTimer,0,1,1)

            playerHead.x = math.lerp(playerHead.goalX,playerHead.startX,easedMoveTime)
            playerHead.y = math.lerp(playerHead.goalY,playerHead.startY,easedMoveTime)
        end

        if playerHead.isRemoving then
            playerHead.popUpTimer = math.min(1,playerHead.popUpTimer + 1/12)

            if playerHead.popUpTimer >= 1 then
                table.remove(self.playerHeads,headIndex)

                if #self.playerHeads > 0 then
                    self:updatePlayerHeadsLayout()
                end
            else
                headIndex = headIndex + 1
            end
        else
            if playerHead.popUpTimer > 0 then
                playerHead.popUpTimer = math.max(0,playerHead.popUpTimer - 1/12)
            end

            headIndex = headIndex + 1
        end
    end
end

function startBlock.statusUI:updateLayout()
    local text = textFiles.maskCollection.statusUI

    self.waveTextLayout = textplus.layout(textFiles.funcs.replace(text.wave,{WAVE = self.waveIndex}),nil,waveTextFormat)

    -- "Small" form
    self.smallMainWidth = self.waveTextLayout.width
    self.smallMainHeight = self.waveTextLayout.height

    -- "Big form"
    local statusText = (maskCollection.gameState == maskCollection.GAME_STATE.WAITING_ROOM and text.starting) or text.playing

    self.preTextLayout = textplus.layout(statusText.pre,nil,statusTextFormat)
    self.postTextLayout = textplus.layout(statusText.post,nil,statusTextFormat)

    self.topTextWidth = self.preTextLayout.width + self.postTextLayout.width + self.waveTextLayout.width
    self.topTextHeight = math.max(self.preTextLayout.height,self.postTextLayout.height,self.waveTextLayout.height) + startBlock.topTextGap

    self.playerHeadsAlignX = 0.5

    self.bigMainWidth = math.max(self.topTextWidth,self.playerHeadsWidth)
    self.bigMainHeight = self.topTextHeight + self.playerHeadsHeight
end

function startBlock.statusUI:addPlayerHead(playerIdx)
    for _,playerHead in ipairs(self.playerHeads) do
        if playerHead.playerIdx == playerIdx then
            playerHead.isRemoving = false
            return
        end
    end

    table.insert(self.playerHeads,{
        playerIdx = playerIdx,
        popUpTimer = 1,
        moveTimer = 0,
        isRemoving = false,
    })

    self:updatePlayerHeadsLayout()
end

function startBlock.statusUI:removePlayerHead(playerIdx)
    for headIndex,playerHead in ipairs(self.playerHeads) do
        if playerHead.playerIdx == playerIdx then
            playerHead.isRemoving = true
        end
    end
end

function startBlock.statusUI:initialisePlayerHeads()
    self.playerHeads = {}

    --[[for _,p in ipairs(maskCollection.getActivePlayersInSection()) do
        self:addPlayerHead(p.idx)
    end]]

    self:updatePlayerHeadsLayout()
end

function startBlock.statusUI:updatePlayerHeadsLayout()
    local headCount = #self.playerHeads

    if headCount == 0 then
        self.playerHeadsWidth = 0
        self.playerHeadsHeight = 0

        self:updateLayout()
        return
    end

    local rowCount = math.ceil(headCount/5)
    local headsPerRow = math.ceil(headCount/rowCount)
    local columnCount = math.min(headCount,headsPerRow)

    for headIndex,playerHead in ipairs(self.playerHeads) do
        local row = math.floor((headIndex - 1)/headsPerRow)
        local column = (headIndex - 1)%headsPerRow

        local rowColumnCount = math.min(columnCount,headCount - row*headsPerRow)

        playerHead.goalX = ((column + 1) - (rowColumnCount + 1)*0.5)*startBlock.playerHeadGapX
        playerHead.goalY = ((row + 1) - (rowCount + 1)*0.5)*startBlock.playerHeadGapY

        if playerHead.x == nil then
            playerHead.x = playerHead.goalX
            playerHead.y = playerHead.goalY
        else
            playerHead.startX = playerHead.x
            playerHead.startY = playerHead.y

            playerHead.moveTimer = 1
        end
    end

    self.playerHeadsWidth = (columnCount - 1)*startBlock.playerHeadGapX + startBlock.playerHeadMarginX*2
    self.playerHeadsHeight = (rowCount - 1)*startBlock.playerHeadGapY + startBlock.playerHeadMarginY*2

    self:updateLayout()
end


function startBlock.statusUI:getPosition(camIdx)
    local section = battleCamera.getCameraSection(camIdx)

    if section == maskCollection.section then
        return startBlock.statusUINormalPosition,startBlock.statusUINormalPivot,0,0
    end

    return startBlock.statusUIOutsidePosition,startBlock.statusUIOutsidePivot,self.bigFactor,0
end

function startBlock.statusUI:draw(camIdx)
    if self.opacity <= 0 then
        return
    end

    local position,pivot,bigFactor,clampFactor = self:getPosition(camIdx)

    if position == nil then
        return
    end

    local cam = Camera(camIdx)

    local easedBigFactor = easing.inOutQuad(bigFactor,0,1,1)

    local mainWidth = math.lerp(self.smallMainWidth,self.bigMainWidth,easedBigFactor)
    local mainHeight = math.lerp(self.smallMainHeight,self.bigMainHeight,easedBigFactor)

    local totalWidth = mainWidth + startBlock.boxMarginX*2
    local totalHeight = mainHeight + startBlock.boxMarginY*2

    local totalX = math.floor(position.x - totalWidth*pivot.x - math.floor(cam.x + 0.5) + 0.5)
    local totalY = math.floor(position.y - totalHeight*pivot.y - math.floor(cam.y + 0.5) + 0.5)

    if clampFactor > 0 then
        totalX = math.lerp(totalX,math.clamp(totalX,startBlock.screenClampMargin,cam.width  - totalWidth  - startBlock.screenClampMargin),clampFactor)
        totalY = math.lerp(totalY,math.clamp(totalY,startBlock.screenClampMargin,cam.height - totalHeight - startBlock.screenClampMargin),clampFactor)
    end

    local mainX = totalX + startBlock.boxMarginX
    local mainY = totalY + startBlock.boxMarginY

    battleMenu.drawSegmentedBox{
        texture = startBlock.boxImage,priority = startBlock.uiPriority,
        color = Color.white.. self.opacity,
        x = totalX,
        y = totalY,
        width = totalWidth,
        height = totalHeight,
    }

    -- "Big" layout
    if bigFactor > 0 then
        local textColor = Color.white*(self.opacity*math.clamp(math.invlerp(0.5,1,bigFactor)))

        -- Player heads
        local playerHeadsX = math.floor(mainX + math.lerp(mainWidth*0.5,math.lerp(self.playerHeadsWidth*0.5,mainWidth - self.playerHeadsWidth*0.5,self.playerHeadsAlignX),easedBigFactor) + 0.5)
        local playerHeadsY = mainY + mainHeight - self.playerHeadsHeight*easedBigFactor*0.5

        for _,playerHead in ipairs(self.playerHeads) do
            battlePlayer.renderOutlinedPlayerHead{
                playerIdx = playerHead.playerIdx,
                priority = startBlock.uiPriority,
                color = Color.white.. self.opacity*bigFactor*(1 - playerHead.popUpTimer),

                x = playerHeadsX + playerHead.x*easedBigFactor,
                y = playerHeadsY + playerHead.y*easedBigFactor + easing.inQuad(playerHead.popUpTimer,0,12,1),
            }
        end

        -- Player counter
        --[[textplus.render{
            layout = self.playerCounterLayout,priority = startBlock.uiPriority,
            color = textColor,
            x = mainX,
            y = mainY + math.lerp(mainHeight - self.playerCounterLayout.height,self.topTextHeight,easedBigFactor),
        }]]

        -- Top text
        textplus.render{
            layout = self.preTextLayout,priority = startBlock.uiPriority,
            color = textColor,
            x = mainX,
            y = mainY,
        }

        textplus.render{
            layout = self.postTextLayout,priority = startBlock.uiPriority,
            color = textColor,
            x = mainX + mainWidth - self.postTextLayout.width,
            y = mainY,
        }
    end

    waveTextX = mainX + mainWidth - self.waveTextLayout.width - self.postTextLayout.width*easedBigFactor

    textplus.render{
        layout = self.waveTextLayout,priority = startBlock.uiPriority,
        color = Color.white*self.opacity,
        x = waveTextX,
        y = mainY,
    }
end


function startBlock.onStart()
    startBlock.statusUI:init()
end

function startBlock.onTick()
    startBlock.statusUI:update()
end

function startBlock.onCameraDraw(camIdx)
    startBlock.statusUI:draw(camIdx)
end


function startBlock.onPostBlockHit(block,fromTop,playerObj)
    if block.id == startBlock.id and block:mem(0x56,FIELD_WORD) == 0 and playerObj ~= nil then
        if battleMenu.currentMenu == nil then
            if onlinePlay.currentMode == onlinePlay.MODE_OFFLINE then
                menus.startMenu:open({},1,playerObj,true)
            elseif playerObj.idx == onlinePlay.playerIdx then
                menus.startMenu:open({},1,playerObj,false)
            end
        end

        playerObj.speedX = playerObj.speedX*0.25
    end
end

function startBlock.onSectionChange(sectionIdx,p)
    if sectionIdx == maskCollection.section then
        startBlock.statusUI:addPlayerHead(p.idx)
    else
        startBlock.statusUI:removePlayerHead(p.idx)
    end
end


function startBlock.onInitAPI()
    registerEvent(startBlock,"onStart")
    registerEvent(startBlock,"onTick")
    registerEvent(startBlock,"onCameraDraw")

    registerEvent(startBlock,"onPostBlockHit")
    registerEvent(startBlock,"onSectionChange")

    maskCollection = require("maskCollection/scripts/maskCollection")
    menus = require("maskCollection/scripts/menus")
    hud = require("maskCollection/scripts/hud")


    function maskCollection.onGameStateChange()
        startBlock.statusUI:updateWaveIndex()
        startBlock.statusUI:updateLayout()
    end

    function maskCollection.onSaveFilePush()
        startBlock.statusUI:updateWaveIndex()
        startBlock.statusUI:updateLayout()
    end
end


return startBlock