-- jellyfish_demo.lua

local bit = require("bit")
local ffi = require("ffi")
local rk = require("rocket")
if (ffi.os == "Windows") then
    --TODO: how do I link to socket package on Linux?
    package.loadlib("socket/core.dll", "luaopen_socket_core")
    local socket = require("socket.core")
end
local fpstimer = require("util.fpstimer")

local bass = require("bass")
local glfw = require("glfw")
local openGL = require("opengl")
openGL.loader = glfw.glfw.GetProcAddress
openGL:import()

local mm = require("util.matrixmath")
local gfx = require("scene.graphics")

local win_w = 800
local win_h = 600

local g_ft = fpstimer.create()

local bpm = 124.0050492
local rpb = 8 -- rows per beat
local row_rate = (bpm / 60) * rpb
local row_time = 0.0
local current_row = 0
local last_sent_row = 0
local paused = 1
local stream = nil

local g_lastFrameTime = 0
local g_lastFilterSwapTime = 0
local g_ft = fpstimer.create()
local animParams = {}


function bass_get_time()
    if stream then
        local pos = bass.BASS_ChannelGetPosition(stream, bass.BASS_POS_BYTE)
        local time = bass.BASS_ChannelBytes2Seconds(stream, pos)
        local startTime = .05 -- Start of first beat according to audacity
        local fudge = .12 -- Line hits up to beat visually

        -- TODO: why is there half a beat discrepancy when running without sync?
        fudge = fudge - 0.48385126/2

        return time - startTime - fudge
    end
end

function bass_get_row()
    if stream then
        return bass_get_time() * row_rate
    end
end

function cb_pause(flag)
    if stream then
        if flag == 1 then
            bass.BASS_ChannelPause(stream)
        else
            bass.BASS_ChannelPlay(stream, false)
        end
    end
end

function cb_setrow(row)
    current_row = row
    last_sent_row = row
    row_time = row / row_rate

    local pos = bass.BASS_ChannelSeconds2Bytes(stream, row / row_rate)
    bass.BASS_ChannelSetPosition(stream, pos, bass.BASS_POS_BYTE)
end

function cb_isplaying()
    return (bass.BASS_ChannelIsActive(stream) == bass.BASS_ACTIVE_PLAYING)
end

local cbs = {
    cb_pause,
    cb_setrow,
    cb_isplaying
}

function onkey(window,k,code,action,mods)
    if action == glfw.GLFW.PRESS or action == glfw.GLFW.REPEAT then
        if k == glfw.GLFW.KEY_ESCAPE then os.exit(0)
        end
    end
end

function init_gl()
    gfx.init_gl()
end

function display()
    if rk then animParams.tparam = rk.get_value("tparam", current_row) or 0 end

    gfx.display()
end

function resize(window, w, h)
    win_w = w
    win_h = h
    gl.Viewport(0,0, win_w, win_h)
    gfx.resize(w, h)
end

function get_current_param_value_by_name(pname)
    if not rk then return end
    return rk.get_value(pname, current_row)
end

function timestep(absTime, dt)
    if cb_isplaying() then
        row_time = row_time + dt
        current_row = row_rate * row_time
        gfx.sync_params(get_current_param_value_by_name)
        gfx.timestep(absTime, dt)
    else
        if socket then socket.sleep(0.001) end
    end
end

function dofile(filename)
    local f = assert(loadfile(filename))
    return f()
end

function main()
    if arg[1] and arg[1] == "sync" then
        SYNC_PLAYER = 1
    end

    if SYNC_PLAYER then
        local success = rk.connect_demo()
        -- Sort keys before inserting
        alphakeys = {}
        for n in pairs(gfx.sync_callbacks) do table.insert(alphakeys, n) end
        table.sort(alphakeys)
        for _,k in ipairs(alphakeys) do
            print("Create track: ",k)
            rk.create_track(k)
            rk.send_track_name(rk.obj, k)
        end
    else
        --print("Load tracks from file")
        local tracks_module = 'tracks'
        local status, module = pcall(require, tracks_module)
        if not status then
            print('Tracks file ['..tracks_module ..'.lua] not found.')
            print('Re-launch with argument sync to connect to editor.')
            print('')
            print('Press any key to exit...')
            io.read()
            os.exit(1)
        end
        if rk then rk.sync_tracks = module end
    end

    -- Load config file
    if arg[1] and arg[1] == "compo" then
        fullscreen = true
        vsync = true
        showfps = false
    else
        dofile('appconfig.lua')
        win_w, win_h = window_w, window_h
    end

    glfw.glfw.Init()

    local windowTitle = "Jellyfish: The Jam"
    local monitor = nil
    if fullscreen == true then monitor = glfw.glfw.GetPrimaryMonitor() end
    if monitor then
        mode = glfw.glfw.GetVideoMode(monitor)
        win_w = mode.width
        win_h = mode.height
        print("Monitor mode:",mode.width, mode.height)
    end

    window = glfw.glfw.CreateWindow(win_w,win_h,windowTitle,monitor,nil)
    glfw.glfw.MakeContextCurrent(window)

    glfw.glfw.SetKeyCallback(window, onkey)
    glfw.glfw.SetWindowSizeCallback(window, resize)

    if fullscreen then
        glfw.glfw.SetInputMode(window, glfw.GLFW.CURSOR, glfw.GLFW.CURSOR_HIDDEN)
    end

    if vsync then
        glfw.glfw.SwapInterval(1)
    else
        glfw.glfw.SwapInterval(0)
    end

    init_gl()
    resize(window, win_w, win_h)

    local init_ret = bass.BASS_Init(-1, 44100, 0, 0, nil)
    stream = bass.BASS_StreamCreateFile(false, "data/oh2.mp3", 0, 0, bass.BASS_STREAM_PRESCAN)
    --[[
        oh2.mp3 is 2:32 @ 124 BPM == 153 seconds == 2.55 min == 316.2 beats ~= 316
        316 beats * 8 rows/beat == 2528 rows
        316 beats counted
        End time: 2'32.947"
        Start time: 0.05"
        Total time: 152.897 seconds == 2.548283 minutes
        316 beats / 2.548283 minutes == 124.0050491 BPM == 2.06675081 beats/second == 0.48385126 seconds/beat
    ]]
    bpm = 124.0050492
    local song_end = 2*60 + 32.947
    gfx.setbpm(bpm)
    gfx.draw_shadertoy = true
    gfx.reset_camera_to_initial()

    bass.BASS_Start()
    bass.BASS_ChannelPlay(stream, false)

    g_lastFrameTime = 0
    local gcc = 0 -- collect garbage every n cycles
    while glfw.glfw.WindowShouldClose(window) == 0 do
        if SYNC_PLAYER then
            local uret = rk.sync_update(rocket.obj, current_row, cbs)
            if uret and uret ~= 0 then
                print("sync_update returned: "..uret)
                --rk.connect_demo()
            end
        end

        glfw.glfw.PollEvents()
        g_ft:onFrame()
        display()

        local now = bass_get_time()
        timestep(now, now - g_lastFrameTime)
        g_lastFrameTime = now

        if not SYNC_PLAYER then
            -- Quit at the end of the song
            if now > song_end then
                glfw.glfw.SetWindowShouldClose(window, true)
            end
        end

        if showfps then
            if (ffi.os == "Windows") then
                glfw.glfw.SetWindowTitle(window, windowTitle.." "..math.floor(g_ft:getFPS()).." fps")
            end
        end
        glfw.glfw.SwapBuffers(window)

        bass.BASS_Update(0) -- decrease the chance of missing vsync

        gcc = gcc + 1
        if gcc > 10 then
            gcc = 0
            collectgarbage()
        end
    end

    bass.BASS_StreamFree(stream)
    bass.BASS_Free()
end

main()
