--
-- pixwerx - bare rendering engine binding usage
--

-- we need everything
pw.script:load_all()

print("executable path:",pw.path.get().executable_path)
print("resource paths:",pw.path.get().resource_paths)

for k,v in pairs(pw.path.get().resource_paths) do
    print(k,v)
end


-- create image
local img = pw.image()

-- initialize with data
if not img:create(pw.size(512,512),pw.pixel_layout.rgb8,nil) then
    print("image couldnt be created")
else

    -- generate some noise
	img:generate_noise()

    -- for debugging
	pw.image_io.get():write("test.png",img,0)

end

-- create a window (remember windows are invisible by default)
local w = pw.window.new()

-- set title
w.title = "pixwerx - bare rendering"

-- set size
w.size = pw.size(640,480)

-- move window
w.position = pw.point(100,100)

-- create a new geometry
local g = pw.geometry()

g.primitive_topology = pw.primitive_topology_type.triangle_list -- meh

-- create texture coordinates
g.texture_coordinates = { {
    { 0.0, 0.0 },
    { 1.0, 1.0 },
    { 1.0, 0.0 },
    { 0.0, 1.0 }
} }

z = 0
s = 1


-- 0 --- 3
-- |  \  |
-- 1 --- 2

-- indices
g.indices = {
    0, 1, 2,    -- triangle #1
    2, 3, 0     -- triangle #2
    }

print(g.indices,#g.indices)


-- geometry can only build with indexed facesets
g.vertices = {
    { -s,-s, z },
    {  s,-s, z },
    {  s, s, z },
    { -s, s, z }
}

g:compute_normals()


local mm = pw.matrix4x4.identity
local mv = pw.matrix4x4()
local mp = pw.matrix4x4()

local s = pw.shader()

s:set_source(pw.shader_type.vertex,[[
#version 400

layout (location = 0) in vec3 vertices;
layout (location = 1) in vec3 normals;
layout (location = 2) in vec2 texture_coords;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;


out vec2 tex_c;

void main() {
    tex_c = texture_coords;
    gl_Position = projection * view * model * vec4(vertices, 1.0);
}
]])

s:set_source(pw.shader_type.fragment,[[
#version 400

uniform vec4 color = vec4(1.0, 0.0, 0.0, 1.0);
uniform sampler2D tex_color;

in vec2 tex_c;

out vec4 frag_color;

void main() {
    frag_color = texture(tex_color,tex_c) * color;
}
]])

-- check if the shader can be compiled and linked
if not s:build() then
    print("Error!")
end

-- the renderer for a geometry
local renderer = pw.renderer()

-- camera position
local cam_pos = pw.vector3(0,0,5)

-- model position
local model_pos = pw.vector3(0,0,0)


-- create new context (should move into a scene later)
local ctx = pw.context()

-- create a texture
local tx = pw.texture()

-- create texture from an image
tx:create(img)

-- unload it - would only make sense for static textures
-- img:release()

-- set function to
w.on_resize = function(self)
    -- use client_size to resize the viewport
    ctx:set_viewport(pw.rectangle(pw.point(0,0),self.client_size:cast_to_float()))
end


-- setup a lua callback function as callback
w.on_update = function(self)

    -- we set the clear color
    ctx.clearcolor = pw.color(pw.mathf.ping_pong(pw.time.now,1.0),0,1,1)
	ctx:clear()

    -- set view matrix with look_at - view matrix is moving the world - hence inverse!
    mv = pw.matrixtransform.look_at(cam_pos,cam_pos + pw.vector3.forward,pw.vector3.up).inverse

    -- compute aspect ratio from canvas size
    aspect_ratio = self.client_size.width / self.client_size.height

    -- create a view frustum for a perspective projection
	mp = pw.matrixtransform.perspective_projection(math.rad(45),aspect_ratio,0.2,100)

    --
	-- this code is raw rendering mode
	--

    -- just some toying around
    local color_red  = pw.mathf.ping_pong(pw.time.now,1.0)
    local color_green = pw.mathf.ping_pong(pw.time.now + 1,1.0)
    local color_blue = 1.0 - pw.mathf.ping_pong(pw.time.now,1.0)
	-- color is currently still a vector4
    local cl = pw.vector4( color_red, color_green, color_blue, 1.0 )


    img:generate_noise()
    tx:update(img)

    -- bind the shader
    s:use()

    -- update the uniforms, currently the slow path
    s:set_uniform_mat4("model",mm)
    s:set_uniform_mat4("view",mv)
    s:set_uniform_mat4("projection",mp)
    s:set_uniform_vec4("color",cl)

    -- specific to our shader
	s:set_uniform_int("tex_color",0) -- unit 0

    -- bind the texture
    tx:bind()

    -- update renderer from geometry
	renderer:update(g)

    -- draw
	renderer:draw()

    local e = ctx:get_error()
    if e ~= 0 then
        print("OpenGL error",e)
    end

end

-- before entering the update loop make the window visible
w.visible = true

-- some fluffyness
local speed = 0

-- main update loop
while w:update() do

    -- only check if get a new input
    if pw.input.get().has_input then


        -- somehow (depending on X11/Wayland) works
        if pw.input.get().input_string == 'f' then
            w.fullscreen = not w.fullscreen
        end

        -- keycode for quit
        if pw.input.get().input_string == 'q' then
            break
        end

        -- just to quickly modify speed
		local move_step = 0.05

        -- camera
        if     pw.input.get().input_string == 'w' then
            cam_pos.z = cam_pos.z - move_step
        elseif pw.input.get().input_string == 's' then
            cam_pos.z = cam_pos.z + move_step
        elseif pw.input.get().input_string == 'a' then
            cam_pos.x = cam_pos.x - move_step
        elseif pw.input.get().input_string == 'd' then
            cam_pos.x = cam_pos.x + move_step
        elseif pw.input.get().input_string == 'z' then
            cam_pos.y = cam_pos.y + move_step
        elseif pw.input.get().input_string == 'x' then
            cam_pos.y = cam_pos.y - move_step
		end

        -- just some debugging
        print(cam_pos.x,cam_pos.y,cam_pos.z)

    end

    -- just to check
    if pw.input:get().mouse_button == 1 then
        print(pw.input:get().mouse_position.x,pw.input:get().mouse_position.y,w.client_size.width,w.client_size.height)
    end

end
