--[[ julia_effect.lua

	Calls into effect_chain with a specific list of filters to initialize.
]]

julia_effect = {}

julia_effect.__index = julia_effect

require("effect.effect_chain")

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

function julia_effect:init(params)
    -- Set custom filter chain parameters here
    local params = {}
    params.filter_names = {
        "chromaticAberration",
        "juliaBorder",
        "passthrough",
    }
    self.filter_names = params.filter_names
    self.chain = effect_chain.new(params)

    -- If is table...
    self.chain.uniforms.sigma = 25
    self.chain.uniforms.lumThresh = .5
    self.chain.uniforms.border = .25
    self.chain.uniforms.magnitude = .03
    self.chain.uniforms.juliaParam = 0
end

-- This is functioning as a metadata access point
function julia_effect:get_filter_names()
    return self.filter_names
end

local inv = [[
uniform sampler2D tex2;
void main()
{
    fragColor = texture(tex, uv);
    fragColor += texture(tex2, uv);
}
]]

function julia_effect:initGL(w,h)
    self.chain:initGL(w,h)


    local params = {}
    params.name = "inv"
    params.source = inv


    local filt = Filter.new(params)
    filt:initGL()

    -- Get w,h from the first fbo in the list if not specified.
    if not w then
        local first = self.chain.filters[1].fbo
        w,h = first.w, first.h
    end
    filt:resize(w,h)

    -- Re-use the VBO for each program
    local vpos_loc = gl.glGetAttribLocation(filt.prog, "vPosition")
    gl.glVertexAttribPointer(vpos_loc, 2, GL.GL_FLOAT, GL.GL_FALSE, 0, nil)
    gl.glEnableVertexAttribArray(vpos_loc)

    --table.insert(self.filters, filt)
    self.filt = filt
end

function julia_effect:exitGL(w,h)
    self.chain:exitGL(w,h)
end

function julia_effect:resize_fbo(w,h)
    self.chain:resize_fbo(w,h)
end

function julia_effect:bind_fbo()
    self.chain:bind_fbo()
end

function julia_effect:unbind_fbo()
    self.chain:unbind_fbo()
end

function julia_effect:present()
    --self.chain:present()

    -- if list empty, do nothing
    local filter = self.chain.filters[1]
    if not filter then return end

    local lastfilter = self.chain.filters[#self.chain.filters]
    if not lastfilter then return end

    -- Display last effect's output to screen(bind fbo 0)
    local f = filter.fbo
    --self.chain:draw(self.filt.prog, f.w, f.h, f.tex)

    -- Patch in another shader here
    -- TODO: feed in original tex(self.chain.filters[1].tex)
    -- and blend with processed tex(self.chain.filters[#self.chain.filters)].tex
    local prog = self.filt.prog
    --local prog = filter.prog

    local w,h,srctex = f.w, f.h, f.tex

    gl.glUseProgram(prog)

    gl.glActiveTexture(GL.GL_TEXTURE0)
    gl.glBindTexture(GL.GL_TEXTURE_2D, srctex)
    local tx_loc = gl.glGetUniformLocation(prog, "tex")
    gl.glUniform1i(tx_loc, 0)

    gl.glActiveTexture(GL.GL_TEXTURE1)
    gl.glBindTexture(GL.GL_TEXTURE_2D, lastfilter.fbo.tex)
    local tx2_loc = gl.glGetUniformLocation(prog, "tex2")
    gl.glUniform1i(tx2_loc, 1)

    -- If these uniforms are not present, we get location -1.
    -- Calling glUniform on that location doesn't hurt, apparently...
    local rx_loc = gl.glGetUniformLocation(prog, "ResolutionX")
    gl.glUniform1i(rx_loc, w)
    local ry_loc = gl.glGetUniformLocation(prog, "ResolutionY")
    gl.glUniform1i(ry_loc, h)

    local t_loc = gl.glGetUniformLocation(prog, "time")
    gl.glUniform1f(t_loc, self.chain.time)

    gl.glBindVertexArray(self.chain.vao)
    gl.glDrawArrays(GL.GL_TRIANGLE_FAN, 0, 4)
    gl.glBindVertexArray(0)

    gl.glUseProgram(0)
end

function julia_effect:timestep(absTime, dt)
    self.chain:timestep(absTime, dt)
end

function julia_effect:handleCommand(args)
    if not args then return {'nil args'} end
    if #args == 0 then return {'empty args'} end
    if #args == 1 then return {args[1]..'no args'} end

    local key = args[1]
    local val = args[2]
    if key and val then
        if key == 'sigma' then
            self.chain.uniforms.sigma = tonumber(val)
            return {'Set sigma to '..tostring(self.chain.uniforms.sigma)}
        elseif key == 'lumThresh' then
            self.chain.uniforms.lumThresh = tonumber(val)
            return {'Set lumThresh to '..tostring(self.chain.uniforms.lumThresh)}
        end
    end
end

-- Return a list of strings(lines) describing commands
function julia_effect:commandHelp()
    return {
        'julia_effect help',
        'Commands:',
        '   sigma(blur) <float> '..tostring(self.chain.uniforms.sigma),
        '   lumThresh <float> '..tostring(self.chain.uniforms.lumThresh),
    }
end

return julia_effect
