/*  ZZ Open GL graphics plugin
 *  Copyright (c)2009-2010 zeydlitz@gmail.com, arcum42@gmail.com
 *  Based on Zerofrog's ZeroGS KOSMOS (c)2005-2008
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 */

#ifndef __ZEROGS_SHADERS_H__
#define __ZEROGS_SHADERS_H__

// -- Not very important things, but we keep it to enumerate shader
#define NUM_FILTERS 2 		// texture filtering
#define NUM_TYPES 5 		// types of texture read modes
#define NUM_TEXWRAPS 4 		// texture wrapping
#define NUM_SHADERS (NUM_FILTERS*NUM_TYPES*NUM_TEXWRAPS*32) // # shaders for a given ps

// Just bitmask for different type of shaders
#define SHADER_REDUCED 1	// equivalent to ps2.0
#define SHADER_ACCURATE 2   	// for older cards with less accurate math (ps2.x+)

#include <math.h>
#include "ZZoglMath.h"
#include "GS.h"

// By default enable nvidia cg api
#if !defined(GLSL_API) && !defined(NVIDIA_CG_API)
#define NVIDIA_CG_API
#endif
// --------------------------- API abstraction level --------------------------------

#ifdef NVIDIA_CG_API				// Code for NVIDIA cg-toolkit API

#include <Cg/cg.h>
#include <Cg/cgGL.h>
#define ZZshProgram 		CGprogram
#define ZZshShader 		CGprogram
#define ZZshShaderLink		CGprogram
#define ZZshParameter 		CGparameter
#define ZZshContext		CGcontext
#define ZZshProfile		CGprofile
#define ZZshError		CGerror
#define pZero			0			// Zero parameter
#define sZero			0			// Zero program

#define SAFE_RELEASE_PROG(x) 	{ if( (x) != NULL ) { cgDestroyProgram(x); x = NULL; } }
inline bool ZZshActiveParameter(ZZshParameter param) {return (param !=NULL); }

#endif					// end NVIDIA cg-toolkit API

#ifdef GLSL4_API
#include "GSUniformBufferOGL.h"
#include "GSVertexArrayOGL.h"
#endif

// GLSL only
// Set it to 0 to diable context usage, 1 -- to enable. FFX-1 have a strange issue with ClampExt.
#define NOCONTEXT		0

#ifdef GLSL_API

enum ZZshPARAMTYPE {
	ZZ_UNDEFINED,
	ZZ_TEXTURE_2D,
	ZZ_TEXTURE_RECT,
	ZZ_TEXTURE_3D,
	ZZ_FLOAT4,
};

typedef struct {
	const char* 	ShName;		// Name of uniform
	ZZshPARAMTYPE	type;		// Choose between parameter type

	float 		fvalue[4];
	GLuint		sampler;	// Number of texture unit in array 
	GLint		texid;		// Number of texture - texid. 

	bool		Constant;	// Uniform could be constants, does not change at program flow
	bool 		Settled;	// Check if Uniform value was set.
} ZZshParamInfo;

typedef struct {
	void*	 	link;
	bool		isFragment;
} ZZshShaderLink; 

#define ZZshProgram 		GLuint
#define ZZshShader 		GLuint
#define ZZshParameter 		GLint
#define ZZshContext		int
#define ZZshProfile		int
#define ZZshError		int
#define ZZshIndex		GLuint

const ZZshParamInfo  qZero = {ShName:"", type:ZZ_UNDEFINED, fvalue:{0}, sampler: -1, texid: 0, Constant: false, Settled: false};

#define pZero			0

const ZZshShaderLink sZero = {link: NULL, isFragment: false};

inline bool ZZshActiveParameter(ZZshParameter param) {return (param > -1); }
#ifndef GLSL4_API
#define SAFE_RELEASE_PROG(x) 	{ /*don't know what to do*/ }
#endif

// ---------------------------

#endif

extern float4 g_vdepth;
extern float4 vlogz;


#ifdef GLSL4_API
enum {
	ZZSH_CTX_0   = 0,
	ZZSH_CTX_1   = 1,
	ZZSH_CTX_ALL = 2
};

// Note A nice template could be better
// Warning order is important for buffer (see GLSL)
// Note must be keep POD (so you can map it to GLSL)
struct GlobalUniform {
	union {
		struct {
			// VS
			float g_fPosXY[4]; // dual context
			// PS
			float g_fFogColor[4];
		};
		float linear[2*4];
	};
	void SettleFloat(uint indice, const float* v) {
		assert(indice + 3 < 2*4);
		linear[indice+0] = v[0];
		linear[indice+1] = v[1];
		linear[indice+2] = v[2];
		linear[indice+3] = v[3];
	}
};
struct ConstantUniform {
	union {
		struct {
			// Both VS/PS
			float g_fBilinear[4];
			float g_fZbias[4];
			float g_fc0[4];
			float g_fMult[4];
			// VS
			float g_fZ[4];
			float g_fZMin[4];
			float g_fZNorm[4];
			// PS
			float g_fExactColor[4];
		};
		float linear[8*4];
	};
	void SettleFloat(uint indice, const float* v) {
		assert(indice + 3 < 8*4);
		linear[indice+0] = v[0];
		linear[indice+1] = v[1];
		linear[indice+2] = v[2];
		linear[indice+3] = v[3];
	}
};
struct FragmentUniform {
	union {
		struct {
			float g_fTexAlpha2[4]; // dual context
			float g_fTexOffset[4]; // dual context
			float g_fTexDims[4]; // dual context
			float g_fTexBlock[4]; // dual context
			float g_fClampExts[4]; // dual context
			float g_fTexWrapMode[4]; // dual context
			float g_fRealTexDims[4]; // dual context
			float g_fTestBlack[4]; // dual context
			float g_fPageOffset[4]; // dual context
			float g_fTexAlpha[4]; // dual context
			float g_fInvTexDims[4];
			float g_fBitBltZ[4];
			float g_fOneColor[4];
		};
		float linear[13*4];
	};
	void SettleFloat(uint indice, const float* v) {
		assert(indice + 3 < 13*4);
		linear[indice+0] = v[0];
		linear[indice+1] = v[1];
		linear[indice+2] = v[2];
		linear[indice+3] = v[3];
	}
};
struct VertexUniform {
	union {
		struct {
			float g_fBitBltPos[4];
			float g_fBitBltTex[4];
			float g_fBitBltTrans[4];
		};
		float linear[3*4];
	};
	void SettleFloat(uint indice, const float* v) {
		assert(indice + 3 <  3*4);
		linear[indice+0] = v[0];
		linear[indice+1] = v[1];
		linear[indice+2] = v[2];
		linear[indice+3] = v[3];
	}
};
#endif



//const static char* g_pPsTexWrap[] = { "-DREPEAT", "-DCLAMP", "-DREGION_REPEAT", NULL };

enum ZZshShaderType {ZZ_SH_ZERO, ZZ_SH_REGULAR, ZZ_SH_REGULAR_FOG, ZZ_SH_TEXTURE, ZZ_SH_TEXTURE_FOG, ZZ_SH_CRTC, ZZ_SH_NONE};
// We have "compatible" shaders, as RegularFogVS and RegularFogPS. if don't need to wory about incompatible shaders
// It used only in GLSL mode. 

// ------------------------- Variables -------------------------------

extern int 		g_nPixelShaderVer;
extern ZZshShaderLink 	pvs[16], g_vsprog, g_psprog;
extern ZZshParameter 	g_vparamPosXY[2], g_fparamFogColor;

#define MAX_ACTIVE_UNIFORMS 600
#define MAX_ACTIVE_SHADERS 400

#ifndef GLSL4_API
struct FRAGMENTSHADER
{
	FRAGMENTSHADER() : prog(sZero), Shader(0), sMemory(pZero), sFinal(pZero), sBitwiseANDX(pZero), sBitwiseANDY(pZero), sInterlace(pZero), sCLUT(pZero), sOneColor(pZero), sBitBltZ(pZero),
		fTexAlpha2(pZero), fTexOffset(pZero), fTexDims(pZero), fTexBlock(pZero), fClampExts(pZero), fTexWrapMode(pZero),
		fRealTexDims(pZero), fTestBlack(pZero), fPageOffset(pZero), fTexAlpha(pZero)  {}
	
	ZZshShaderLink prog;						// it link to FRAGMENTSHADER structure, for compability between GLSL and CG
	ZZshShader Shader;						// GLSL store shader's not as ready programs, but as shaders compilated object. VS and PS should be linked together to
									// made a program.
	ZZshShaderType ShaderType;					// Not every PS and VS are used together, only compatible ones.

	ZZshParameter sMemory, sFinal, sBitwiseANDX, sBitwiseANDY, sInterlace, sCLUT;
	ZZshParameter sOneColor, sBitBltZ, sInvTexDims;
	ZZshParameter fTexAlpha2, fTexOffset, fTexDims, fTexBlock, fClampExts, fTexWrapMode, fRealTexDims, fTestBlack, fPageOffset, fTexAlpha;

	int ParametersStart, ParametersFinish;				// this is part of UniformsIndex array in which parameters of this shader stored. Last one is ParametersFinish-1

#ifdef _DEBUG
	string filename;
#endif

#ifdef NVIDIA_CG_API
	void set_uniform_param(ZZshParameter &var, const char *name)
	{
		ZZshParameter p;
		p = cgGetNamedParameter(prog, name);

		if (p != NULL && cgIsParameterUsed(p, prog) == CG_TRUE) var = p;
	}

	bool set_texture(GLuint texobj, const char *name)
	{
		ZZshParameter p;

		p = cgGetNamedParameter(prog, name);

		if (p != NULL && cgIsParameterUsed(p, prog) == CG_TRUE)
		{
			cgGLSetTextureParameter(p, texobj);
			cgGLEnableTextureParameter(p);
			return true;
		}

		return false;
	}

	bool connect(ZZshParameter &tex, const char *name)
	{
		ZZshParameter p;

		p = cgGetNamedParameter(prog, name);

		if (p != NULL && cgIsParameterUsed(p, prog) == CG_TRUE)
		{
			cgConnectParameter(tex, p);
			return true;
		}

		return false;
	}

	bool set_texture(ZZshParameter &tex, const char *name)
	{
		ZZshParameter p;

		p = cgGetNamedParameter(prog, name);

		if (p != NULL && cgIsParameterUsed(p, prog) == CG_TRUE)
		{
			//cgGLEnableTextureParameter(p);
			tex = p;
			return true;
		}

		return false;
	}

	bool set_shader_const(float4 v, const char *name)
	{
		ZZshParameter p;

		p = cgGetNamedParameter(prog, name);

		if (p != NULL && cgIsParameterUsed(p, prog) == CG_TRUE)
		{
			cgGLSetParameter4fv(p, v);
			return true;
		}

		return false;
	}
#endif
};
#else
const GLenum g_texture_target[11] = {GL_TEXTURE_RECTANGLE, GL_TEXTURE_RECTANGLE, GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_RECTANGLE, GL_TEXTURE_RECTANGLE, GL_TEXTURE_RECTANGLE, GL_TEXTURE_2D, GL_TEXTURE_RECTANGLE};

extern uint g_current_texture_bind[11];
struct SamplerParam {
	int		unit;
	GLuint	texid;
	GLenum target;

	SamplerParam() : unit(-1), texid(0), target(0) {}

	void set_unit(int new_unit) {
		assert(new_unit < 11);
		unit = new_unit;
		target = g_texture_target[new_unit];
	}

	void enable_texture() {
		assert(unit >= 0);
		assert(unit < 11);
		if (texid) {
			// Unfortunately there is a nastly corner case
			// 1/ Attach a texture to the unit
			// 2/ delete the texture
			// 3/ recreate a texture (with same id)
			// 4/ => texture need to be reattached again...
#if 0
			if (g_current_texture_bind[unit] != texid) {
				glActiveTexture(GL_TEXTURE0 + unit);
				glBindTexture(target, texid);
				g_current_texture_bind[unit] = texid;
			}
#else
			glActiveTexture(GL_TEXTURE0 + unit);
			glBindTexture(target, texid);
#endif
		}
	}

	void set_texture(GLuint new_texid) {
		texid = new_texid;
	}

	void release_texture() {
		texid = 0;
	}
};

struct FRAGMENTSHADER
{
	FRAGMENTSHADER() : prog(sZero) 
					  , program(0)
					  , context(0)
					  , sMemory(0) // dual context need 2 slots
					  , sFinal(2)
					  , sBitwiseANDX(3)
					  , sBitwiseANDY(4)
					  , sInterlace(5)
					  , sCLUT(6)
	{
		// Uniform
		sOneColor    = (ZZshParameter)offsetof(struct FragmentUniform, g_fOneColor) /4;
		sBitBltZ     = (ZZshParameter)offsetof(struct FragmentUniform, g_fBitBltZ) /4;
		sInvTexDims  = (ZZshParameter)offsetof(struct FragmentUniform, g_fInvTexDims) /4;
		fTexAlpha    = (ZZshParameter)offsetof(struct FragmentUniform, g_fTexAlpha) /4;
		fTexAlpha2   = (ZZshParameter)offsetof(struct FragmentUniform, g_fTexAlpha2) /4;
		fTexOffset   = (ZZshParameter)offsetof(struct FragmentUniform, g_fTexOffset) /4;
		fTexDims     = (ZZshParameter)offsetof(struct FragmentUniform, g_fTexDims) /4;
		fTexBlock    = (ZZshParameter)offsetof(struct FragmentUniform, g_fTexBlock) /4;
		fClampExts   = (ZZshParameter)offsetof(struct FragmentUniform, g_fClampExts) /4; 	// FIXME: There is a bug, that lead FFX-1 to incorrect CLAMP if this uniform have context.
		fTexWrapMode = (ZZshParameter)offsetof(struct FragmentUniform, g_fTexWrapMode) /4;
		fRealTexDims = (ZZshParameter)offsetof(struct FragmentUniform, g_fRealTexDims) /4;
		fTestBlack   = (ZZshParameter)offsetof(struct FragmentUniform, g_fTestBlack) /4;
		fPageOffset  = (ZZshParameter)offsetof(struct FragmentUniform, g_fPageOffset) /4;

		//sFinal               = 2;
		//sBitwiseANDX         = 3;
		//sBitwiseANDY         = 4;
		//sInterlace           = 5;
		//sCLUT                = 6;
		samplers[sMemory].set_unit(10);
		samplers[sMemory+1].set_unit(10); // Dual context. Use same unit
		samplers[sFinal].set_unit(1);
		samplers[sBitwiseANDX].set_unit(6);
		samplers[sBitwiseANDY].set_unit(7);
		samplers[sInterlace].set_unit(8);
		samplers[sCLUT].set_unit(9);

		prog.isFragment = true;
		prog.link		= (void*)this;
	}

	ZZshShaderLink prog;						// it link to FRAGMENTSHADER structure, for compability between GLSL and CG
	ZZshProgram program;
	uint context;

	FragmentUniform uniform_buffer[ZZSH_CTX_ALL];

	// sampler
	const ZZshParameter sMemory;
	const ZZshParameter sFinal, sBitwiseANDX, sBitwiseANDY, sInterlace, sCLUT;
	SamplerParam samplers[7];

	// uniform
	ZZshParameter sOneColor, sBitBltZ, sInvTexDims;
	ZZshParameter fTexAlpha2, fTexOffset, fTexDims, fTexBlock, fClampExts, fTexWrapMode, fRealTexDims, fTestBlack, fPageOffset, fTexAlpha;

#ifdef _DEBUG
	string filename;
#endif

	void ZZshSetParameter4fv(ZZshParameter param, const float* v) {
		if (IsDualContext(param))
			uniform_buffer[context].SettleFloat((int) param, v);
		else
			for ( int i = 0; i < ZZSH_CTX_ALL ; i++)
				uniform_buffer[i].SettleFloat((int) param, v);
	}

	bool IsDualContext(ZZshParameter param) {
		if (param == sInvTexDims || param == sBitBltZ || param == sOneColor)
			return false;
		else
			return true;
	}

	void enable_texture() {
		samplers[sMemory+context].enable_texture(); // sMemory is dual context
		for (int i = 2; i < 7; i++)
			samplers[i].enable_texture();
	}

	void set_texture(ZZshParameter param, GLuint texid) {
		if (param == sMemory) // sMemory is dual context
			samplers[sMemory+context].set_texture(texid);
		else
			samplers[param].set_texture(texid);
	}

	void release_prog() {
		if(program) {
			glDeleteProgram(program);
			program = 0;
		}
		for (uint i = 0; i < 7 ; i++)
			samplers[i].release_texture();
	}

	void set_context(uint new_context) { context = new_context * NOCONTEXT;}
};
#endif

#ifdef GLSL4_API
struct COMMONSHADER
{
	COMMONSHADER() : context(0)
					 , sBlocks(0)
					 , sBilinearBlocks(1)
					 , sConv16to32(2)
				     , sConv32to16(3)
	{
		// sBlocks          = 0;
		// sBilinearBlocks  = 1;
		// sConv16to32      = 2;
		// sConv32to16      = 3;
		samplers[sBlocks].set_unit(2);
		samplers[sBilinearBlocks].set_unit(3);
		samplers[sConv16to32].set_unit(4);
		samplers[sConv32to16].set_unit(5);


		g_fparamFogColor = (ZZshParameter)offsetof(struct GlobalUniform, g_fFogColor) /4;
		g_vparamPosXY    = (ZZshParameter)offsetof(struct GlobalUniform, g_fPosXY) /4;

		g_fBilinear      = (ZZshParameter)offsetof(struct ConstantUniform, g_fBilinear) /4;
		g_fZBias         = (ZZshParameter)offsetof(struct ConstantUniform, g_fZbias) /4;
		g_fc0            = (ZZshParameter)offsetof(struct ConstantUniform, g_fc0) /4;
		g_fMult          = (ZZshParameter)offsetof(struct ConstantUniform, g_fMult) /4;
		g_fZ             = (ZZshParameter)offsetof(struct ConstantUniform, g_fZ) /4;
		g_fZMin          = (ZZshParameter)offsetof(struct ConstantUniform, g_fZMin) /4;
		g_fZNorm         = (ZZshParameter)offsetof(struct ConstantUniform, g_fZNorm) /4;
		g_fExactColor    = (ZZshParameter)offsetof(struct ConstantUniform, g_fExactColor) /4;

		// Setup the constant buffer

		// Set Z-test, log or no log;
		if (conf.settings().no_logz) {
			g_vdepth = float4( 255.0 /256.0f,  255.0/65536.0f, 255.0f/(65535.0f*256.0f), 1.0f/(65536.0f*65536.0f));
			vlogz = float4( 1.0f, 0.0f, 0.0f, 0.0f);
		}
		else {
			g_vdepth = float4( 256.0f*65536.0f, 65536.0f, 256.0f, 65536.0f*65536.0f);
			vlogz = float4( 0.0f, 1.0f, 0.0f, 0.0f);
		}
		uniform_buffer_constant.SettleFloat(g_fZ, g_vdepth );
		uniform_buffer_constant.SettleFloat(g_fZMin, vlogz );

		const float g_filog32 = 0.999f / (32.0f * logf(2.0f));
		float4 vnorm = float4(g_filog32, 0, 0,0);
		uniform_buffer_constant.SettleFloat(g_fZNorm, vnorm);

		uniform_buffer_constant.SettleFloat(g_fBilinear, float4(-0.2f, -0.65f, 0.9f, 1.0f / 32767.0f ) );
		uniform_buffer_constant.SettleFloat(g_fZBias, float4(1.0f/256.0f, 1.0004f, 1, 0.5f) );
		uniform_buffer_constant.SettleFloat(g_fc0, float4(0,1, 0.001f, 0.5f) );

		uniform_buffer_constant.SettleFloat(g_fExactColor, float4(0.5f, (conf.settings().exact_color)?0.9f/256.0f:0.5f/256.0f, 0,1/255.0f) );
		uniform_buffer_constant.SettleFloat(g_fMult, float4(1/1024.0f, 0.2f/1024.0f, 1/128.0f, 1/512.0f));
	}

	ZZshParameter g_fparamFogColor, g_vparamPosXY;
	ZZshParameter g_fBilinear, g_fZBias, g_fc0, g_fMult, g_fZ, g_fZMin, g_fZNorm, g_fExactColor;
	uint context;

	GlobalUniform	uniform_buffer[ZZSH_CTX_ALL];
	ConstantUniform uniform_buffer_constant;

	// Sampler
	const ZZshParameter sBlocks, sBilinearBlocks, sConv16to32, sConv32to16;
	SamplerParam samplers[4];

	void ZZshSetParameter4fv(ZZshParameter param, const float* v) {
		if (IsDualContext(param))
			uniform_buffer[context].SettleFloat((int) param, v);
		else
			for ( int i = 0; i < ZZSH_CTX_ALL ; i++)
				uniform_buffer[i].SettleFloat((int) param, v);
	}

	bool IsDualContext(ZZshParameter param) {
		if (param == g_vparamPosXY) return true;
		else return false;
	}

	void set_texture(ZZshParameter param, GLuint texid) {
		samplers[param].set_texture(texid);
	}

	void enable_texture() {
		for (int i = 0; i < 4; i++)
			samplers[i].enable_texture();
	}

	void set_context(uint new_context) { context = new_context * NOCONTEXT;}
};
#endif

#ifndef GLSL4_API
struct VERTEXSHADER
{
	VERTEXSHADER() : prog(sZero), Shader(0), sBitBltPos(pZero), sBitBltTex(pZero) {}
	
	ZZshShaderLink prog;
	ZZshShader Shader;
	ZZshShaderType ShaderType;

	ZZshParameter sBitBltPos, sBitBltTex, fBitBltTrans;		 // vertex shader constants

	int ParametersStart, ParametersFinish;
};
#else
struct VERTEXSHADER
{

	VERTEXSHADER() : prog(sZero), program(0), context(0)
	{
		sBitBltPos = (ZZshParameter)offsetof(struct VertexUniform, g_fBitBltPos) /4;
		sBitBltTex = (ZZshParameter)offsetof(struct VertexUniform, g_fBitBltTex) /4;
		fBitBltTrans = (ZZshParameter)offsetof(struct VertexUniform, g_fBitBltTrans) /4;

		// Default value not sure it is needed
		uniform_buffer[0].SettleFloat(fBitBltTrans, float4(0.5f, -0.5f, 0.5, 0.5 + 0.4/416.0f ) );
		uniform_buffer[1].SettleFloat(fBitBltTrans, float4(0.5f, -0.5f, 0.5, 0.5 + 0.4/416.0f ) );

		prog.isFragment = false;
		prog.link		= (void*)this;
	}

	VertexUniform uniform_buffer[ZZSH_CTX_ALL];

	ZZshShaderLink prog;
	ZZshProgram program;
	uint	context;

	ZZshParameter sBitBltPos, sBitBltTex, fBitBltTrans;		 // vertex shader constants

	void ZZshSetParameter4fv(ZZshParameter param, const float* v) {
		if (IsDualContext(param))
			uniform_buffer[context].SettleFloat((int) param, v);
		else
			for ( int i = 0; i < ZZSH_CTX_ALL ; i++)
				uniform_buffer[i].SettleFloat((int) param, v);
	}

	bool IsDualContext(ZZshParameter param) { return false;}

	void set_context(uint new_context) { context = new_context * NOCONTEXT;}

	void release_prog() {
		if(program) {
			glDeleteProgram(program);
			program = 0;
		}
	}
};
#endif

#ifdef GLSL4_API
#define SAFE_RELEASE_PROG(x) 	{ \
	if ((x.link) != NULL) { \
		if (x.isFragment) { \
			FRAGMENTSHADER* shader = (FRAGMENTSHADER*)x.link; \
			shader->release_prog(); \
		} else { \
			VERTEXSHADER* shader = (VERTEXSHADER*)x.link; \
			shader->release_prog(); \
		} \
	} \
}
#endif

	extern VERTEXSHADER pvsBitBlt;
	extern FRAGMENTSHADER ppsBitBlt[2], ppsBitBltDepth, ppsOne;					// ppsOne used to stop using shaders for draw
	extern FRAGMENTSHADER ppsBaseTexture, ppsConvert16to32, ppsConvert32to16;

	extern FRAGMENTSHADER ppsRegular[4], ppsTexture[NUM_SHADERS];
	extern FRAGMENTSHADER ppsCRTC[2], /*ppsCRTC24[2],*/ ppsCRTCTarg[2];
#ifdef GLSL4_API
	extern COMMONSHADER g_cs;
#endif

	extern int interlace_mode;

	enum CRTC_TYPE
	{
		CRTC_RENDER,
		//CRTC_RENDER_24,
		CRTC_RENDER_TARG
	};

	static __forceinline FRAGMENTSHADER* curr_ppsCRTC() { return &ppsCRTC[interlace_mode]; }
	//static __forceinline FRAGMENTSHADER* curr_ppsCRTC24() { return &ppsCRTC24[interlace_mode]; }
	static __forceinline FRAGMENTSHADER* curr_ppsCRTCTarg() { return &ppsCRTCTarg[interlace_mode]; }
	
	static __forceinline FRAGMENTSHADER* curr_pps(CRTC_TYPE render_type) 
	{
		switch (render_type)
		{
			case CRTC_RENDER: return curr_ppsCRTC();
			//case CRTC_RENDER_24: return curr_ppsCRTC24();
			case CRTC_RENDER_TARG: return curr_ppsCRTCTarg();
			default: return NULL;
		}
		
	}
// ------------------------- Functions -------------------------------

#ifdef NVIDIA_CG_API
inline bool ZZshExistProgram(FRAGMENTSHADER* pf) {return (pf->prog != NULL); };			// We don't check ps != NULL, so be warned,
inline bool ZZshExistProgram(VERTEXSHADER* pf) {return (pf->prog != NULL); };
inline bool ZZshExistProgram(ZZshShaderLink prog) {return (prog != NULL); };
#endif
#if defined(GLSL_API) && !defined(GLSL4_API)
inline bool ZZshExistProgram(FRAGMENTSHADER* pf) {return (pf->Shader != 0); };
inline bool ZZshExistProgram(VERTEXSHADER* pf) {return (pf->Shader != 0); };
inline bool ZZshExistProgram(ZZshShaderLink prog) {return (prog.link != NULL); }		// This is used for pvs mainly. No NULL means that we do LOAD_VS
#endif
#if defined(GLSL4_API)
inline bool ZZshExistProgram(FRAGMENTSHADER* pf) {return (pf->program != 0); };
inline bool ZZshExistProgram(VERTEXSHADER* pf) {return (pf->program != 0); };
inline bool ZZshExistProgram(ZZshShaderLink prog) {return (prog.link != NULL); }		// This is used for pvs mainly. No NULL means that we do LOAD_VS
#endif

extern const char* ShaderCallerName;
extern const char* ShaderHandleName;

inline void SetShaderCaller(const char* Name) {	
	ShaderCallerName = Name;
}

inline void SetHandleName(const char* Name) {
	ShaderHandleName = Name;
}
		
inline void ResetShaderCounters() {
//	g_vsprog = g_psprog = sZero;
}


/////////////////////////////////////////////////////////////////
// Improvement:
//  * store the location of uniform. Avoid to call glGetUniformLocation a lots of time
//  * Use separate shader build pipeline: current code emulate this behavior but with the recent opengl4
//	  it would be much more easier to code it.
/////////////////////////////////////////////////////////////////

// GLSL: Stub
extern bool ZZshCheckProfilesSupport();

// Try various Shader to choose supported configuration
// g_nPixelShaderVer -> SHADER_ACCURATE and SHADER_REDUCED
// Honestly we can probably stop supporting those cards.
extern bool ZZshStartUsingShaders();

// Open the shader file into an source array
extern bool ZZshCreateOpenShadersFile();

// Those 2 functions are used to stop/start shader. The idea is to draw the HUD text.
// Enable is not implemented and it is likely to stop everythings
extern void ZZshGLDisableProfile();
extern void ZZshGLEnableProfile();

// Set the Uniform parameter in host (NOT GL)
// Param seem to be an absolute index inside a table of uniform
extern void ZZshSetParameter4fv(ZZshShaderLink& prog, ZZshParameter param, const float* v, const char* name);
extern void ZZshSetParameter4fv(ZZshParameter param, const float* v, const char* name);
extern void ZZshSetParameter4fvWithRetry(ZZshParameter* param, ZZshShaderLink& prog, const float* v, const char* name);

// Set the Texture parameter in host (NOT GL)
extern void ZZshGLSetTextureParameter(ZZshShaderLink prog, ZZshParameter param, GLuint texobj, const char* name);
extern void ZZshGLSetTextureParameter(ZZshParameter param, GLuint texobj, const char* name);

// Set a default value for 1 uniform in host (NOT GL)
extern void ZZshDefaultOneColor( FRAGMENTSHADER& ptr );

// Link then run with the new Vertex/Fragment Shader
extern void ZZshSetVertexShader(ZZshShaderLink prog);
extern void ZZshSetPixelShader(ZZshShaderLink prog);

// Compile standalone Fragment/Vertex shader program
// Note It also init all the Uniform parameter in host (NOT GL)
extern bool ZZshLoadExtraEffects();

// Clean some stuff on exit
extern void ZZshExitCleaning();

extern FRAGMENTSHADER* ZZshLoadShadeEffect(int type, int texfilter, int fog, int testaem, int exactcolor, const clampInfo& clamp, int context, bool* pbFailed);

// only sets a limited amount of state (for Update)
void SetTexVariablesInt(int context, int bilinear, const tex0Info& tex0, bool CheckVB, FRAGMENTSHADER* pfragment, int force);

extern u32 ptexBlocks;		// holds information on block tiling. It's texture number in OpenGL -- if 0 than such texture
extern u32 ptexConv16to32;	// does not exists. This textures should be created on start and released on finish.
extern u32 ptexBilinearBlocks;
extern u32 ptexConv32to16;

#ifdef GLSL4_API
extern GSUniformBufferOGL *constant_buffer;
extern GSUniformBufferOGL *common_buffer;
extern GSUniformBufferOGL *vertex_buffer;
extern GSUniformBufferOGL *fragment_buffer;
extern GSVertexBufferStateOGL *vertex_array;

extern GLenum g_current_vs;
extern GLenum g_current_ps;

extern void init_shader();
extern void PutParametersInProgram(VERTEXSHADER* vs, FRAGMENTSHADER* ps);
extern void init_shader();
extern void ZZshSetupShader();

#endif

#endif
