local onlinePlay = require("scripts/onlinePlay")

local easing = require("ext/easing")

local blockShockwaves = {}


blockShockwaves.idList = {}
blockShockwaves.idMap = {}

blockShockwaves.affectedBlocks = {}
blockShockwaves.affectedBlockMap = {}


blockShockwaves.width = 320


local startCommand = onlinePlay.createCommand("blockShockwaves_start",onlinePlay.IMPORTANCE_MAJOR)


function blockShockwaves.registerBlock(blockID)
    table.insert(blockShockwaves.idList,blockID)
    blockShockwaves.idMap[blockID] = true
end



local function startInternal(x,y)
    local collider = Colliders.Box(x - blockShockwaves.width*0.5,y - 8,blockShockwaves.width,16)
    local blocks = Colliders.getColliding{a = collider,b = blockShockwaves.idList,btype = Colliders.BLOCK}

    for _,block in ipairs(blocks) do
        if not blockShockwaves.affectedBlockMap[block] then
            table.insert(blockShockwaves.affectedBlocks,block)
            blockShockwaves.affectedBlockMap[block] = true
        end

        local distance = math.abs((block.x + block.width*0.5) - x)

        block.data.moveTimer = -distance/4
        block.data.distanceMultiplier = 1 - distance/blockShockwaves.width*2
        block.data.startY = block.y
        block.data.lastY = block.y
    end
end

function blockShockwaves.start(x,y)
    if onlinePlay.currentMode ~= onlinePlay.MODE_OFFLINE then
        startCommand:send(0, x,y)
    end

    startInternal(x,y)
end

function startCommand.onReceive(sourcePlayerIdx, x,y)
    startInternal(x,y)
end


local function updateBlock(block)
    if not block.isValid then
        return true
    end

    block.data.moveTimer = block.data.moveTimer + 1.5

    local t = math.clamp(block.data.moveTimer/64,0,1)
    local newY = block.data.startY + math.sin((t*3 + math.max(0,t - 0.5)*2)*math.pi)*(1 - t)*block.data.distanceMultiplier*64

    block.speedY = newY - block.y
    --block.data.lastY = block.y
    block:translate(0,newY - block.y)

    if block.speedY == 0 then
        block.speedY = 0.01
    end

    if block.data.moveTimer > 64 then
        block.speedY = 0
        block.data.moveTimer = nil
        block.data.startY = nil
        block.data.lastY = nil
        return true
    end

    return false
end

local function handlePlayer(p)
    local speedY = 0.1
    local minY

    for _,block in ipairs(blockShockwaves.affectedBlocks) do
        if (p.x + p.width) >= block.x and p.x <= (block.x + block.width) and (p.y + p.height - p.speedY) <= block.data.lastY and (p.y + p.height + p.speedY) >= block.data.lastY then
            if minY == nil or minY > block.y then
                minY = block.y
            end

            if speedY == nil or block.speedY > speedY then
                speedY = block.speedY
            end
        end
    end


    if minY ~= nil then
        for _,block in Block.iterateIntersecting(p.x,minY - p.height,p.x + p.width,minY + p.height) do
            if not blockShockwaves.affectedBlockMap[block] and (Block.SOLID_MAP[block.id] or Block.SEMISOLID_MAP[block.id]) then
                minY = math.min(minY,block.y)
            end
        end

        p.y = minY - p.height
        p.speedY = p.speedY
    end
end


function blockShockwaves.onTick()
    local i = 1

    while (blockShockwaves.affectedBlocks[i] ~= nil) do
        local block = blockShockwaves.affectedBlocks[i]

        if updateBlock(block) then
            table.remove(blockShockwaves.affectedBlocks,i)
            blockShockwaves.affectedBlockMap[block] = nil
        else
            i = i + 1
        end
    end

    for _,p in ipairs(Player.get()) do
        --[[local blockIdx = getPlayerStoodOnBlockIndex(p)

        if blockIdx > 0 and blockShockwaves.affectedBlockMap[Block(blockIdx)] then
            p.y = p.y + Block(blockIdx).speedY
        end]]

        handlePlayer(p)
    end
end

function blockShockwaves.onInitAPI()
    registerEvent(blockShockwaves,"onTick")
end


return blockShockwaves