--[[
    mainapp.lua

    A GLFW user interface around the Scene, Camera and PostFx types
    for interactive editing and development.
]]
local bit = require("bit")
local ffi = require("ffi")
local glfw = require("glfw")
local openGL = require("opengl")
local mm = require("util.matrixmath")
local fpstimer = require("util.fpstimer")
local gfx = require("scene.graphics")

openGL.loader = glfw.glfw.GetProcAddress
openGL:import()

-- Types from:
-- https://github.com/nanoant/glua/blob/master/init.lua
local glFloatv = ffi.typeof('GLfloat[?]')

local win_w = 800
local win_h = 600
-- mouse motion internal state
local oldx = 0
local oldy = 0
local newx = 0
local newy = 0
local which_button = -1
local which_mods = 0

local g_lastFrameTime = 0

local g_ft = fpstimer.create()

local sidx = 0
local tidx = 1

local keys_pressed = {}
function onkey(window,k,code,action,mods)
    which_mods = mods
    if action == glfw.GLFW.PRESS then
        keys_pressed[k] = true
    elseif action == glfw.GLFW.RELEASE then
        keys_pressed[k] = false
    end

    local func_table = {
        [glfw.GLFW.KEY_ESCAPE] = function (x) os.exit(0) end,
        [glfw.GLFW.KEY_Z] = function (x) sidx = 3*3 end, -- "shoulder"
        [glfw.GLFW.KEY_X] = function (x) sidx = 3*0 end, -- "elbow"
        [glfw.GLFW.KEY_C] = function (x) sidx = 3*1 end, -- "hand"
        [glfw.GLFW.KEY_V] = function (x) sidx = 3*2 end, -- "root?" - shouldn't move

        [glfw.GLFW.KEY_G] = function (x) tidx = 1 end, -- feet
        [glfw.GLFW.KEY_H] = function (x) tidx = 2 end, -- arms/hands

        [glfw.GLFW.KEY_SPACE] = function (x) gfx.dump_info() end,
        [glfw.GLFW.KEY_P] = function (x) gfx.toggle_animation() end,
        [glfw.GLFW.KEY_GRAVE_ACCENT] = function (x) gfx.switch_draw_list() end,
        [glfw.GLFW.KEY_F1] = function (x) gfx.switch_to_effect(1) end,
        [glfw.GLFW.KEY_F2] = function (x) gfx.switch_to_effect(2) end,
        [glfw.GLFW.KEY_F3] = function (x) gfx.switch_to_effect(3) end,
        [glfw.GLFW.KEY_F4] = function (x) gfx.switch_to_effect(4) end,
        [glfw.GLFW.KEY_F5] = function (x) gfx.switch_to_effect(5) end,
        [glfw.GLFW.KEY_F6] = function (x) gfx.switch_to_effect(nil) end,

        [glfw.GLFW.KEY_1] = function (x) gfx.switch_to_camera(1) end,
        [glfw.GLFW.KEY_2] = function (x) gfx.switch_to_camera(2) end,
        [glfw.GLFW.KEY_3] = function (x) gfx.switch_to_camera(3) end,
    }

    local control_func_table = {
        [glfw.GLFW.KEY_1] = function (x) gfx.switch_to_scene("scene.trihex_scene") end,
        [glfw.GLFW.KEY_2] = function (x) gfx.switch_to_scene("scene.inst_scene") end,
        [glfw.GLFW.KEY_3] = function (x) gfx.switch_to_scene("scene.jellyfish_scene") end,
        [glfw.GLFW.KEY_4] = function (x) gfx.switch_to_scene("scene.tri_scene") end,
        [glfw.GLFW.KEY_5] = function (x) gfx.switch_to_scene("scene.font_scene") end,
    }

    if action == glfw.GLFW.PRESS or action == glfw.GLFW.REPEAT then
        local f = func_table[k]
        if mods == glfw.GLFW.MOD_CONTROL then f = control_func_table[k] end
        if f then f() end
    end
end

function onmouseclick(window, button, action, mods)
    local x, y
    glfw.glfw.GetCursorPos(window, x, y)
    which_button = button

    if action == 0 then
        which_button = -1
    end
    which_mods = mods
end

function onmousemove(window, x, y)
    oldx = newx
    oldy = newy
    newx = x
    newy = y
    local mmx = x - oldx
    local mmy = y - oldy
    local gain = 100.0

    local pts = gfx.get_pts(tidx)

    if which_mods == glfw.GLFW.MOD_SHIFT then
        gain = gain * 10
    end

    local func_table = {
        [glfw.GLFW.MOUSE_BUTTON_1] = function(x)
            -- nothing
        end,
        [glfw.GLFW.MOUSE_BUTTON_2] = function(x)
            if pts then
                gain = gain / 10
                if sidx == 3 then gain = gain * -10 end
                pts[sidx] = pts[sidx] + mmy/gain
                pts[sidx+1] = pts[sidx+1] + mmx/gain
            end
        end,
        [glfw.GLFW.MOUSE_BUTTON_3] = function(x)
            if pts then
                gain = 10
                if sidx == 3 then gain = gain * 10 end
                pts[sidx+2] = pts[sidx+2] + mmx/gain
            end
        end,
    }
    local f = func_table[which_button]
    if f then f() end
end

function display()
    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 init_gl()
    gfx.init_gl()
end

function timestep(absTime, dt)
    gfx.timestep(absTime, dt)

    local speed = 10
    if which_mods == glfw.GLFW.MOD_CONTROL then
        speed = speed * 10
    elseif which_mods == glfw.GLFW.MOD_SHIFT then
        speed = speed / 10
    end

    local d = dt * speed
    local fd = {0,0,-d,0}
    local bk = {0,0,d,0}
    local rt = {d,0,0,0}
    local lf = {-d,0,0,0}
    local up = {0,d,0,0}
    local dn = {0,-d,0,0}

    local cammove = gfx.cammove
    if cammove then
        if keys_pressed[glfw.GLFW.KEY_W] then cammove(fd) end
        if keys_pressed[glfw.GLFW.KEY_S] then cammove(bk) end
        if keys_pressed[glfw.GLFW.KEY_A] then cammove(lf) end
        if keys_pressed[glfw.GLFW.KEY_D] then cammove(rt) end
        if keys_pressed[glfw.GLFW.KEY_Q] then cammove(up) end
        if keys_pressed[glfw.GLFW.KEY_E] then cammove(dn) end
    end

    local camyaw = gfx.camyaw
    if camyaw then
        if keys_pressed[glfw.GLFW.KEY_Z] then camyaw(-d*.1) end
        if keys_pressed[glfw.GLFW.KEY_C] then camyaw(d*.1) end
    end

--[[
    local targetmove = gfx.targetmove
    if targetmove then
        if keys_pressed[glfw.GLFW.KEY_T] then targetmove(fd) end
        if keys_pressed[glfw.GLFW.KEY_G] then targetmove(bk) end
        if keys_pressed[glfw.GLFW.KEY_F] then targetmove(lf) end
        if keys_pressed[glfw.GLFW.KEY_H] then targetmove(rt) end
        if keys_pressed[glfw.GLFW.KEY_R] then targetmove(up) end
        if keys_pressed[glfw.GLFW.KEY_Y] then targetmove(dn) end
    end]]
end

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

function main()
    for k,v in pairs(arg) do
        if k > 0 then
            if v == 'fullscreen' then fullscreen = true end
        end
    end

    -- Load config file
    dofile('appconfig.lua')
    win_w, win_h = window_w, window_h

    glfw.glfw.Init()

    local windowTitle = "Lua OpenGL App"
    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.SetMouseButtonCallback(window, onmouseclick)
    glfw.glfw.SetCursorPosCallback(window, onmousemove)
    glfw.glfw.SetWindowSizeCallback(window, resize)

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

    init_gl()
    resize(window, win_w, win_h)

    local gcc = 0 -- collect garbage every n cycles
    while glfw.glfw.WindowShouldClose(window) == 0 do
        glfw.glfw.PollEvents()
        g_ft:onFrame()
        display()

        local now = os.clock()
        timestep(now, now - g_lastFrameTime)
        g_lastFrameTime = now
        if (ffi.os == "Windows") then
            glfw.glfw.SetWindowTitle(window, windowTitle.." "..math.floor(g_ft:getFPS()).." fps")
        end
        glfw.glfw.SwapBuffers(window)

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

main()
