/*
								+----------------------------------+
								|                                  |
								|  ***  GPU noise shader lib  ***  |
								|                                  |
								|   Copyright © -tHE SWINe- 2008   |
								|                                  |
								|            GPUNoise.h            |
								|                                  |
								+----------------------------------+
*/

#pragma once
#ifndef __NOISE_SHADER_LIBRARY_INCLUDED
#define __NOISE_SHADER_LIBRARY_INCLUDED

/**
 *	@file dev/GPUNoise.h
 *	@date 2008
 *	@author -tHE SWINe-
 *	@brief GPU noise shader lib
 *
 *	@date 2008-08-08
 *
 *	added \#ifdef for windows 64, added \#define for GL_GLEXT_LEGACY (for linux builds)
 *
 *	@date 2012-06-19
 *
 *	Moved multiple inclusion guard before file documentation comment.
 *
 */

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

/*
 *	class CGLNoise
 *		- repository of GLSL noise functions
 */
class CGLNoise {
public:
	/*
	 *	CGLNoise::<unnamed_0>
	 *		- noise function type names
	 */
	enum {
		noise_2D = 0,
		noise_2D_Simplex,
		noise_3D,
		noise_3D_Simplex,
		noise_4D,
		noise_4D_Simplex
	};

protected:
	CGLTexture_2D *m_p_grad, *m_p_perm;
	CGLTexture_1D *m_p_simplex;

	static const uint8_t m_p_perm_table[256];
	static const int8_t m_p_grad3_table[16][3];
	static const int8_t m_p_grad4_table[32][4];
	static const uint8_t m_p_simplex4_table[64][4];

	static const char *m_p_s_noise_func_source_list[6];

public:
	/*
	 *	CGLNoise::CGLNoise()
	 *		- default constructor; has no effect
	 */
	CGLNoise();

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

	/*
	 *	bool CGLNoise::Create_GradTex(CGLState *p_state)
	 *		- creates gradient texture (in case it hasn't been created already)
	 *		- p_state is OpenGL state guard
	 *		- returns true on success, false on failure
	 */
	bool Create_GradTex(CGLState *p_state);

	/*
	 *	bool CGLNoise::Create_PermTex(CGLState *p_state)
	 *		- creates permutation texture (in case it hasn't been created already)
	 *		- p_state is OpenGL state guard
	 *		- returns true on success, false on failure
	 */
	bool Create_PermTex(CGLState *p_state);

	/*
	 *	bool CGLNoise::Create_SimplexTex(CGLState *p_state)
	 *		- creates simplex texture (in case it hasn't been created already)
	 *		- note simplex texture is one-dimensional
	 *		- p_state is OpenGL state guard
	 *		- returns true on success, false on failure
	 */
	bool Create_SimplexTex(CGLState *p_state);

	/*
	 *	bool CGLNoise::b_GradTex_Required(int n_noise_type) const
	 *		- returns true if gradient texture is required for noise function n_noise_type
	 *		  (one of noise_2D, noise_2D_Simplex, noise_3D, noise_3D_Simplex, noise_4D or
	 *		  noise_4D_Simplex) otherwise returns false
	 *		- gradient texture is required for 4D noise (both simplex and regular)
	 *		- note permutation texture is always required
	 */
	bool b_GradTex_Required(int n_noise_type) const;

	/*
	 *	bool CGLNoise::b_SimplexTex_Required(int n_noise_type) const
	 *		- returns true if simplex texture is required for noise function n_noise_type
	 *		  (one of noise_2D, noise_2D_Simplex, noise_3D, noise_3D_Simplex, noise_4D or
	 *		  noise_4D_Simplex) otherwise returns false
	 *		- simplex texture is required for any kind of simplex noise but 2D
	 *		- note permutation texture is always required
	 */
	bool b_SimplexTex_Required(int n_noise_type) const;

	/*
	 *	const char *CGLNoise::p_s_GradTex_SamplerName() const
	 *		- returns name of sampler for gradient texture
	 */
	const char *p_s_GradTex_SamplerName() const;

	/*
	 *	const char *CGLNoise::p_s_PermTex_SamplerName() const
	 *		- returns name of sampler for permutation texture
	 */
	const char *p_s_PermTex_SamplerName() const;

	/*
	 *	const char *CGLNoise::p_s_SimplexTex_SamplerName() const
	 *		- returns name of sampler for simplex texture
	 */
	const char *p_s_SimplexTex_SamplerName() const;

	/*
	 *	const char *CGLNoise::p_s_NoiseShader(int n_noise_type) const
	 *		- returns source code for noise function n_noise_type (one of noise_2D,
	 *		  noise_2D_Simplex, noise_3D, noise_3D_Simplex, noise_4D or noise_4D_Simplex)
	 *		- regular noise is implemented by the functions:
	 *			float noise(vec2 v_point)
	 *			float noise(vec3 v_point)
	 *			float noise(vec4 v_point)
	 * 
	 *		  simplex noise is implemented by the functions:
	 *			float snoise(vec2 v_point)
	 *			float snoise(vec3 v_point)
	 *			float snoise(vec4 v_point)
	 * 
	 *		  author: Stefan Gustavson ITN-LiTH (stegu@itn.liu.se) 2004-12-05
	 *		- note the original source code was reformatted and comments were stripped off
	 *		- note it is possible to include more noise functions in a single shader,
	 *		  shared variables / functions are properly #ifdef-ed
	 */
	const char *p_s_NoiseShader(int n_noise_type) const;

	/*
	 *	bool CGLNoise::IncludeInShader(TShaderInfo &r_shader_info, int n_noise_type)
	 *		- includes noise function of type n_noise_type in shader source r_shader_info
	 *		- returns true on success, false on failure
	 */
	bool IncludeInShader(TShaderInfo &r_shader_info, int n_noise_type)
	{
		if(!r_shader_info.p_high)
			return false;
		// only works with high-level shaders

		if(!r_shader_info.p_high->IncludeSource(p_s_NoiseShader(n_noise_type)) ||
		   (r_shader_info.p_high->n_TexUnit(p_s_PermTex_SamplerName()) == -1 &&
		   r_shader_info.p_high->n_AssignTextureUnit(p_s_PermTex_SamplerName()) == -1))
			return false;
		if(b_GradTex_Required(n_noise_type) &&
		   r_shader_info.p_high->n_TexUnit(p_s_GradTex_SamplerName()) == -1 &&
		   r_shader_info.p_high->n_AssignTextureUnit(p_s_GradTex_SamplerName()) == -1)
			return false;
		if(b_SimplexTex_Required(n_noise_type) &&
		   r_shader_info.p_high->n_TexUnit(p_s_SimplexTex_SamplerName()) == -1 &&
		   r_shader_info.p_high->n_AssignTextureUnit(p_s_SimplexTex_SamplerName()) == -1)
			return false;
		// include noise source code, add samplers if needed

		if(r_shader_info.p_low) {
			delete r_shader_info.p_low;
			r_shader_info.p_low = 0;
		}
		// do not have noise function for low-level program, must delete it

		return true;
	}

	inline void Bind_GradTex(CGLState *p_state)
	{
		m_p_grad->Bind(p_state);
	}

	inline void Bind_Enable_GradTex(CGLState *p_state)
	{
		m_p_grad->Bind_Enable(p_state);
	}

	inline void Bind_PermTex(CGLState *p_state)
	{
		m_p_perm->Bind(p_state);
	}

	inline void Bind_Enable_PermTex(CGLState *p_state)
	{
		m_p_perm->Bind_Enable(p_state);
	}

	inline void Bind_SimplexTex(CGLState *p_state)
	{
		m_p_simplex->Bind(p_state);
	}

	inline void Bind_Enable_SimplexTex(CGLState *p_state)
	{
		m_p_simplex->Bind_Enable(p_state);
	}
};

#endif // __NOISE_SHADER_LIBRARY_INCLUDED
