local maskCollection = require("maskCollection/scripts/maskCollection")

local stageChangeEffect = {}


stageChangeEffect.gridSize = 32

stageChangeEffect.panelFlipTime = 16
stageChangeEffect.interPanelDelay = 0.2


stageChangeEffect.oldStageBlocks = {}
stageChangeEffect.oldStageBGOs = {}

stageChangeEffect.minGridX = 0
stageChangeEffect.minGridY = 0
stageChangeEffect.maxGridX = 0
stageChangeEffect.maxGridY = 0

stageChangeEffect.transitionActive = false
stageChangeEffect.transitionTimer = 0


local transitionShader = Shader.fromFile(nil,"maskCollection/resources/stageChangeEffect.frag")
local oldStageBuffer,newStageBuffer


function stageChangeEffect.setUp()
    if not maskCollection.activeOnClientside then
        return
    end

    local sectionBounds = Section(maskCollection.section).origBoundary

    stageChangeEffect.minGridX = math.floor(camera.x/stageChangeEffect.gridSize)
    stageChangeEffect.minGridY = math.floor(camera.y/stageChangeEffect.gridSize)

    if camera2.isSplit then
        stageChangeEffect.minGridX = math.min(stageChangeEffect.minGridX,math.floor(camera2.x/stageChangeEffect.gridSize))
        stageChangeEffect.minGridY = math.min(stageChangeEffect.minGridY,math.floor(camera2.y/stageChangeEffect.gridSize))
    end

    stageChangeEffect.maxGridX = math.floor(sectionBounds.right/stageChangeEffect.gridSize)
    stageChangeEffect.maxGridY = math.floor(sectionBounds.bottom/stageChangeEffect.gridSize)

    -- Collect the relevant blocks and BGOs
    --local minX = stageChangeEffect.minGridX*stageChangeEffect.gridSize
    --local minY = stageChangeEffect.minGridY*stageChangeEffect.gridSize
    --local maxX = (stageChangeEffect.maxGridX + 1)*stageChangeEffect.gridSize
    --local maxY = (stageChangeEffect.maxGridY + 1)*stageChangeEffect.gridSize

    stageChangeEffect.oldStageBlocks = {}
    stageChangeEffect.oldStageBGOs = {}

    for _,block in Block.iterateIntersecting(sectionBounds.left,sectionBounds.top,sectionBounds.right,sectionBounds.bottom) do
        if not block.isHidden then
            table.insert(stageChangeEffect.oldStageBlocks,block)
        end
    end

    for _,bgo in BGO.iterateIntersecting(sectionBounds.left,sectionBounds.top,sectionBounds.right,sectionBounds.bottom) do
        if not bgo.isHidden then
            table.insert(stageChangeEffect.oldStageBGOs,bgo)
        end
    end
end

function stageChangeEffect.begin()
    if not maskCollection.activeOnClientside then
        return
    end

    stageChangeEffect.transitionActive = true
    stageChangeEffect.transitionTimer = 0
end


local drawVC = {}
local drawTC = {}
local drawArgs = {vertexCoords = drawVC,textureCoords = drawTC,sceneCoords = true}

local blockBatchIDs = {}
local blockBatches = {}

local bgoBatchIDs = {}
local bgoBatches = {}


local function drawBlockBatch(blockID)
    drawArgs.texture = Graphics.sprites.block[blockID].img

    if drawArgs.texture == nil then
        return
    end

    local vertexCounter = 0

    for _,block in ipairs(blockBatches[blockID]) do
        -- Add vertex coords
        local x1 = block.x
        local y1 = block.y
        local x2 = x1 + block.width
        local y2 = y1 + block.height

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

        -- Add texture coords
        local tx1 = 0
        local ty1 = 0
        local tx2 = 1
        local ty2 = 1

        drawTC[vertexCounter + 1 ] = tx1 -- top left
        drawTC[vertexCounter + 2 ] = ty1
        drawTC[vertexCounter + 3 ] = tx2 -- top right
        drawTC[vertexCounter + 4 ] = ty1
        drawTC[vertexCounter + 5 ] = tx1 -- bottom left
        drawTC[vertexCounter + 6 ] = ty2
        drawTC[vertexCounter + 7 ] = tx2 -- top right
        drawTC[vertexCounter + 8 ] = ty1
        drawTC[vertexCounter + 9 ] = tx1 -- bottom left
        drawTC[vertexCounter + 10] = ty2
        drawTC[vertexCounter + 11] = tx2 -- bottom right
        drawTC[vertexCounter + 12] = ty2

        vertexCounter = vertexCounter + 12
    end

    -- Clear old vertices
    vertexCounter = vertexCounter + 1

    while (drawVC[vertexCounter] ~= nil) do
        drawVC[vertexCounter] = nil
        drawTC[vertexCounter] = nil

        vertexCounter = vertexCounter + 1
    end

    Graphics.glDraw(drawArgs)
end

local function drawBGOBatch(bgoID)
    drawArgs.texture = Graphics.sprites.background[bgoID].img

    if drawArgs.texture == nil then
        return
    end

    local vertexCounter = 0

    for _,bgo in ipairs(bgoBatches[bgoID]) do
        -- Add vertex coords
        local x1 = bgo.x
        local y1 = bgo.y
        local x2 = x1 + bgo.width
        local y2 = y1 + bgo.height

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

        -- Add texture coords
        local tx1 = 0
        local ty1 = 0
        local tx2 = 1
        local ty2 = 1

        drawTC[vertexCounter + 1 ] = tx1 -- top left
        drawTC[vertexCounter + 2 ] = ty1
        drawTC[vertexCounter + 3 ] = tx2 -- top right
        drawTC[vertexCounter + 4 ] = ty1
        drawTC[vertexCounter + 5 ] = tx1 -- bottom left
        drawTC[vertexCounter + 6 ] = ty2
        drawTC[vertexCounter + 7 ] = tx2 -- top right
        drawTC[vertexCounter + 8 ] = ty1
        drawTC[vertexCounter + 9 ] = tx1 -- bottom left
        drawTC[vertexCounter + 10] = ty2
        drawTC[vertexCounter + 11] = tx2 -- bottom right
        drawTC[vertexCounter + 12] = ty2

        vertexCounter = vertexCounter + 12
    end

    -- Clear old vertices
    vertexCounter = vertexCounter + 1

    while (drawVC[vertexCounter] ~= nil) do
        drawVC[vertexCounter] = nil
        drawTC[vertexCounter] = nil

        vertexCounter = vertexCounter + 1
    end

    Graphics.glDraw(drawArgs)
end


function stageChangeEffect.onTick()
    if stageChangeEffect.transitionActive then
        stageChangeEffect.transitionTimer = stageChangeEffect.transitionTimer + 1

        local delayFactor = (stageChangeEffect.maxGridX - stageChangeEffect.minGridX) + (stageChangeEffect.maxGridY - stageChangeEffect.minGridY)

        if (stageChangeEffect.transitionTimer - stageChangeEffect.interPanelDelay*stageChangeEffect.panelFlipTime*delayFactor) > stageChangeEffect.panelFlipTime then
            stageChangeEffect.transitionActive = false
        end
    end
end

function stageChangeEffect.onCameraDraw(camIdx)
    if not stageChangeEffect.transitionActive or not maskCollection.activeOnClientside then
        return
    end

    local cam = Camera(camIdx)

    if oldStageBuffer == nil then
        oldStageBuffer = Graphics.CaptureBuffer()
        newStageBuffer = Graphics.CaptureBuffer()
    end

    oldStageBuffer:clear(-101)
    newStageBuffer:clear(-101)

    Graphics.redirectCameraFB(newStageBuffer, -66,-64)

    -- Split blocks into batches
    drawArgs.target = oldStageBuffer
    drawArgs.priority = -65

    for _,block in ipairs(stageChangeEffect.oldStageBlocks) do
        if (block.x + block.width) > cam.x and block.x < (cam.x + cam.width) and (block.y + block.height) > cam.y and block.y < (cam.y + cam.height) then
            if blockBatches[block.id] == nil then
                blockBatches[block.id] = {block}
                table.insert(blockBatchIDs,block.id)
            else
                table.insert(blockBatches[block.id],block)
            end
        end
    end

    -- Render those batches
    local i = 1

    while (blockBatchIDs[i] ~= nil) do
        local blockID = blockBatchIDs[i]

        drawBlockBatch(blockID)

        blockBatches[blockID] = nil
        blockBatchIDs[i] = nil
        i = i + 1
    end

    -- Split BGOs into batches
    for _,bgo in ipairs(stageChangeEffect.oldStageBGOs) do
        if (bgo.x + bgo.width) > cam.x and bgo.x < (cam.x + cam.width) and (bgo.y + bgo.height) > cam.y and bgo.y < (cam.y + cam.height) then
            if bgoBatches[bgo.id] == nil then
                bgoBatches[bgo.id] = {bgo}
                table.insert(bgoBatchIDs,bgo.id)
            else
                table.insert(bgoBatches[bgo.id],bgo)
            end
        end
    end

    -- Render those batches
    local i = 1

    while (bgoBatchIDs[i] ~= nil) do
        local bgoID = bgoBatchIDs[i]

        drawBGOBatch(bgoID)

        bgoBatches[bgoID] = nil
        bgoBatchIDs[i] = nil
        i = i + 1
    end

    Graphics.drawScreen{
        texture = newStageBuffer,priority = -64,
        shader = transitionShader,uniforms = {
            oldStageBuffer = oldStageBuffer,

            gridSize = stageChangeEffect.gridSize,
            panelFlipTime = stageChangeEffect.panelFlipTime,
            interPanelDelay = stageChangeEffect.interPanelDelay,

            minGridPos = vector(stageChangeEffect.minGridX,stageChangeEffect.minGridY),
            cameraPos = vector(cam.x,cam.y),
            bufferSize = vector(newStageBuffer.width,newStageBuffer.height),

            transitionTimer = stageChangeEffect.transitionTimer,
        },
    }
end


function stageChangeEffect.onInitAPI()
    registerEvent(stageChangeEffect,"onTick")
    registerEvent(stageChangeEffect,"onCameraDraw")
end


return stageChangeEffect