-- constants
local LIMIT = 2000
local SCALE_MIN = 0.27
local SCALE_MAX = 0.67
local SPEED_MIN = 0.8
local SPEED_MAX = 1.2
local SIZE = Vector3.new(0.6, 0.3, 0.24)
local ANGLE = math.pi / 4
local SPIN = 0.9
local FADE_TIME = 0.3
local SATURATE = Color.new(1.5, 1)
local SCALE = Vector3.new( settings.scale )
local ROTATION = Rotation.euler( ANGLE, 0, 0 )
local OFFSET = ROTATION:multiplyVector( Vector3.new( SIZE.x, 0, 0 ) * SCALE )
local COLOR1 = settings.color1 * SATURATE
local COLOR2 = settings.color2 * SATURATE

-- options
ym.setCullMode( true )
ym.setZBuffer( true )
ym.setGlobalLight( Vector3.new( 0, -1, 0 ) )

-- setup
local mesh = Mesh.new({
    vertices = {
        0, 0, -SIZE.z,
        -SIZE.x, 0, 0,
        0, -SIZE.y, 0,

        0, 0, -SIZE.z,
        0, -SIZE.y, 0,
        SIZE.x, 0, 0,

        0, 0, -SIZE.z,
        SIZE.x, 0, 0,
        0, SIZE.y, 0,

        0, 0, -SIZE.z,
        0, SIZE.y, 0,
        -SIZE.x, 0, 0,

        0, 0,  SIZE.z,
        0, -SIZE.y, 0,
        -SIZE.x, 0, 0,

        0, 0, SIZE.z,
        SIZE.x, 0, 0,
        0, -SIZE.y, 0,

        0, 0, SIZE.z,
        0, SIZE.y, 0,
        SIZE.x, 0, 0,

        0, 0,  SIZE.z,
        -SIZE.x, 0, 0,
        0, SIZE.y,  0,
    }
})

-- state
local petals = {}
local active = false
local spawnEta = 0

-- spawn
local pointer = Node.new()
pointer:addMesh( mesh )
pointer.rotation = ROTATION
pointer.scale = SCALE
pointer.color = COLOR1
pointer:spawn()

-- functions
function newPetal()
    -- enforce limit
    if #petals >= LIMIT then
        return null
    end

    -- solve initial physics stuff
    local position = pointer.position + OFFSET / 4
    local rotation = Rotation.angleZ( ANGLE + ym.random(-settings.spread, settings.spread) )
    local direction = rotation:multiplyVector( Vector3.new( 1, 0, 0 ) )
    local scale = settings.scale * ym.random(SCALE_MIN, SCALE_MAX)
    local velocity = direction * ym.random(settings.speed * SPEED_MIN, settings.speed * SPEED_MAX) * 6
    local spin = Vector3.new( ym.random(-SPIN, SPIN), ym.random(-SPIN, SPIN), ym.random(-SPIN, SPIN) )
    
    -- create petal node
    local node = Node.new()
    node:addMesh( mesh )
    node.position = position
    node.scale = Vector3.new(scale)
    node.rotation = rotation
    node.color = COLOR1
    node.blend = true

    -- create petal entry
    local petal = {
        node = node,
        velocity = velocity,
        spin = spin,
        age = 0
    }
    
    -- add to petal list
    table.insert(petals, petal)
    
    -- spawn petal node
    node:spawn()
end

function stepPointer()
    pointer.position = Vector3.new( ym.getCursorPosition(), 0 ) + OFFSET
end

function stepSpawn(dt)
    if active then
        -- update spawn eta
        spawnEta = spawnEta - dt

        -- while time to spawn
        while spawnEta <= 0 do
            -- spawn petal
            newPetal()

            -- update spawn eta
            spawnEta = spawnEta + 1.0 / settings.rate
        end
    end
end

function stepPetal(dt, petal)
    local expiring = settings.life - petal.age
    local node = petal.node

    -- expire if expired or in front of pointer
    if expiring < 0 then
        node:despawn()
        return false
    end

    -- update position by velocity
    node.position = node.position + petal.velocity * dt
    
    -- update rotation by spin
    node.rotation:rotateBy( petal.spin * dt )
    
    -- update color
    node.color = COLOR1:lerp( COLOR2, petal.age / settings.life )

    -- update fade alpha
    local fadeTime = expiring / settings.life
    if fadeTime < FADE_TIME then
        node.color.a = fadeTime / FADE_TIME
    end
    
    -- increment age, accelerate if in front/above pointer
    if node.position.x < pointer.position.x or node.position.y < pointer.position.y then
        petal.age = petal.age + dt * 4
    else
        petal.age = petal.age + dt
    end

    return true
end

function stepPetals(dt)
    -- step each petal and remove those that need to despawn
    for i = #petals, 1, -1 do
        if not stepPetal(dt, petals[i]) then
            table.remove(petals, i)
        end
    end
end

-- events
function start()
    active = true
end

function stop()
    active = false
end

function step(deltaTime)
    stepPointer()
    stepSpawn(deltaTime)
    stepPetals(deltaTime)
end
