-- graphics.lua

local mm = require("util.matrixmath")

local graphics = {}

-- Window info
local win_w = 800
local win_h = 600
local fb_w = win_w
local fb_h = win_h

-- Camera controls
graphics.objrot = {0,0}
graphics.camerapan = {0,0,0}
graphics.chassis = {0,0,1}

local Scene = nil
local scenedir = "scene"
local scene_names = {
    "at",
    "colorcube",
    "spiral1",
    "spiral2",
    "exploding_scene",
    "tube",
    "flying_strings",
    "spiral3",
    "flying_strings2",
    "squid",
}
local scenes = {}

local Effect = nil
local effectdir = 'effect'
local effect_names = {
    'custom_effect',
    'clut_effect',
    'effect_chain',
    'iir_effect',
    'julia_effect',
}
local effects = {}

local Camera = nil
local cameradir = 'util'
local camera_names = {
    'camera4',
    'camera5',
}
local cameras = {}

-- Load  and call initGL on all scenes at startup
function graphics.initGL()
    for _,name in pairs(scene_names) do
        local fullname = scenedir..'.'..name
        local SceneLibrary = require(fullname)
        s = SceneLibrary.new()
        if s then
            if s.setDataDirectory then s:setDataDirectory("data") end
            s:initGL()
        end
        table.insert(scenes, s)
    end
    Scene = scenes[1]

    for _,name in pairs(effect_names) do
        local fullname = effectdir..'.'..name
        local EffectLibrary = require(fullname)
        e = EffectLibrary.new()
        if e then
            if e.setDataDirectory then e:setDataDirectory("data") end
            e:initGL(win_w,win_h)
            e:resize_fbo(win_w,win_h)
        end
        table.insert(effects, e)
    end
    Effect = nil

    for _,name in pairs(camera_names) do
        local fullname = cameradir..'.'..name
        local CameraLibrary = require(fullname)
        c = CameraLibrary.new()
        if c then
            if c.setDataDirectory then c:setDataDirectory("data") end
            c:init()
        end
        table.insert(cameras, c)
    end
    Camera = cameras[1]
end

function graphics.display()
    gl.glViewport(0,0, fb_w, fb_h)
    if Effect then Effect:bind_fbo() end

    local b = .3
    gl.glClearColor(b,b,b,0)
    gl.glClear(GL.GL_COLOR_BUFFER_BIT + GL.GL_DEPTH_BUFFER_BIT)
    gl.glEnable(GL.GL_DEPTH_TEST)

    if Scene and Camera then
        local m = {}
        mm.make_identity_matrix(m)
        if Camera.getModelMatrix then m = Camera:getModelMatrix() end

        local v = Camera:getViewMatrix()
        mm.affine_inverse(v)
        local p = {}
        local aspect = win_w / win_h
        mm.glh_perspective_rh(p, 60 * math.pi/180, aspect, .004, 500)

        if Scene.renderEye then Scene:renderEye(m,v,p) else
        Scene:render_for_one_eye(v,p) end
    end
    if Effect then Effect:unbind_fbo() end

    -- Effect post-processing and present
    if Effect then
        gl.glDisable(GL.GL_DEPTH_TEST)
        gl.glViewport(0,0, win_w, win_h)
        Effect:present(win_w, win_h)
    end

end

function graphics.resize(w, h)
    win_w, win_h = w, h
    fb_w, fb_h = win_w, win_h
    if Scene and Scene.resizeViewport then Scene:resizeViewport(w,h) end
    if Effect then Effect:resize_fbo(w,h) end
end

function graphics.timestep(absTime, dt)
    if Scene and Scene.timestep then Scene:timestep(absTime, dt) end
    if Effect and Effect.timestep then Effect:timestep(absTime, dt) end
end

function graphics.setbpm(bpm)
    Scene.BPM = bpm
end

-- A table of handlers for different track name-value pairs coming from
-- the rocket module. Values may be updated by messages from the editor.
-- Keys must match track names sent to editor.
graphics.sync_callbacks = {
    ["Scene"] = function(v)
        -- Switch scenes with an index
        if scenes[v] then Scene = scenes[v] end
    end,
    ["Effect"] = function(v)
        local oldEffect = Effect
        Effect = effects[v]
        if Effect and oldEffect ~= Effect then
            Effect:resize_fbo(win_w,win_h)
        end
    end,
    ["Camera"] = function(v)
        if cameras[v] then Camera = cameras[v] end
    end,

    ["tri:col"] = function(v)
        if Scene.col then Scene.col = v end
        if Scene.time then Scene.time = 6*v end
    end,

    ["at:insts"] = function(v)
        if Scene.npts then Scene.npts = math.floor(v) end
        if Scene.Vert and Scene.Vert.npts then Scene.Vert.npts = math.floor(v) end
    end,
    ["at:wraps"] = function(v)
        if Scene.wraps then Scene.wraps = v end
    end,
    ["at:twist"] = function(v)
        if Scene.twist then Scene.twist = v end
    end,

    ["explode"] = function(v)
        if Scene.Vert and Scene.Vert.effectGain then Scene.Vert.effectGain = v end
    end,
    ["forward"] = function(v)
        if Scene and Scene.forward then Scene.forward = v end
    end,
    ["heatup"] = function(v)
        if Scene and Scene.heatup then Scene.heatup = v end
    end,

    ["toCenter"] = function(v)
        if Scene and Scene.towardsCenter then Scene.towardsCenter = v end
    end,
    ["textFlatten"] = function(v)
        if Scene and Scene.textFlatten then Scene.textFlatten = v end
    end,
    ["textScroll"] = function(v)
        if Scene and Scene.textscroll then Scene.textscroll = v end
    end,

    ["phase"] = function(v)
        if Scene and Scene.phase then Scene.phase = v end
        if Scene.Vert and Scene.Vert.phase then Scene.Vert.phase = v end
    end,
    ["Cubes:colorPhase"] = function(v)
        if Scene and Scene.colorPhase then Scene.colorPhase = v end
    end,
    ["Cubes:colorSat"] = function(v)
        if Scene and Scene.colorSaturation then Scene.colorSaturation = v end
    end,

    ["lumth"] = function(v)
        if Effect and Effect.chain and Effect.chain.uniforms then Effect.chain.uniforms.lumThresh = v end
    end,
    ["Bloom.samx"] = function(v)
        if Effect and Effect.chain and Effect.chain.uniforms then Effect.chain.uniforms.samplesx = v end
    end,
    ["Bloom.samy"] = function(v)
        if Effect and Effect.chain and Effect.chain.uniforms then Effect.chain.uniforms.samplesy = v end
    end,
    ["Bloom.sigma"] = function(v)
        if Effect and Effect.chain and Effect.chain.uniforms then Effect.chain.uniforms.sigma = v end
    end,
    ["jborder"] = function(v)
        if Effect and Effect.chain and Effect.chain.uniforms then Effect.chain.uniforms.border = v end
    end,
    ["jparam"] = function(v)
        if Effect and Effect.chain and Effect.chain.uniforms then Effect.chain.uniforms.juliaParam = v end
    end,
    ["chromab"] = function(v)
        if Effect and Effect.chain and Effect.chain.uniforms then Effect.chain.uniforms.magnitude = v end
    end,
    ["mix_coeff"] = function(v)
        if Effect and Effect.mix_coeff then Effect.mix_coeff = v end
    end,

    ["cam1:pan"] = function(v)
        local Cam = cameras[1] if Cam then Cam.chassis[3] = v end
    end,
    ["cam1:panx"] = function(v)
        local Cam = cameras[1] if Cam then Cam.chassis[1] = v end
    end,
    ["cam1:pany"] = function(v)
        local Cam = cameras[1] if Cam then Cam.chassis[2] = v end
    end,
    ["cam2:panx"] = function(v)
        local Cam = cameras[2] if Cam then Cam.chassis[1] = v end
    end,
    ["cam2:pany"] = function(v)
        local Cam = cameras[2] if Cam then Cam.chassis[2] = v end
    end,
    ["cam2:pan"] = function(v)
        local Cam = cameras[2]
        if not Cam then return end
        Cam.chassis[3] = v
    end,
    ["cam2:spin"] = function(v)
        local Cam = cameras[2]
        if not Cam then return end
        Cam.objrot[1] = v
    end,
    ["cam2:roll"] = function(v)
        local Cam = cameras[2]
        if not Cam then return end
        Cam.objrot[2] = v
    end,

    -- Add new keyframe names here
}

function graphics.sync_params(get_param_value_at_current_time)
    -- The get_param_value_at_current_time function
    -- calls into rocket's track list of keyframes
    -- with the current time(according to main) as a parameter.
    local f = get_param_value_at_current_time
    if not f then return end

    for trackname,_ in pairs(graphics.sync_callbacks) do
        local val = f(trackname)
        local g = graphics.sync_callbacks[trackname]
        if g and val then g(val) end
    end
end

return graphics
