/******************************************************************************
 * $Id: gfx_texture_opengl.hh,v 1.34 2006/08/10 03:18:58 devel Exp $
 * @file
 * @author	Jim E. Brooks  http://www.jimbrooks.org
 * @brief	Texture classes (OpenGL).
 * @remarks
 *//*
 * LEGAL:	COPYRIGHT (C) 2006 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE (GPL).
 ******************************************************************************/

#ifndef GFX_TEXTURE_OPENGL_HH
#define GFX_TEXTURE_OPENGL_HH 1

#include "gfx_tga.hh"

namespace gfx {

/// Texture minification filter and magnification filter control.
enum eGpTextureMinFilter
{
	GP_TEXTURE_MIN_FILTER_DEFAULT					= 0,
	GP_TEXTURE_MIN_FILTER_NEAREST					= GL_NEAREST,
	GP_TEXTURE_MIN_FILTER_LINEAR					= GL_LINEAR,
	GP_TEXTURE_MIN_FILTER_NEAREST_MIPMAP_NEAREST	= GL_NEAREST_MIPMAP_NEAREST,
	GP_TEXTURE_MIN_FILTER_NEAREST_MIPMAP_LINEAR		= GL_NEAREST_MIPMAP_LINEAR,
	GP_TEXTURE_MIN_FILTER_LINEAR_MIPMAP_NEAREST		= GL_LINEAR_MIPMAP_NEAREST,
	GP_TEXTURE_MIN_FILTER_LINEAR_MIPMAP_LINEAR		= GL_LINEAR_MIPMAP_LINEAR,
};
enum eGpTextureMagFilter
{
	GP_TEXTURE_MAG_FILTER_DEFAULT					= 0,
	GP_TEXTURE_MAG_FILTER_NEAREST					= GL_NEAREST,
	GP_TEXTURE_MAG_FILTER_LINEAR					= GL_LINEAR,
	// Magnfication filter doesn't do mipmap filtering.
};

/// Texture wrapping control.
enum eGpTextureWrap
{
	GP_TEXTURE_WRAP_DEFAULT				= 0,
	GP_TEXTURE_WRAP_REPEAT				= GL_REPEAT,
	GP_TEXTURE_WRAP_CLAMP				= GL_CLAMP,
	GP_TEXTURE_WRAP_CLAMP_TO_EDGE		= GL_CLAMP_TO_EDGE,
	GP_TEXTURE_WRAP_CLAMP_TO_BORDER		= GL_CLAMP,
};

// Texture function.
// The resulting fragment is function of the texture image and the "texture function".
enum eGpTextureFunc
{
	GP_TEXTURE_FUNC_DEFAULT				= 0,
	GP_TEXTURE_FUNC_DECAL				= GL_DECAL,
	GP_TEXTURE_FUNC_REPLACE				= GL_REPLACE,
	GP_TEXTURE_FUNC_MODULATE			= GL_MODULATE,
	GP_TEXTURE_FUNC_BLEND				= GL_BLEND,
};

////////////////////////////////////////////////////////////////////////////////
// Base Texture class.
class Texture
{
    PREVENT_COPYING(Texture)
public:
	// Texture settings.  Struct is opaque to clients.
	class Mode
	{
	// allow copying
	public:
		Mode( void )
		{
			mMinFilter	= GP_TEXTURE_MIN_FILTER_LINEAR;
			mMagFilter	= GP_TEXTURE_MAG_FILTER_LINEAR;
			mWrap[0]	= GP_TEXTURE_WRAP_REPEAT;
			mWrap[1]	= GP_TEXTURE_WRAP_REPEAT;
			mWrap[2]	= GP_TEXTURE_WRAP_DEFAULT;
			mMipmap		= false;
			mFunc		= GP_TEXTURE_FUNC_DEFAULT;
			mBlendColor.Set(0,0,0,0);
		}
	private:
		eGpTextureMinFilter		mMinFilter;
		eGpTextureMagFilter		mMagFilter;
		eGpTextureWrap			mWrap[3];		// wrap in each dimension
		bool					mMipmap;
		eGpTextureFunc			mFunc;			// texture function
		RGBA					mBlendColor;	// for texture function GL_BLEND
	friend class Texture;
	};

public:
	// Create texture with empty texture data.
	Texture( uint width, uint height, uint depth=1 );
	Texture( Targa& targa );
	virtual ~Texture();

	uint8*		GetBuf( void )				{ return mBuf; }
	uint		GetBufLen( void )			{ return mBufLen; }
	uint8		ReadBuf( uint i )			{ ASSERT( i < mBufLen ); return mBuf[i]; }
	void		WriteBuf( uint i, uint8 c )	{ ASSERT( i < mBufLen ); mBuf[i] = c; }
	uint		GetWidth( void )			{ return mWidth; }
	uint		GetHeight( void )			{ return mHeight; }
	uint		GetDepth( void )			{ return mDepth; }  // returns 1 for 2D texture

	// Texture rendering can be disabled.  Affects only BeginDraw(), EndDraw(), Map().
	// Other methods will remain enabled.
	void		Enable( bool enable = true )	{ mEnabled = enable; }
	void		Disable( void )					{ mEnabled = false; }

	// Texture mode.
	void	SetMinFilter( eGpTextureMinFilter filter )	{ mChangedMode = true; mMode.mMinFilter = filter; }
	void	SetMagFilter( eGpTextureMagFilter filter )	{ mChangedMode = true; mMode.mMagFilter = filter; }
	void	SetWrap( eGpTextureWrap wrap, uint dim )	{ mChangedMode = true; ASSERT(dim<3); mMode.mWrap[dim] = wrap; }
	void	SetMipmap( bool mipmap = true )				{ mChangedMode = true; mMode.mMipmap = mipmap; }
	void	SetFunc( eGpTextureFunc func )				{ mChangedMode = true; mMode.mFunc = func; }
	void	SetBlendColor( const RGBA rgba )			{ mChangedMode = true; mMode.mBlendColor = rgba; }
	void	SetMode( const Texture::Mode& mode )		{ mChangedMode = true; mMode = mode; }
	Texture::Mode	GetMode( void )						{ return mMode; }

	// Methods for the rendering of a texture-mapped primitive.
	//
	//     GFX_BEGIN( GP_QUADS, texture );				// calls Texture::Draw()
	//         texture->Map( x, y ); GFX_VERTEXV( v );	// map 2D texel to 3D vertex
    //     GFX_END( texture );							// calls Texture::EndDraw()
	//
	void		Begin( void );
	void		End( void );
	void		Map( fp x, fp y )			{ if ( mEnabled ) glTexCoord2f( x, y );    }
	void		Map( fp x, fp y, fp z )		{ if ( mEnabled ) glTexCoord3f( x, y, z ); }
	void		Map( const TexCoord2& tc )	{ Map( tc.x, tc.y ); }
	void		Map( const TexCoord3& tc )	{ Map( tc.x, tc.y, tc.z ); }

	// Return number of dimensions (2 or 3).  Equal to texture coordinates per vertex.
	int			DimCnt( void ) { return mDepth == 1 ? 2 : 3; }

protected:
	void			Init( uint width, uint height, uint depth=1 );
	void			Update( void );
	void			UpdateMode( void );
	void			UpdateBuf( void );
	virtual void	CommitBuffer( void );  // CommitBuffer() isn't public to keep it outside MutableTexture's public interface.

protected:
	enum			{ TEXTURE_ID_INVALID = 0 };  // OpenGL defines 0 to mean no texture
	enum			{ MAX_TEXTURE_SIZE = 512*1024 };
	bool			mValid;			// if texture data was never written by client
	bool			mEnabled;		// if false, texture is a NOP
	bool			mChangedMode;	// if texture settings changed
	bool			mChangedBuf;	// if host buffer changed
	bool			mMutableImage;	// if client can repeatedly change the texture image (rather than once only)
	uint			mWidth, mHeight, mDepth;  // texture dimensions
	uint			mDimEnum;		// GL_TEXTURE_2D or GL_TEXTURE_3D
	uint8*			mBuf;			// texture image data
	uint			mBufLen;
	GLuint			mId;			// OpenGL texture object "name"
	Texture::Mode mMode;			// texture settings

	friend bool operator<( Texture& a, Texture& b );
	virtual void nop( void ) = 0;  // prevents instantiating Texture
};
#if DEBUG
typedef       Ptr<Texture> TexturePtr;
typedef const Ptr<Texture> TexturePtrc;
#else
typedef Texture* TexturePtr;
typedef Texture* TexturePtrc;
#endif

////////////////////////////////////////////////////////////////////////////////
// StaticTexture class.
//
// The adjective static refers only to the texture image.
// After the client writes the texture image (to the host buffer),
// and calls CommitBuffer(), the texture image can never be changed.
//
class StaticTexture : public Texture
{
    PREVENT_COPYING(StaticTexture)
private:
	typedef Texture Parent;

public:
	StaticTexture( uint width, uint height, uint depth=1 ) : Parent(width,height,depth) { }
	StaticTexture( Targa& targa ) : Parent(targa) { }
	virtual ~StaticTexture() { }

	virtual void	CommitBuffer( void ) { Parent::CommitBuffer(); }

protected:
	virtual void nop( void ) { };  // prevents instantiating Texture
};

////////////////////////////////////////////////////////////////////////////////
// MutableTexture class.
//
// The adjective mutable refers only to the texture image.
// The client can repeatedly write to the texture image (host buffer)
// then call BufferWasChanged() to change the actual graphics texture object.
//
class MutableTexture : public Texture
{
    PREVENT_COPYING(MutableTexture)
private:
	typedef Texture Parent;

public:
	MutableTexture( uint width, uint height, uint depth=1 ) : Parent(width,height,depth) { mMutableImage = true; }
	MutableTexture( Targa& targa ) : Parent(targa) { }
	virtual ~MutableTexture() { }

	void	BufferWasChanged( void ) { mChangedBuf = true; }

protected:
	// CommitBuffer() isn't part of public interface.
	// When base class calls it, reroute to BufferWasChanged().
	virtual void	CommitBuffer( void ) { BufferWasChanged(); }
	virtual void nop( void ) { };  // prevents instantiating Texture
};

// prototypes
bool
operator<( TexCoord2& a, TexCoord2& b );

bool
operator<( TexCoord3& a, TexCoord3& b );

bool
operator<( Texture& a, Texture& b );

} // namespace gfx

#endif // GFX_TEXTURE_OPENGL_HH
