local battlePlayer,battleCamera,battlePhanto,battleHUD
local onlinePlay
local booMushroom

local battleMap = {}


battleMap.size = 120

battleMap.iconOpacity = 0.75

battleMap.backColor = Color.darkgrey.. 0.35
battleMap.sizeableColor = Color.lightgrey.. 0.5
battleMap.solidBlockColor = Color.white.. 0.5

battleMap.blacklistedTerrainLayerMap = {}


local mapShader = Shader.fromFile(nil,"resources/map.frag")

local terrainBuffer = {}
local terrainRedrawQueued = {}


function battleMap.getSection(camIdx)
    if battleCamera.isSplitScreen() then
        -- If both players are in the same section, display it in the middle of the screen
        local section1 = battleCamera.getCameraSection(1)
        local section2 = battleCamera.getCameraSection(2)

        if section1 == section2 then
            return section1,true
        end

        -- Otherwise, each camera will have its own map
        if camIdx == 1 then
            return section1,false
        else
            return section2,false
        end
    end

    return battleCamera.getCameraSection(1),false
end

function battleMap.getRelativePosition(x,y,bounds)
    local mapX = math.invlerp(bounds.left,bounds.right,x)
    local mapY = math.invlerp(bounds.top,bounds.bottom,y)

    return mapX,mapY
end

function battleMap.playerIconShouldBeVisible(p)
    local data = battlePlayer.getPlayerData(p)

    if not data.isActive or data.isDead then
        return false
    end

    if booMushroom.isActive(p) then
        if onlinePlay.currentMode == onlinePlay.MODE_OFFLINE or p.idx ~= battleCamera.onlineFollowedPlayerIdx then
            return false
        end
    end

    return true
end


function battleMap.queueTerrainRedraw(sectionIdx)
    terrainRedrawQueued[sectionIdx] = true
end


local function getMapSize(bounds)
    local sectionWidth = bounds.right - bounds.left
    local sectionHeight = bounds.bottom - bounds.top

    --local scale = battleMap.size/math.max(sectionWidth,sectionHeight)
    local scale = battleMap.size/((sectionWidth + sectionHeight)*0.5)

    return math.floor(sectionWidth*scale*0.5 + 0.5)*2,math.floor(sectionHeight*scale*0.5 + 0.5)*2
end


function battleMap.getSize(camIdx)
    return getMapSize(Section(battleCamera.getCameraSection(camIdx)).boundary)
end


local function drawTerrain(sectionIdx)
    local bounds = Section(sectionIdx).boundary
    local target = terrainBuffer[sectionIdx]

    local scale = target.width/(bounds.right - bounds.left)

    local vertexCoords = {}
    local vertexColors = {}
    local vertexCounter = 0
    local colorCounter = 0

    target:clear(-101)

    -- The actual terrain
    for _,block in Block.iterateIntersecting(bounds.left,bounds.top,bounds.right,bounds.bottom) do
        if (Block.SOLID_MAP[block.id] or Block.SEMISOLID_MAP[block.id]) and not block.isHidden and not block:mem(0x5A,FIELD_BOOL) and not battleMap.blacklistedTerrainLayerMap[block.layerName] then
            -- Place vertex coords
            local baseX = (block.x - bounds.left)*scale
            local baseY = (block.y - bounds.top)*scale

            local x1 = math.clamp(baseX,0,target.width)
            local y1 = math.clamp(baseY,0,target.height)
            local x2 = math.clamp(baseX + math.max(1,block.width*scale),0,target.width)
            local y2 = math.clamp(baseY + math.max(1,block.height*scale),0,target.height)

            vertexCoords[vertexCounter+1 ] = x1 -- top left
            vertexCoords[vertexCounter+2 ] = y1
            vertexCoords[vertexCounter+3 ] = x2 -- top right
            vertexCoords[vertexCounter+4 ] = y1
            vertexCoords[vertexCounter+5 ] = x1 -- bpttom left
            vertexCoords[vertexCounter+6 ] = y2
            vertexCoords[vertexCounter+7 ] = x2 -- top right
            vertexCoords[vertexCounter+8 ] = y1
            vertexCoords[vertexCounter+9 ] = x1 -- bpttom left
            vertexCoords[vertexCounter+10] = y2
            vertexCoords[vertexCounter+11] = x2 -- bpttom left
            vertexCoords[vertexCounter+12] = y2

            vertexCounter = vertexCounter + 12

            -- Add vertex colours
            for i = 1,6 do
                if Block.SIZEABLE_MAP[block.id] then
                    vertexColors[colorCounter+1] = 0
                    vertexColors[colorCounter+2] = 1
                    vertexColors[colorCounter+3] = 0
                    vertexColors[colorCounter+4] = 0
                else
                    vertexColors[colorCounter+1] = 1
                    vertexColors[colorCounter+2] = 0
                    vertexColors[colorCounter+3] = 0
                    vertexColors[colorCounter+4] = 0
                end

                colorCounter = colorCounter + 4
            end
        end
    end

    Graphics.glDraw{
        target = target,
        priority = -101,
        vertexCoords = vertexCoords,
        vertexColors = vertexColors,
    }
end

local function getTerrainBuffer(sectionIdx)
    if terrainBuffer[sectionIdx] == nil then
        local mapWidth,mapHeight = getMapSize(Section(sectionIdx).boundary)

        terrainBuffer[sectionIdx] = Graphics.CaptureBuffer(math.floor(mapWidth*0.5 + 0.5),math.floor(mapHeight*0.5 + 0.5))
        drawTerrain(sectionIdx)
    elseif terrainRedrawQueued[sectionIdx] then
        drawTerrain(sectionIdx)
    end

    terrainRedrawQueued[sectionIdx] = nil

    return terrainBuffer[sectionIdx]
end


function battleMap.draw(sectionIdx,priority,opacity,x,y,alignX,alignY)
    local sectionObj = Section(sectionIdx)
    local bounds = sectionObj.boundary

    local width,height = getMapSize(bounds)

    x = x - width*alignX
    y = y - height*alignY


    --[[Graphics.drawBox{
        color = battleMap.baseColor.. battleMap.baseColor.a*opacity,priority = priority,
        x = x,y = y,width = width,height = height,
    }]]

    Graphics.drawBox{
        texture = getTerrainBuffer(sectionIdx),
        color = Color.white.. opacity,
        priority = priority,
        x = x,y = y,width = width,height = height,

        shader = mapShader,uniforms = {
            backColor = battleMap.backColor,
            solidBlockColor = battleMap.solidBlockColor,
            sizeableColor = battleMap.sizeableColor,
        },
    }

    -- Draw player icons
    for _,p in ipairs(Player.get()) do
        if p.section == sectionIdx and battleMap.playerIconShouldBeVisible(p) then
            local relativeX,relativeY = battleMap.getRelativePosition(p.x + p.width*0.5,p.y + p.height,bounds)
            local image = battlePlayer.getPlayerHead(p)

            local iconPriority = priority
            local iconOpacity = opacity*battleMap.iconOpacity

            if battlePhanto.isWearingMask(p) then
                image = battleHUD.phantoImage
            end

            if onlinePlay.currentMode ~= onlinePlay.MODE_OFFLINE then
                if p.idx == battleCamera.onlineFollowedPlayerIdx then
                    iconPriority = iconPriority + 0.05
                else
                    iconOpacity = iconOpacity*0.75
                end
            end

            battlePlayer.renderOutlinedPlayerHead{
                image = image,playerIdx = p.idx,priority = iconPriority,
                color = Color.white.. iconOpacity,

                x = x + width*relativeX,
                y = y + height*relativeY,
            }
        end
    end


    battleMap.onMapDraw(sectionIdx,priority,opacity,x,y,width,height)
end


function battleMap.onInitAPI()
    registerCustomEvent(battleMap,"onMapDraw")

    battlePlayer = require("scripts/battlePlayer")
    battleCamera = require("scripts/battleCamera")
    battlePhanto = require("scripts/battlePhanto")
    battleHUD = require("scripts/battleHUD")
    onlinePlay = require("scripts/onlinePlay")
    booMushroom = require("scripts/booMushroom")
end


return battleMap