/* class demo01
   mostly for testing individual things
*/

#pragma once

#include <memory>

#include "demo_base.h"
#include "common.hpp"
#include "shader.hpp"
#include "tga.h"
#include "transformation_node.h"
#include "geometry_node.h"
#include "object_node.h"
#include "model.h"
#include "frustrum.h"


const int num_cubes = 5; //number of cubes for postprocessing test

class demo01 : public demo_base
{
public:
	demo01(void);
	virtual ~demo01(void);

	virtual bool init();

protected:
	enum tex_indices {BG, TREE, CHIMNEY, BRICK, NUM_TEXTURES};
	glm::mat4 view_matrix; //matrix that only contains view/camera transformations
	//object handles
	GLuint vbo_id[3], vao_id;
	GLuint bg_vbo_id[4], bg_vao_id;
	GLuint cube_vbo_id[3], cube_vao_id;
	GLuint atoff_vbo_id[3], atoff_vao_id;
	//GLuint model_vbo_id[3], model_vao_id;

	//models
	Model object_model, chimney_model, teapot_model;

	GLuint texture[NUM_TEXTURES];

	//available shaders
	std::auto_ptr<Shader> minimal;
	std::auto_ptr<Shader> sandbox;
	std::auto_ptr<Shader> texture_shader;
	std::auto_ptr<Shader> smooth;
	std::auto_ptr<Shader> toon;
	std::auto_ptr<Shader> phong;
	std::auto_ptr<Shader> texture_cm;
	std::auto_ptr<Shader> gauss_vertical;
	std::auto_ptr<Shader> gauss_horizontal;
	std::auto_ptr<Shader> shadowmap_gen, pp_dof;
	
	//light
	glm::vec3 light_0, transformed_light_0;

	//scenegraph
	scene_node root;
	transformation_node triangle_pos, atoff_pos, chimney_pos;
	geometry_node triangle, atoff2, triangle2;
	object_node atoff, bg, model, chimney, scene_quad;


	//skybox
	GLuint skybox_texture;
	GLuint skybox_vao, skybox_vbo[3];

	//post-processing tests
	GLuint fbo_id[3]; //fbo for drawing the scene
	GLuint scene_tex_id[3], depth_tex_id[3], depth_buffer[3]; //texture ids for scene and depth textures, and renderbuffer for depth
	GLuint pp_vao_id, pp_vbo[3];
	GLfloat pp_border; //screenspace border for post-processing
	transformation_node cube_node[num_cubes*num_cubes];
	GLuint shadow_tex_id, shadow_fbo; //ids for depth only fbo/texture
	
	//frustrum culling
	frustrum view_frustrum;
	int number_culled;
	bool in_frustrum(const scene_node& node) const;

	virtual void draw();	//main drawing method
	virtual void cleanup();

	void release_vbo_vao(GLuint *vbo_id, GLuint *vao_id, int numbuffers = 2);
	void init_vbo_vao(const Shader &shader, GLuint *vbo_id, GLuint *vao_id);
	void init_bg(const Shader &shader, GLuint *vbo_id, GLuint *vao_id);
	int init_cube(const Shader &shader, GLuint *vbo_id, GLuint *vao_id);
	void init_model(const Shader &shader);
	void draw_vao (const Shader &shader, GLuint vao_id,  int vertices, GLenum draw_type = GL_TRIANGLES);
	//load textures
	bool loadGLTextures();
	virtual void process_keys();
	void render_node(scene_node& node);
	//skybox
	bool init_skybox();
	void draw_skybox();
	//fbo setup
	bool init_fbo();
	//post-processing test
	void init_postprocess();
	void draw_postprocess(const Shader& shader, GLuint source_tex_id, GLuint z_tex_id);
	void render_shadowmap(scene_node& node);
};