-- exploding_scene.lua

exploding_scene = {}
exploding_scene.__index = exploding_scene

function exploding_scene.new(...)
    local self = setmetatable({}, exploding_scene)
    if self.init ~= nil and type(self.init) == "function" then
        self:init(...)
    end 
    return self
end

local ffi = require("ffi")
local VsLib = require("scene.vs")
local FsLib = require("scene.fs")
local EffectLibrary = require("effect.effect_chain")
local CameraLibrary = require("util.camera4")
local mm = require("util.matrixmath")

local hsv = [[
uniform float time;

// https://www.shadertoy.com/view/MsS3Wc
// Official HSV to RGB conversion 
vec3 hsv2rgb( in vec3 c )
{
    vec3 rgb = clamp( abs(mod(c.x*6.0+vec3(0.0,4.0,2.0),6.0)-3.0)-1.0, 0.0, 1.0 );
    return c.z * mix( vec3(1.0), rgb, c.y);
}

void main()
{
    vec3 hsl = vec3( uv.x, 1.0, uv.y );
    vec3 rgb = hsv2rgb( hsl );
    fragColor = vec4( rgb, 1.0 );
}
]]

function exploding_scene:init()
    self.Vert = VsLib.new()
    self.Frag = FsLib.new()
    self.Camera = CameraLibrary.new()

    self.Vert.npts = 20*20
    self.Vert.fogDensity = .0
    self.Vert.dynamic_scale = false
    self.fw,self.fh = 512,512
    self.Vert.clearColor = {42/255,114/255,154/255,1}
end

function exploding_scene:setDataDirectory(dir)
    self.Vert:setDataDirectory(dir)
    --self.Frag:setDataDirectory(dir)
    if self.Camera.setDataDirectory then self.Camera:setDataDirectory("data") end
    self.dataDir = dir
end

function exploding_scene:initGL()
    self.Vert:initGL()
    self.Frag:initGL()
    self.Camera:init()

    -- Cam params
    self.Camera.camera4pan = {0,1,3}
    self.Camera.chassis = {0, 2.5, 9.7}

    self.PostFX = EffectLibrary.new()
    if self.PostFX then self.PostFX:initGL(self.fw, self.fh) end

    local args = {'frag','texlight'}
    local ret = self.Vert:handleCommand(args)
    self.Vert:buildShader(self.Vert.src)


    function readAll(file)
        local f = io.open(file, "rb")
        local content = f:read("*all")
        f:close()
        return content
    end
    --self.Frag:buildShader(hsv)

    local fn = self.dataDir..'/shaders/fs/@party'
    self.Frag:buildShader(readAll(fn))
end

function exploding_scene:exitGL()
    self.Vert:exitGL()
    self.Frag:exitGL()
    if self.PostFX then self.PostFX:exitGL() end
end

function exploding_scene:renderEye(model, view, proj)

    -- Save viewport dimensions
    -- TODO: this is very inefficient; store them manually, restore at caller
    local vp = ffi.new("GLuint[4]", 0,0,0,0)
    gl.glGetIntegerv(GL.GL_VIEWPORT, vp)
    -- Save bound FBO
    local boundfbo = ffi.new("int[1]")
    gl.glGetIntegerv(GL.GL_FRAMEBUFFER_BINDING, boundfbo)

    -- Draw quad scene to this scene's first internal fbo
    if self.PostFX then self.PostFX:bind_fbo() end

    gl.glClearColor(0, 0.5, 1, 0)
    gl.glClear(GL.GL_COLOR_BUFFER_BIT + GL.GL_DEPTH_BUFFER_BIT)
    gl.glEnable(GL.GL_DEPTH_TEST)

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

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

    self.Frag:renderEye(m,v,p)


    if self.PostFX then self.PostFX:unbind_fbo() end
    gl.glEnable(GL.GL_DEPTH_TEST)

    -- Restore viewport
    gl.glViewport(vp[0],vp[1],vp[2],vp[3])
    -- Restore FBO binding
    if boundfbo[0] ~= 0 then
        gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, boundfbo[0])
    end

    -- set texture reference
    local lastFilt = self.PostFX.filters[#self.PostFX.filters] 
    self.Vert.tex = lastFilt.fbo.tex

    self.Vert:renderEye(model, view, proj)
end

function exploding_scene:render_for_one_eye(view, proj)
    self.Vert:render_for_one_eye(view, proj)
    self.Frag:render_for_one_eye(view, proj)
end

function exploding_scene:timestep(absTime, dt)
    if true then
        self.Vert:timestep(absTime, dt)
        self.Frag:timestep(absTime, dt)
        if self.PostFX and self.PostFX.timestep then self.PostFX:timestep(absTime, dt) end
    end
end

return exploding_scene
