-- Genuary 12: Boxes Only

local boxes = {}
local conveyor = {}
local bucket = {}
local machine = {}
local world

local sceneSize
local cameraPos = vec2(0, 0)
local cameraTarget = vec2(0, 0)
local panSpeed = 220
local phase = "run"
local cameraTween

local spawnTimer = 0.5
local spawnInterval = 1

local p2m = 128

local function resetPhysics()
    world = physics2d.world()
    world.gravity = vec2(0, -15.8)
    boxes = {}
end

local function setupScene()    
    sceneSize = vec2(WIDTH, HEIGHT)
    spawnTimer = 0.5
    spawnInterval = 1

    local aspect = (773/987)
    local topHeight = 120 * (280/987)
    machine.size = vec2(280, 280 * aspect)
    machine.pos = vec2(machine.size.x/2, HEIGHT - (machine.size.y/2) + topHeight)    
    machine.chute = vec2(machine.pos.x + 10, machine.pos.y - 40)

    bucket.size = vec2(280, 280 * aspect)
    bucket.pos = vec2(WIDTH - bucket.size.x/2, bucket.size.y/2 + topHeight)
    
    conveyor.pos = vec2((WIDTH - bucket.size.x)/2, HEIGHT * 0.35)
    conveyor.size = vec2(WIDTH - bucket.size.x, 24)
    conveyor.speed = 220/p2m

    resetPhysics()

    -- Conveyor base
    conveyor.body = world:body(STATIC, conveyor.pos.x/p2m, conveyor.pos.y/p2m)
    local beltCol = conveyor.body:box(conveyor.size.x/p2m, conveyor.size.y/p2m)
    beltCol.restitution = 0
    beltCol.friction = 1.0
    conveyor.body.onPreSolve = function(contact)
        if contact.worldNormal.y > 0.2 then
            contact.tangentSpeed = conveyor.speed
        end
    end

    -- Bucket: left wall, right wall, bottom
    local leftPos = bucket.pos + vec2(-bucket.size.x * 0.5 + 15, 0)
    local leftWall = world:body(STATIC, leftPos.x/p2m, leftPos.y/p2m)
    leftWall:box(12/p2m, bucket.size.y/p2m)

    local rightPos = bucket.pos + vec2(bucket.size.x * 0.5 - 15, 0)
    local rightWall = world:body(STATIC, rightPos.x/p2m, rightPos.y/p2m)
    rightWall:box(12/p2m, (bucket.size.y + 20)/p2m)

    local bottomPos = bucket.pos + vec2(0, -bucket.size.y * 0.5 + 10)
    local bottom = world:body(STATIC, bottomPos.x/p2m, bottomPos.y/p2m)
    bottom:box(bucket.size.x/p2m, 12/p2m)

    -- Floor
    local floor = world:body(STATIC, (WIDTH * 0.5)/p2m, 0)
    floor:box(WIDTH/p2m, 12/p2m)
end

local function spawnBox()
    local b = {}
    b.size = vec2(64, 64)
    b.body = world:body(DYNAMIC, machine.chute.x/p2m, (machine.chute.y + 40)/p2m)
    b.body.rotation = math.random(1, 50)
    b.asset = asset["crate" .. tostring(math.random(1,3))]
    local boxCol = b.body:box(b.size.x/p2m, b.size.y/p2m)
    boxCol.restitution = 0.15
    boxCol.friction = 0.3
    table.insert(boxes, b)
end

local function updateCamera(dt)
    if phase == "reset" then
        cameraPos = vec2(0, 0)
        cameraTarget = vec2(0, 0)
        phase = "run"
        setupScene()
    end
end

local function drawMachine()
    pushStyle()
    sprite(asset.boxMachine, machine.pos, machine.size)
    popStyle()
end

local function drawConveyor()
    pushStyle()
    rectMode(CENTER)
    fill(34, 36, 39)
    rect(conveyor.pos.x, conveyor.pos.y, conveyor.size.x, conveyor.size.y)
    fill(90)
    for i = 1, 10 do
        local x = conveyor.pos.x - conveyor.size.x * 0.5 + i * (conveyor.size.x / 10)
        rect(x, conveyor.pos.y, 8, conveyor.size.y)
    end
    popStyle()
end

local function drawBucket()
    pushStyle()
    rectMode(CENTER)
    fill(120)
    sprite(asset.boxMachine, bucket.pos, bucket.size)
    popStyle()
end

local function drawBoxes()
    pushStyle()
    rectMode(CENTER)
    fill(210)
    for i = 1, #boxes do
        local b = boxes[i]
        local p = b.body.position
        matrix.push()
        translate(b.body.position * p2m)
        rotate(b.body.rotation)
        sprite(b.asset, 0, 0, b.size.x, b.size.y)
        matrix.pop()
    end
    popStyle()
end

function setup()
    viewer.mode = FULLSCREEN
    setupScene()
end

function draw()
    background(20)    
    
    style.tint(50,80,100)
    sprite(asset.metal, WIDTH/2, HEIGHT/2, WIDTH, HEIGHT)
    style.tint(255)
    sprite(asset.fade, WIDTH/2, HEIGHT/2, WIDTH, HEIGHT)
    updateCamera(DeltaTime)
    world:step(DeltaTime)
    spawnTimer = spawnTimer + DeltaTime
    if phase == "run" and spawnTimer >= spawnInterval then
        spawnTimer = 0
        spawnBox()
    end

    pushMatrix()
    translate(-cameraPos.x, -cameraPos.y)
    drawBoxes()
    drawMachine()
    drawConveyor()
    drawBucket()
    
    translate(getCameraTarget())
    drawConveyor()
    drawBucket()
    popMatrix()
end

function getCameraTarget()
    return vec2(sceneSize.x - bucket.size.x, -sceneSize.y + bucket.size.y)
end

function panCamera()
    phase = "pan"
    cameraTarget = getCameraTarget()
    if cameraTween then
        cameraTween:cancel()
    end
    cameraTween = tween(cameraPos)
    :to{ x = cameraTarget.x, y = cameraTarget.y }
    :time(1.6)
    :ease(tween.cubicInOut)
    :onComplete(function()
        phase = "reset"
    end)
end

function touched(t)
    if t.state == BEGAN and phase == "run" then
        panCamera()
    end
end

function sizeChanged()
    resetPhysics()
    setupScene()
end
