/*
								+----------------------------------+
								|                                  |
								|  ***  GPU image processing  ***  |
								|                                  |
								|   Copyright © -tHE SWINe- 2008   |
								|                                  |
								|           GPUImgProc.h           |
								|                                  |
								+----------------------------------+
*/

#pragma once
#ifndef __GPU_IMAGE_PROCESSING_INCLUDED
#define __GPU_IMAGE_PROCESSING_INCLUDED

/**
 *	@file dev/GPUImgProc.h
 *	@date 2008
 *	@author -tHE SWINe-
 *	@brief GPU image processing
 *
 *	@date 2008-08-03
 *
 *	renamed from ImageProc.h to GPUImgProc.h for the sake of consistency
 *
 *	@date 2008-08-08
 *
 *	added \#ifdef for windows 64, added \#define for GL_GLEXT_LEGACY (for linux builds)
 *
 *	@date 2008-08-09
 *
 *	added void CGLImageProc::DisableProgrammableShading(),
 *	CGLImageProc::DisableFixedVertexProcessing(), and
 *	CGLImageProc::DisableFixedFragmentProcessing(). those can be used to disable unwanted
 *	operations on outputs of vertex / fragment programs
 *
 *	added IdentityMatrices() and FullscreenQuad(). those are pretty common
 *	functions in GPU image processing.
 *
 *	added GPUImgProc.inl
 *
 *	@date 2009-05-23
 *
 *	removed all instances of std::vector::reserve and replaced them by stl_ut::Reserve_*
 *
 *	changed format of message, printed on FBO allocation (GPU_IMGPROC_REPORT_ALLOCATING_FBO)
 *
 *	@date 2009-10-20
 *
 *	fixed some warnings when compiling under VC 2005, implemented "Security
 *	Enhancements in the CRT" for VC 2008. compare against MyProjects_2009-10-19_
 *
 *	@date 2012-06-19
 *
 *	Moved multiple inclusion guard before file documentation comment.
 *
 */

//#define GPU_IMGPROC_REPORT_COMPILING_SHADER
/*
 *	- if defined, everytime shader needs to be compiled
 *	  (not re-used from the pool), it's name and md5 is printed to stdout
 */

#define GPU_IMGPROC_REPORT_COMPILER_MESSAGES
/*
 *	- if defined, errors and warnings generated while compiling shaders are printed to stderr
 */

//#define GPU_IMGPROC_REPORT_ALLOCATING_FBO
/*
 *	- if defined, everytime a new FBO is allocated, message is printed to stdout
 */

#include "../Vector.h"
#include "../ShaderSys.h"
#include "../Texture.h"
#include "../RenderBuffer2.h"

/*
 *	class CGLImageProc
 *		- GPU image processing manager object; serves as render-buffer pool and shader pool
 *		- at least one instance must exist while using any of classes below
 */
class CGLImageProc {
protected:
	struct TLockableFBO {
		CGLFrameBuffer_FBO *p_fbo;
		bool b_locked;

		int n_significant;
		// number of framebuffers, really allocated in GPU memory for FBO
		// framebuffers size in bytes can be approximated as n_significant * 4 bytes
		// for common pixel format combinations, such as GL_RGBA8 and GL_DEPTH_COMPONENT32
		// (note GL_RGB8 and GL_DEPTH_COMPONENT24 would be presumably aligned to 4 byte
		// boundaries as well; problems would arise only with stencil (1 byte), but
		// there is currently no implementation, supporting it (one need to create texture
		// with mixed depth-stencil format instead - and then it's not allocated as
		// framebuffer so it doesn't cause trouble))

		TLockableFBO(CGLFrameBuffer_FBO *_p_fbo, bool _b_locked, int _n_significant)
			:p_fbo(_p_fbo), b_locked(_b_locked), n_significant(_n_significant)
		{}
	};

	class CFindBuffer;

	struct TShaderInstance {
		//std::string s_source; // filenames - not used right now. todo?
		std::string s_id; // name and md5 for every processor

		CGLShaderObject *p_shader;
		int n_reference_num;

		TShaderInstance(CGLShaderObject *_p_shader)
			:p_shader(_p_shader), n_reference_num(1)
		{}

		inline bool operator ==(CGLShaderObject *_p_shader) const
		{
			return _p_shader == p_shader;
		}
	};
	//class CFindShaderBySource; // not used right now
	class CFindShaderById;

	static int m_n_instance_num;
	static std::vector<TLockableFBO> m_frame_buffer_pool;
	static std::vector<TShaderInstance> m_shader_pool;

public:
	/*
	 *	CGLImageProc::CGLImageProc()
	 *		- default constructor
	 */
	CGLImageProc();

	/*
	 *	CGLImageProc::~CGLImageProc()
	 *		- destructor
	 *		- takes care of cleanup once instance counter reaches zero
	 */
	~CGLImageProc();

	/*
	 *	static inline void CGLImageProc::DisableProgrammableShading(CGLState *p_state)
	 *		- utility function; disables programmable shading
	 *		  (vertex programs, fragment programs and shader objects)
	 *		- p_state is OpenGL state guard
	 */
	static inline void DisableProgrammableShading(CGLState *p_state);

	/*
	 *	static inline void CGLImageProc::DisableFixedVertexProcessing(CGLState *p_state)
	 *		- utility function; disables fixed vertex processing (backface culling
	 *		  and user-defined clipping)
	 *		- p_state is OpenGL state guard
	 */
	static inline void DisableFixedVertexProcessing(CGLState *p_state);

	/*
	 *	static inline void CGLImageProc::DisableFixedFragmentProcessing(CGLState *p_state,
	 *		bool b_disable_stipple = false, bool b_disable_scissor = false,
	 *		bool b_disable_dither = false, bool b_disable_color_logic_op = false,
	 *		bool b_disable_index_logic_op = false)
	 *		- utility function; disables fixed fragment processing (polygon stipple (only if
	 *		  b_disable_stipple is true), scissor test (only if b_disable_scissor is true),
	 *		  alpha test, stencil test, depth test, blending, dithering (only if b_disable_dither
	 *		  is true), color logical operation and index logical operation (only if
	 *		  b_disable_color_logic_op or b_disable_index_logic_op is true, respectively)
	 *		  and resets color write mask)
	 *		- p_state is OpenGL state guard
	 */
	static inline void DisableFixedFragmentProcessing(CGLState *p_state,
		bool b_disable_scissor = false, bool b_disable_stipple = false,
		bool b_disable_dither = false, bool b_disable_color_logic_op = false,
		bool b_disable_index_logic_op = false);

	/*
	 *	static inline void CGLImageProc::IdentityMatrices()
	 *		- sets modelview and projection matrices to identity
	 *		- does not modify texture matrices
	 *		- always ends with GL_MODELVIEW matrix mode
	 */
	static inline void IdentityMatrices();

	/*
	 *	static inline void CGLImageProc::IdentityMatrices(CGLState *p_state,
	 *		int n_set_identity_texture = 1)
	 *		- sets modelview, projection and texture matrices to identity
	 *		- n_set_identity_texture is number of texture units (starting with 0)
	 *		  to set identity matrix in
	 *		- always ends with GL_MODELVIEW matrix mode
	 *		- if n_set_identity_texture is positive number, ends with active texture unit 0
	 */
	static inline void IdentityMatrices(CGLState *p_state, int n_set_identity_texture = 1);

	/*
	 *	static inline void CGLImageProc::FullscreenQuad(int n_texture_coord_num = 1,
	 *		bool b_use_fullscreen_tri = false)
	 *		- renders fullscreen quad
	 *		- n_texture_coord_num is number of texture coordinates (for multitexturing);
	 *		  texture coordinates (2D) are: (0, 1), (0, 0), (1, 0), (1, 1)
	 *		- if b_use_fullscreen_tri is true, uses fullscreen triangle rather than
	 *		  fullscreen quad, that might prove faster on some renderers since three
	 *		  vertices are specified only (but more clipping needs to be done)
	 *		- note modelview and projection matrices needs to be identity for this to
	 *		  work correctly
	 *		- note the primitive is drawn in immediate mode, use of display list is advised
	 *		  (that might be hidden inside this function in future versions)
	 */
	static inline void FullscreenQuad(int n_texture_coord_num = 1,
		bool b_use_fullscreen_tri = false);

	/*
	 *	static CGLFrameBuffer_FBO *CGLImageProc::p_GetRenderBuffer(int n_width, int n_height,
	 *		int n_color_buffer_num, bool b_color_texture_target, GLenum n_color_internal_format,
	 *		bool b_depth_buffer, bool b_depth_texture_target, GLenum n_depth_internal_format,
	 *		bool b_stencil_buffer, bool b_stencil_texture_target, GLenum n_stencil_internal_format,
	 *		bool b_lock = false)
	 *
	 */
	static CGLFrameBuffer_FBO *p_GetRenderBuffer(int n_width, int n_height,
		int n_color_buffer_num, bool b_color_texture_target, GLenum n_color_internal_format,
		bool b_depth_buffer, bool b_depth_texture_target, GLenum n_depth_internal_format,
		bool b_stencil_buffer, bool b_stencil_texture_target, GLenum n_stencil_internal_format,
		bool b_lock = false);

	/*
	 *	static void CGLImageProc::UnlockRenderBuffer(CGLFrameBuffer_FBO *p_buffer)
	 *		- returns framebuffer which was requested locked
	 *		- silently ignores framebuffers which do not exist in the pool
	 */
	static void UnlockRenderBuffer(CGLFrameBuffer_FBO *p_buffer);

	/*
	 *	static inline CGLShaderObject *CGLImageProc::p_CompileLinkReport(CGLState *p_state,
	 *		const TShaderInfo *p_vs, const TShaderInfo *p_fs, bool b_exclusive)
	 *		- compiles and links shaders p_vs and p_fs (one of them can be 0)
	 *		  and returns new CGLShaderObject
	 *		- setting b_exclusive to true disables this shader from being stored in the pool,
	 *		  caller is responsible for deleting shader once it's no longer needed
	 *		- reports any errors / warnings to stderr
	 *		- if shader couldn't be compiled returns 0
	 */
	static inline CGLShaderObject *p_CompileLinkReport(CGLState *p_state,
		const TShaderInfo *p_vs, const TShaderInfo *p_fs, bool b_exclusive);

	/*
	 *	static CGLShaderObject *CGLImageProc::p_CompileLinkReport(CGLState *p_state,
	 *		const TShaderInfo *p_vs, const TShaderInfo *p_gs,
	 *		const TShaderInfo *p_fs, bool b_exclusive)
	 *		- compiles and links shaders p_vs, p_gs and p_fs (one or two of them can be 0)
	 *		  and returns new CGLShaderObject
	 *		- setting b_exclusive to true disables this shader from being stored in the pool,
	 *		  caller is responsible for deleting shader once it's no longer needed
	 *		- reports any errors / warnings to stderr
	 *		- if shader couldn't be compiled returns 0
	 */
	static CGLShaderObject *p_CompileLinkReport(CGLState *p_state,
		const TShaderInfo *p_vs, const TShaderInfo *p_gs,
		const TShaderInfo *p_fs, bool b_exclusive);

	/*
	 *	static void CGLImageProc::ReturnShaderObject(CGLShaderObject *p_shader)
	 *		- decrements reference counter for shader p_shader,
	 *		  once it reaches zero shader is deleted
	 *		- if p_shader is not found in the pool it is deleted right away
	 */
	static void ReturnShaderObject(CGLShaderObject *p_shader);

protected:
	static inline void DeleteRenderBuffer(TLockableFBO &t_buffer);
	static inline void DeleteShader(TShaderInstance &t_shader);
	static bool Identify_ShaderSource(std::string &r_s_id, const TShaderInfo *p_shader);
	static bool Identify_ShaderSources(std::string &r_s_id_all,
		const TShaderInfo *p_vs, const TShaderInfo *p_gs, const TShaderInfo *p_fs);
	static bool Generate_SourceString(std::string &r_s_source,
		const char *p_s_vertex_filename, const char *p_s_geometry_filename,
		const char *p_s_fragment_filename);
};

#include "GPUImgProc.inl"
// inlined functions source

/*
 *	class CGLPerlinNoise
 *		- 2D perlin noise generator on GPU
 *		- original code written by Stefan Gustavson
 */
class CGLPerlinNoise {
protected:
	static const unsigned char m_p_perm[256];
	static const int m_p_grad3[16][3]; // permutation tables

	CGLTexture_2D *m_p_noise_perm_tex;
	CGLShaderObject *m_p_noise_shader;

	CShaderVectorParameterRef<float> m_t_scale, m_t_turbulence, m_t_amplitude;
	CShaderVectorParameterRef<int> m_t_octave_num;

public:
	/*
	 *	CGLPerlinNoise::CGLPerlinNoise()
	 *		- default constructor; does nothing
	 */
	CGLPerlinNoise();

	/*
	 *	CGLPerlinNoise::~CGLPerlinNoise()
	 *		- default destructor
	 */
	~CGLPerlinNoise();

	/*
	 *	bool CGLPerlinNoise::Render_Noise(CGLState *p_state, CGLTexture_2D *p_dest,
	 *		Vector2f v_phase, float f_scale, float f_amplitude, int n_octave_num,
	 *		float f_persistence)
	 *		- renders turbulence noise to 2D texture p_dest (RGBA are all set to noise value)
	 *		- p_state is noise phase, f_scale is noise size, f_amplitude is amplitude,
	 *		  n_octave_num is number of noise octaves and f_persistence is octave
	 *		  amplitude exponent
	 *		- returns true on success, false on failure
	 */
	bool Render_Noise(CGLState *p_state, CGLTexture_2D *p_dest, Vector2f v_phase, float f_scale,
		float f_amplitude, int n_octave_num, float f_persistence);

protected:
	static unsigned char *p_PermTextureData();
	static CGLTexture_2D *p_MakePermTexture(CGLState *p_state);
};

/*
 *	class CGLGaussFilter
 *		- simple separable gaussian blur filter
 *		- kernel is calculated on GPU, it's not precalculated in texture
 */
class CGLGaussFilter {
protected:
	CGLShaderObject *m_p_gauss_shader;

	CShaderVectorParameterRef<float> m_t_org_off, m_t_scale;
	CShaderVectorParameterRef<int> m_t_kernel_size;

	int m_n_radius;
	float m_f_kernel_rescale; // kernel scaling factor cache

public:
	/*
	 *	CGLGaussFilter::CGLGaussFilter()
	 *		- default constructor
	 *		- doesn't do anything
	 */
	CGLGaussFilter();

	/*
	 *	CGLGaussFilter::~CGLGaussFilter()
	 *		- destructor
	 */
	~CGLGaussFilter();

	/*
	 *	bool CGLGaussFilter::Filter(CGLState *p_state, CGLTexture_2D *p_dest,
	 *		CGLTexture_2D *p_temp_tex, CGLTexture_2D *p_src, int n_radius)
	 *		- applies separable gaussian filter with radius n_radius
	 *		- p_src is source image, p_temp_tex is temporary texture and p_dest
	 *		  is destination texture. note all three should have the same resolution.
	 *		- p_state is OpenGL state guard
	 *		- returns true on success, false on failure
	 */
	bool Filter(CGLState *p_state, CGLTexture_2D *p_dest,
		CGLTexture_2D *p_temp_tex, CGLTexture_2D *p_src, int n_radius);
};

/*
 *	class CGLMinMaxFilter
 *		- somewhat nicer separable minmax filter
 *		- finds minima / maxima under cross-shaped kernel
 *		  (cross shape is supposed to be better than ordinary square as
 *		  it is similar to diamond / circle with low radii)
 *		- note this needs support for four draw buffers
 */
class CGLMinMaxFilter {
protected:
	CGLShaderObject *m_p_minmax_shader1, *m_p_minmax_shader2;

	CShaderVectorParameterRef<float> m_t_right_off_center, m_t_off_left_off_right;
	CShaderVectorParameterRef<int> m_t_kernel_size_side_size1; // pass 1 params

	CShaderVectorParameterRef<float> m_t_up_off_center, m_t_off_top_off_bottom;
	CShaderVectorParameterRef<int> m_t_kernel_size_side_size2; // pass 2 params

public:
	/*
	 *	CGLMinMaxFilter::CGLMinMaxFilter()
	 *		- default constructor; does nothing
	 */
	CGLMinMaxFilter();

	/*
	 *	CGLMinMaxFilter::~CGLMinMaxFilter()
	 *		- destructor
	 */
	~CGLMinMaxFilter();

	/*
	 *	bool CGLMinMaxFilter::Filter(CGLState *p_state, CGLTexture_2D *p_dest_min,
	 *		CGLTexture_2D *p_dest_max, CGLTexture_2D *p_temp_tex[4], CGLTexture_2D *p_src,
	 *		int n_radius)
	 *		- finds minimal and maximal pixel values in p_src image under cross-shaped
	 *		  kernel with radius n_radius
	 *		- p_dest_min is filled with minima and p_dest_max is filled with maxima
	 *		- p_temp_tex is array of four temporary textures
	 *		- p_state is OpenGL state guard
	 *		- note all textures must have the same dimensions and should have the same format
	 *		- returns true on success, false on failure
	 */
	bool Filter(CGLState *p_state, CGLTexture_2D *p_dest_min, CGLTexture_2D *p_dest_max,
		CGLTexture_2D *p_temp_tex[4], CGLTexture_2D *p_src, int n_radius);
};

#endif // __GPU_IMAGE_PROCESSING_INCLUDED
