/*
								+----------------------------------+
								|                                  |
								| *** Generic GPU particle sys *** |
								|                                  |
								|   Copyright © -tHE SWINe- 2008   |
								|                                  |
								|          GPUParticles.h          |
								|                                  |
								+----------------------------------+
*/

#pragma once
#ifndef __GENERIC_GPU_PARTICLE_SYSTEM_INCLUDED
#define __GENERIC_GPU_PARTICLE_SYSTEM_INCLUDED

/**
 *	@file dev/GPUParticles.h
 *	@date 2008
 *	@author -tHE SWINe-
 *	@brief generic GPU particle sys
 *
 *	@date 2008-08-08
 *
 *	added \#ifdef for windows 64, added \#define for GL_GLEXT_LEGACY (for linux builds)
 *
 *	@date 2009-05-23
 *
 *	removed all instances of std::vector::reserve and replaced them by stl_ut::Reserve_*
 *
 *	@date 2012-06-19
 *
 *	Moved multiple inclusion guard before file documentation comment.
 *
 */

/*
 *	class CGLParticleSystem
 *		- generic OpenGL particle system wrapper / allocator
 *		- this only manages allocation and automatic deallocation
 *		  of vertex texture pages and vertex buffer objects.
 *		  for function of particle systems, core object is needed,
 *		  it's task is to alloc texture pages and vertex objects,
 *		  generate initial contents, update particles in time, convert
 *		  particles to vertices and finally to render particles
 */
class CGLParticleSystem {
public:
	/*
	 *	class CGLParticleSystem::CCore
	 *		- particle system core object virtual class
	 */
	class CCore {
	public:
		/*
		 *	CGLParticleSystem::CCore::CCore(CGLParticleSystem *p_parent)
		 *		- CCore constructor is supposed to take parent object so it can
		 *		  be later used to easily allocate textures and vertex buffers
		 */
		//CGLParticleSystem::CCore::CCore(CGLParticleSystem *p_parent);
		// to be implemented in inherent class

		/*
		 *	virtual bool CGLParticleSystem::CCore::Alloc_Resources(CGLState *p_state)
		 *		- allocates textures and vbo's required for particle system
		 *		  (it is not required to generate any data, just allocate)
		 *		- in case particle system requires some textures or shaders, it is
		 *		  supposed they are loaded here
		 *		- p_state is OpenGL state guard
		 *		- returns true on success, false on failure
		 *		- note this function should use CGLParticleSystem::p_Alloc_Texture() and
		 *		  CGLParticleSystem::p_Alloc_VertexBuffer() (CCore's constructor should
		 *		  take pointer to CGLParticleSystem parent object and store it inside CCore)
		 */
		virtual bool Alloc_Resources(CGLState *p_state) = 0;

		/*
		 *	virtual bool CGLParticleSystem::CCore::Reset_Textures(CGLState *p_state, float f_seed)
		 *		- fills vertex textures with some initial data
		 *		- f_seed is used to specify random seed (discussion: prefer int or float seed?)
		 *		- note it is not required to update vertex buffers
		 *		- p_state is OpenGL state guard
		 *		- returns true on success, false on failure
		 */
		virtual bool Reset_Textures(CGLState *p_state, float f_seed) = 0;

		/*
		 *	virtual bool CGLParticleSystem::CCore::Update_Textures(CGLState *p_state, float f_dt)
		 *		- updates vertex textures, f_dt is time step
		 *		- note it is not required to update vertex buffers
		 *		- p_state is OpenGL state guard
		 *		- returns true on success, false on failure
		 */
		virtual bool Update_Textures(CGLState *p_state, float f_dt) = 0;

		/*
		 *	virtual bool CGLParticleSystem::CCore::Assemble_Vertices(CGLState *p_state)
		 *		- generates contents of vertex buffers
		 *		- note it is not required to update vertex textures
		 *		- p_state is OpenGL state guard
		 *		- returns true on success, false on failure
		 */
		virtual bool Assemble_Vertices(CGLState *p_state) = 0;

		/*
		 *	virtual bool CGLParticleSystem::CCore::Render_Particles(CGLState *p_state)
		 *		- renders contents of vertex buffers
		 *		- note it is not required to update vertex textures nor buffers
		 *		- p_state is OpenGL state guard
		 *		- returns true on success, false on failure
		 */
		virtual bool Render_Particles(CGLState *p_state) = 0;
	};

protected:
	int m_n_page_width, m_n_page_height;
	std::vector<CGLTexture_2D*> m_texture_list;
	std::vector<CGLVertexBufferObject*> m_vbo_list;
	CCore *m_p_core;
	bool m_b_particles_initialized;
	bool m_b_vertices_up_to_date;

public:
	/*
	 *	CGLParticleSystem::CGLParticleSystem(int n_particle_num)
	 *		- calculates texture sizes to contain n_particle_num particles
	 *		  (chooses nearest greater or equal power of two), otherwise has no effect
	 */
	CGLParticleSystem(int n_particle_num);

	/*
	 *	CGLParticleSystem::~CGLParticleSystem()
	 *		- destroctor; deletes controller and all textures / vertex buffers it allocated
	 */
	~CGLParticleSystem();

	/*
	 *	static bool CGLParticleSystem::b_Supported()
	 *		- returns true if extensions required for general mechanism of GPU
	 *		  particle system are supported (in particular those are vertex and
	 *		  pixel buffer objects and floating-point textures for vertices),
	 *		  otherwise returns false
	 */
	static bool b_Supported();

	/*
	 *	bool CGLParticleSystem::Set_Controller(CGLState *p_state, CCore *p_controller)
	 *		- sets controller p_controller and makes it allocate textures and vertex objects
	 *		- p_state is OpenGL state guard
	 *		- returns true on success, false on failure
	 *		- note p_controller is deleted by CGLParticleSystem destructor (or immediately,
	 *		  in case it fails to initialize)
	 *		- note in case of multiple calls to this function, previously specified controllers
	 *		  are deleted along with all resources they might allocate
	 */
	bool Set_Controller(CGLState *p_state, CCore *p_controller);

	/*
	 *	bool CGLParticleSystem::b_Status() const
	 *		- returns true if particle system is ready, otherwise returns false
	 */
	bool b_Status() const;

	/*
	 *	int CGLParticleSystem::n_Particle_Num() const
	 *		- returns real number of particles
	 *		- note this is power of two, greater or equal to number passed to constructor
	 */
	int n_Particle_Num() const;

	/*
	 *	CCore *CGLParticleSystem::p_Controller()
	 *		- returns pointer to particle system controller or 0 in case
	 *		  none has been successfully assigned
	 */
	CCore *p_Controller();

	/*
	 *	const CCore *CGLParticleSystem::p_Controller() const
	 *		- returns const pointer to particle system controller or 0 in case
	 *		  none has been successfully assigned
	 */
	const CCore *p_Controller() const;

	/*
	 *	int CGLParticleSystem::n_VertexTexture_Width() const
	 *		- returns width of vertex texture
	 *		- note each pixel of vertex texture holds (part of) data of a single particle
	 */
	int n_VertexTexture_Width() const;

	/*
	 *	int CGLParticleSystem::n_VertexTexture_Height() const
	 *		- returns height of vertex texture
	 *		- note each pixel of vertex texture holds (part of) data of a single particle
	 */
	int n_VertexTexture_Height() const;

	/*
	 *	CGLTexture_2D *CGLParticleSystem::p_Alloc_Texture(CGLState *p_state,
	 *		GLenum n_internal_format = GL_RGB16F_ARB, int n_width = -1, int n_height = -1)
	 *		- allocates 2D texture with format n_internal_format and resolution
	 *		  n_width per n_height
	 *		- in case n_width or n_height is invalid (negative), corresponding
	 *		  vertex texture dimension is used instead
	 *		- texture wrap mode is set to GL_CLAMP and filtering is set to GL_NEAREST
	 *		  (no mip-maps are generated)
	 *		- returns new texture (valid) or 0 on error
	 */
	CGLTexture_2D *p_Alloc_Texture(CGLState *p_state,
		GLenum n_internal_format = GL_RGB16F_ARB, int n_width = -1, int n_height = -1);

	/*
	 *	CGLVertexBufferObject *CGLParticleSystem::p_Alloc_VertexBuffer(CGLState *p_state,
	 *		int n_particle_vertices_size, GLenum n_usage = GL_STATIC_DRAW_ARB)
	 *		- allocates a new vertex buffer for all particles
	 *		- n_particle_vertices_size is size of all vertices used for a single particles
	 *		  (for example it might be 3 * sizeof(float) for point sprites (vertex per particle)
	 *		  or 4 * 3 * sizeof(float) for screen-aligned quads (four vertices per particle))
	 *		- returns new vertex buffer object (valid) or 0 on error
	 */
	CGLVertexBufferObject *p_Alloc_VertexBuffer(CGLState *p_state,
		int n_particle_vertices_size, GLenum n_usage = GL_STATIC_DRAW_ARB);

	/*
	 *	bool CGLParticleSystem::Reset(CGLState *p_state, float f_seed)
	 *		- resets particle system (puts particles on their initial positions / etc)
	 *		- f_seed is used to specify random seed (discussion: prefer int or float seed?)
	 *		- p_state is OpenGL state guard
	 *		- returns true on success, false on failure
	 */
	bool Reset(CGLState *p_state, float f_seed);

	/*
	 *	bool CGLParticleSystem::Update(CGLState *p_state, float f_dt)
	 *		- updates particle system; note in case it is not initialized,
	 *		  calls Reset() with default seed 0
	 *		- f_dt is delta-time step
	 *		- p_state is OpenGL state guard
	 *		- returns true on success, false on failure
	 */
	bool Update(CGLState *p_state, float f_dt);

	/*
	 *	bool CGLParticleSystem::Render(CGLState *p_state)
	 *		- renders particle system; note in case it is not initialized,
	 *		  calls Reset() with default seed 0
	 *		- p_state is OpenGL state guard
	 *		- returns true on success, false on failure
	 */
	bool Render(CGLState *p_state);

	/*
	 *	static void CGLParticleSystem::Copy_TextureToVBO(CGLState *p_state,
	 *		CGLTexture_2D *p_texture, CGLVertexBufferObject *p_vbo,
	 *		GLenum n_pixel_format = GL_RGB, GLenum n_data_type = GL_FLOAT, int n_offset = 0)
	 *		- utility function for copying texture p_texture to VBO p_vbo
	 *		- n_pixel_format is pixel format (not texture internal) for vertices
	 *		  (use GL_RGB for 3D vertices), n_data_type is vertex component data type
	 *		- p_state is OpenGL state guard
	 *		- note copying is done inside server address space and therefore is very fast
	 */
	static void Copy_TextureToVBO(CGLState *p_state, CGLTexture_2D *p_texture,
		CGLVertexBufferObject *p_vbo, GLenum n_pixel_format = GL_RGB,
		GLenum n_data_type = GL_FLOAT, int n_offset = 0);

protected:
	static int n_ceil_log2(unsigned int n_x);
	void _Destroy();
	static inline void DeleteTexture(CGLTexture_2D *p_texture);
	static inline void DeleteVertexBuffer(CGLVertexBufferObject *p_vbo);
};

#endif // __GENERIC_GPU_PARTICLE_SYSTEM_INCLUDED
