#include "global.h"

#include "RageDisplay.h"
#include "RageDisplay_GLES2.h"
#include "RageUtil.h"
#include "RageLog.h"
#include "RageTimer.h"
#include "RageMath.h"
#include "RageTypes.h"
#include "RageUtil.h"
#include "RageSurface.h"
#include "RageTextureManager.h"

#include "DisplayResolutions.h"

#include "arch/LowLevelWindow/LowLevelWindow.h"

#include <GL/glew.h>

#ifdef NO_GL_FLUSH
#define glFlush()
#endif

namespace
{
	RageDisplay::RagePixelFormatDesc
	PIXEL_FORMAT_DESC[NUM_RagePixelFormat] = {
		{
			/* R8G8B8A8 */
			32,
			{ 0xFF000000,
			  0x00FF0000,
			  0x0000FF00,
			  0x000000FF }
		}, {
			/* B8G8R8A8 */
			32,
			{ 0x0000FF00,
			  0x00FF0000,
			  0xFF000000,
			  0x000000FF }
		}, {
			/* R4G4B4A4 */
			16,
			{ 0xF000,
			  0x0F00,
			  0x00F0,
			  0x000F },
		}, {
			/* R5G5B5A1 */
			16,
			{ 0xF800,
			  0x07C0,
			  0x003E,
			  0x0001 },
		}, {
			/* R5G5B5X1 */
			16,
			{ 0xF800,
			  0x07C0,
			  0x003E,
			  0x0000 },
		}, {
			/* R8G8B8 */
			24,
			{ 0xFF0000,
			  0x00FF00,
			  0x0000FF,
			  0x000000 }
		}, {
			/* Paletted */
			8,
			{ 0,0,0,0 } /* N/A */
		}, {
			/* B8G8R8 */
			24,
			{ 0x0000FF,
			  0x00FF00,
			  0xFF0000,
			  0x000000 }
		}, {
			/* A1R5G5B5 */
			16,
			{ 0x7C00,
			  0x03E0,
			  0x001F,
			  0x8000 },
		}, {
			/* X1R5G5B5 */
			16,
			{ 0x7C00,
			  0x03E0,
			  0x001F,
			  0x0000 },
		}
	};

	/* g_GLPixFmtInfo is used for both texture formats and surface formats.
	 * For example, it's fine to ask for a RagePixelFormat_RGB5 texture, but to
	 * supply a surface matching RagePixelFormat_RGB8.  OpenGL will simply
	 * discard the extra bits.
	 *
	 * It's possible for a format to be supported as a texture format but
	 * not as a surface format.  For example, if packed pixels aren't
	 * supported, we can still use GL_RGB5_A1, but we'll have to convert to
	 * a supported surface pixel format first.  It's not ideal, since we'll
	 * convert to RGBA8 and OGL will convert back, but it works fine.
	 */
	struct GLPixFmtInfo_t {
		GLenum internalfmt; /* target format */
		GLenum format; /* target format */
		GLenum type; /* data format */
	} const g_GLPixFmtInfo[NUM_RagePixelFormat] = {
		{
			/* R8G8B8A8 */
			GL_RGBA8,
			GL_RGBA,
			GL_UNSIGNED_BYTE,
		}, {
			/* R8G8B8A8 */
			GL_RGBA8,
			GL_BGRA,
			GL_UNSIGNED_BYTE,
		}, {
			/* B4G4R4A4 */
			GL_RGBA4,
			GL_RGBA,
			GL_UNSIGNED_SHORT_4_4_4_4,
		}, {
			/* B5G5R5A1 */
			GL_RGB5_A1,
			GL_RGBA,
			GL_UNSIGNED_SHORT_5_5_5_1,
		}, {
			/* B5G5R5 */
			GL_RGB5,
			GL_RGBA,
			GL_UNSIGNED_SHORT_5_5_5_1,
		}, {
			/* B8G8R8 */
			GL_RGB8,
			GL_RGB,
			GL_UNSIGNED_BYTE,
		}, {
			/* Paletted */
			GL_COLOR_INDEX8_EXT,
			GL_COLOR_INDEX,
			GL_UNSIGNED_BYTE,
		}, {
			/* B8G8R8 */
			GL_RGB8,
			GL_BGR,
			GL_UNSIGNED_BYTE,
		}, {
			// TODO: These don't work on ES2. Work out what needs to happen.
			/* A1R5G5B5 (matches D3DFMT_A1R5G5B5) */
			GL_RGB5_A1,
			GL_BGRA,
			GL_UNSIGNED_SHORT_1_5_5_5_REV,
		}, {
			/* X1R5G5B5 */
			GL_RGB5,
			GL_BGRA,
			GL_UNSIGNED_SHORT_1_5_5_5_REV,
		}
	};

	LowLevelWindow *g_pWind;

	void FixLittleEndian()
	{
#if defined(ENDIAN_LITTLE)
		static bool bInitialized = false;
		if (bInitialized)
			return;
		bInitialized = true;

		for( int i = 0; i < NUM_RagePixelFormat; ++i )
		{
			RageDisplay::RagePixelFormatDesc &pf = PIXEL_FORMAT_DESC[i];

			/* OpenGL and RageSurface handle byte formats differently; we need
			 * to flip non-paletted masks to make them line up. */
			if (g_GLPixFmtInfo[i].type != GL_UNSIGNED_BYTE || pf.bpp == 8)
				continue;

			for( int mask = 0; mask < 4; ++mask)
			{
				int m = pf.masks[mask];
				switch( pf.bpp )
				{
				case 24: m = Swap24(m); break;
				case 32: m = Swap32(m); break;
				default:
					 FAIL_M(ssprintf("Unsupported BPP value: %i", pf.bpp));
				}
				pf.masks[mask] = m;
			}
		}
#endif
	}
	namespace Caps
	{
		int iMaxTextureUnits = 1;
		int iMaxTextureSize = 256;
	}
	namespace State
	{
		bool bZTestEnabled = false;
		bool bZWriteEnabled = false;
		bool bAlphaTestEnabled = false;
	}
}

RageDisplay_GLES2::RageDisplay_GLES2()
{
	LOG->Trace( "RageDisplay_GLES2::RageDisplay_GLES2()" );
	LOG->MapLog("renderer", "Current renderer: OpenGL ES 2.0");

	FixLittleEndian();
//	RageDisplay_GLES2_Helpers::Init();

	g_pWind = NULL;
}

RString
RageDisplay_GLES2::Init( const VideoModeParams &p, bool bAllowUnacceleratedRenderer )
{
	g_pWind = LowLevelWindow::Create();

	bool bIgnore = false;
	RString sError = SetVideoMode( p, bIgnore );
	if (sError != "")
		return sError;

	// Get GPU capabilities up front so we don't have to query later.
	glGetIntegerv( GL_MAX_TEXTURE_SIZE, &Caps::iMaxTextureSize );
	glGetIntegerv( GL_MAX_TEXTURE_IMAGE_UNITS, &Caps::iMaxTextureUnits );

	// Log driver details
	g_pWind->LogDebugInformation();
	LOG->Info( "OGL Vendor: %s", glGetString(GL_VENDOR) );
	LOG->Info( "OGL Renderer: %s", glGetString(GL_RENDERER) );
	LOG->Info( "OGL Version: %s", glGetString(GL_VERSION) );
	LOG->Info( "OGL Max texture size: %i", Caps::iMaxTextureSize );
	LOG->Info( "OGL Texture units: %i", Caps::iMaxTextureUnits );

	/* Pretty-print the extension string: */
	LOG->Info( "OGL Extensions:" );
	{
		// glGetString(GL_EXTENSIONS) doesn't work for GL3 core profiles.
		// this will be useful in the future.
#if 0
		vector<string> extensions;
		const char *ext = 0;
		for (int i = 0; (ext = (const char*)glGetStringi(GL_EXTENSIONS, i)); i++)
		{
			extensions.push_back(string(ext));
		}

		sort( extensions.begin(), extensions.end() );
		size_t next = 0;
		while( next < extensions.size() )
		{
			size_t last = next;
			string type;
			for( size_t i = next; i<extensions.size(); ++i )
			{
				vector<string> segments;
				split(extensions[i], '_', segments);
				string this_type;
				if (segments.size() > 2)
					this_type = join("_", segments.begin(), segments.begin()+2);
				if (i > next && this_type != type)
					break;
				type = this_type;
				last = i;
			}

			if (next == last)
			{
				printf( "  %s\n", extensions[next].c_str() );
				++next;
				continue;
			}

			string sList = ssprintf( "  %s: ", type.c_str() );
			while( next <= last )
			{
				vector<string> segments;
				split( extensions[next], '_', segments );
				string ext_short = join( "_", segments.begin()+2, segments.end() );
				sList += ext_short;
				if (next < last)
					sList += ", ";
				if (next == last || sList.size() + extensions[next+1].size() > 78)
				{
					printf( "%s\n", sList.c_str() );
					sList = "    ";
				}
				++next;
			}
		}
#else
		const char *szExtensionString = (const char *) glGetString(GL_EXTENSIONS);
		vector<RString> asExtensions;
		split( szExtensionString, " ", asExtensions );
		sort( asExtensions.begin(), asExtensions.end() );
		size_t iNextToPrint = 0;
		while( iNextToPrint < asExtensions.size() )
		{
			size_t iLastToPrint = iNextToPrint;
			RString sType;
			for( size_t i = iNextToPrint; i<asExtensions.size(); ++i )
			{
				vector<RString> asBits;
				split( asExtensions[i], "_", asBits );
				RString sThisType;
				if (asBits.size() > 2)
					sThisType = join( "_", asBits.begin(), asBits.begin()+2 );
				if (i > iNextToPrint && sThisType != sType)
					break;
				sType = sThisType;
				iLastToPrint = i;
			}

			if (iNextToPrint == iLastToPrint)
			{
				LOG->Info( "  %s", asExtensions[iNextToPrint].c_str() );
				++iNextToPrint;
				continue;
			}

			RString sList = ssprintf( "  %s: ", sType.c_str() );
			while( iNextToPrint <= iLastToPrint )
			{
				vector<RString> asBits;
				split( asExtensions[iNextToPrint], "_", asBits );
				RString sShortExt = join( "_", asBits.begin()+2, asBits.end() );
				sList += sShortExt;
				if (iNextToPrint < iLastToPrint)
					sList += ", ";
				if (iNextToPrint == iLastToPrint || sList.size() + asExtensions[iNextToPrint+1].size() > 120)
				{
					LOG->Info( "%s", sList.c_str() );
					sList = "    ";
				}
				++iNextToPrint;
			}
#endif
		}
	}

	glewExperimental = true;
	glewInit();

	/* Log this, so if people complain that the radar looks bad on their
	 * system we can compare them: */
	//glGetFloatv( GL_LINE_WIDTH_RANGE, g_line_range );
	//glGetFloatv( GL_POINT_SIZE_RANGE, g_point_range );

	return RString();
}

// Return true if mode change was successful.
// bNewDeviceOut is set true if a new device was created and textures
// need to be reloaded.
RString RageDisplay_GLES2::TryVideoMode( const VideoModeParams &p, bool &bNewDeviceOut )
{
	VideoModeParams vm = p;
	vm.windowed = 1; // force windowed until I trust this thing.
	LOG->Warn( "RageDisplay_GLES2::TryVideoMode( %d, %d, %d, %d, %d, %d )",
		vm.windowed, vm.width, vm.height, vm.bpp, vm.rate, vm.vsync );

	RString err = g_pWind->TryVideoMode( vm, bNewDeviceOut );
	if (err != "")
		return err;	// failed to set video mode

	if (bNewDeviceOut)
	{
		// NOTE: This isn't needed in an actual GLES2 context...
		glewInit();

		/* We have a new OpenGL context, so we have to tell our textures that
		 * their OpenGL texture number is invalid. */
		if (TEXTUREMAN)
			TEXTUREMAN->InvalidateTextures();

		/* Delete all render targets.  They may have associated resources other than
		 * the texture itself. */
		//FOREACHM( unsigned, RenderTarget *, g_mapRenderTargets, rt )
		//	delete rt->second;
		//g_mapRenderTargets.clear();

		/* Recreate all vertex buffers. */
		//InvalidateObjects();

		//InitShaders();
	}

	ResolutionChanged();

	return RString();
}

int RageDisplay_GLES2::GetMaxTextureSize() const
{
	return Caps::iMaxTextureSize;
}

bool RageDisplay_GLES2::BeginFrame()
{
	/* We do this in here, rather than ResolutionChanged, or we won't update the
	 * viewport for the concurrent rendering context. */
	int fWidth = g_pWind->GetActualVideoModeParams().width;
	int fHeight = g_pWind->GetActualVideoModeParams().height;

	glViewport( 0, 0, fWidth, fHeight );

	glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
	SetZWrite( true );
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

	return RageDisplay::BeginFrame();
}

void RageDisplay_GLES2::EndFrame()
{
	glFlush();

	// XXX: This is broken on NVidia, as their xrandr sucks.
	FrameLimitBeforeVsync( g_pWind->GetActualVideoModeParams().rate );
	g_pWind->SwapBuffers();
	FrameLimitAfterVsync();

	g_pWind->Update();

	RageDisplay::EndFrame();
}

RageDisplay_GLES2::~RageDisplay_GLES2()
{
	delete g_pWind;
}

void
RageDisplay_GLES2::GetDisplayResolutions( DisplayResolutions &out ) const
{
	out.clear();
	g_pWind->GetDisplayResolutions( out );
}

RageSurface*
RageDisplay_GLES2::CreateScreenshot()
{
	const RagePixelFormatDesc &desc = PIXEL_FORMAT_DESC[RagePixelFormat_RGB8];
	RageSurface *image = CreateSurface(
		640, 480, desc.bpp,
		desc.masks[0], desc.masks[1], desc.masks[2], desc.masks[3] );

	memset( image->pixels, 0, 480*image->pitch );

	return image;
}

const RageDisplay::RagePixelFormatDesc*
RageDisplay_GLES2::GetPixelFormatDesc(RagePixelFormat pf) const
{
	ASSERT( pf >= 0 && pf < NUM_RagePixelFormat );
	return &PIXEL_FORMAT_DESC[pf];
}

RageMatrix
RageDisplay_GLES2::GetOrthoMatrix( float l, float r, float b, float t, float zn, float zf )
{
	RageMatrix m(
		2/(r-l),      0,            0,           0,
		0,            2/(t-b),      0,           0,
		0,            0,            -2/(zf-zn),   0,
		-(r+l)/(r-l), -(t+b)/(t-b), -(zf+zn)/(zf-zn),  1 );
	return m;
}

class RageCompiledGeometryGLES2 : public RageCompiledGeometry
{
public:
	
	void Allocate( const vector<msMesh> &vMeshes )
	{
		// TODO
	}
	void Change( const vector<msMesh> &vMeshes )
	{
		// TODO
	}
	void Draw( int iMeshIndex ) const
	{
		// TOO
	}
};

RageCompiledGeometry*
RageDisplay_GLES2::CreateCompiledGeometry()
{
	return new RageCompiledGeometryGLES2;
}

void
RageDisplay_GLES2::DeleteCompiledGeometry( RageCompiledGeometry *p )
{
	delete p;
}

RString
RageDisplay_GLES2::GetApiDescription() const
{
	return "OpenGL ES 2.0";
}

VideoModeParams
RageDisplay_GLES2::GetActualVideoModeParams() const
{
	return g_pWind->GetActualVideoModeParams();
}

void
RageDisplay_GLES2::SetBlendMode( BlendMode mode )
{
	// TODO
}

bool
RageDisplay_GLES2::SupportsTextureFormat( RagePixelFormat pixfmt, bool realtime )
{
	/* If we support a pixfmt for texture formats but not for surface formats, then
	 * we'll have to convert the texture to a supported surface format before uploading.
	 * This is too slow for dynamic textures. */
	if (realtime && !SupportsSurfaceFormat(pixfmt))
		return false;

	switch (g_GLPixFmtInfo[pixfmt].format)
	{
	case GL_COLOR_INDEX:
		return false;
	case GL_BGR:
	case GL_BGRA:
		//return !!GLEW_EXT_bgra;
		return false; // no BGRA on ES2 (without exts)
	default:
		return true;
	}

	return true;
}

bool
RageDisplay_GLES2::SupportsPerVertexMatrixScale()
{
	return true;
}

unsigned
RageDisplay_GLES2::CreateTexture(
	RagePixelFormat pixfmt,
	RageSurface* img,
	bool bGenerateMipMaps
	)
{
	// TODO
	return 1;
}

void
RageDisplay_GLES2::UpdateTexture( 
	unsigned iTexHandle, 
	RageSurface* img,
	int xoffset, int yoffset, int width, int height 
	)
{
	// TODO
}

void
RageDisplay_GLES2::DeleteTexture( unsigned iTexHandle )
{
	// TODO
}

void
RageDisplay_GLES2::ClearAllTextures()
{
	FOREACH_ENUM( TextureUnit, i )
		SetTexture( i, 0 );

	// HACK:  Reset the active texture to 0.
	// TODO:  Change all texture functions to take a stage number.
	glActiveTexture(GL_TEXTURE0);
}

int
RageDisplay_GLES2::GetNumTextureUnits()
{
	return Caps::iMaxTextureUnits;
}

static bool
SetTextureUnit( TextureUnit tu )
{
	if ((int) tu > Caps::iMaxTextureUnits)
		return false;
	glActiveTexture( enum_add2(GL_TEXTURE0, tu) );
	return true;
}

void
RageDisplay_GLES2::SetTexture( TextureUnit tu, unsigned iTexture )
{
	if (!SetTextureUnit( tu ))
		return;

	if (iTexture)
	{
		glEnable( GL_TEXTURE_2D );
		glBindTexture( GL_TEXTURE_2D, iTexture );
	}
	else
	{
		glDisable( GL_TEXTURE_2D );
	}
}

void 
RageDisplay_GLES2::SetTextureMode( TextureUnit tu, TextureMode tm )
{
	// TODO
}

void
RageDisplay_GLES2::SetTextureWrapping( TextureUnit tu, bool b )
{
	// TODO
}

void
RageDisplay_GLES2::SetTextureFiltering( TextureUnit tu, bool b )
{
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, b ? GL_LINEAR : GL_NEAREST);
	
	GLint iMinFilter = 0;
	if (b)
	{
		GLint iWidth1 = -1;
		GLint iWidth2 = -1;
		glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &iWidth1);
		glGetTexLevelParameteriv(GL_TEXTURE_2D, 1, GL_TEXTURE_WIDTH, &iWidth2);
		if (iWidth1 > 1 && iWidth2 != 0)
		{
			/* Mipmaps are enabled. */
			if (g_pWind->GetActualVideoModeParams().bTrilinearFiltering)
				iMinFilter = GL_LINEAR_MIPMAP_LINEAR;
			else
				iMinFilter = GL_LINEAR_MIPMAP_NEAREST;
		}
		else
		{
			iMinFilter = GL_LINEAR;
		}
	}
	else
	{
		iMinFilter = GL_NEAREST;
	}

	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, iMinFilter );
}

bool
RageDisplay_GLES2::IsZWriteEnabled() const
{
	return State::bZWriteEnabled;
}

bool
RageDisplay_GLES2::IsZTestEnabled() const
{
	return State::bZTestEnabled;
}

void
RageDisplay_GLES2::SetZWrite( bool b )
{
	if (State::bZWriteEnabled != b)
	{
		State::bZWriteEnabled = b;
		glDepthMask( b );
	}
}

void
RageDisplay_GLES2::SetZBias( float f )
{
	float fNear = SCALE( f, 0.0f, 1.0f, 0.05f, 0.0f );
	float fFar = SCALE( f, 0.0f, 1.0f, 1.0f, 0.95f );

	glDepthRange( fNear, fFar );
}

void
RageDisplay_GLES2::SetZTestMode( ZTestMode mode )
{
	glEnable( GL_DEPTH_TEST );
	switch( mode )
	{
	case ZTEST_OFF:
		glDisable( GL_DEPTH_TEST );
		glDepthFunc( GL_ALWAYS );
		State::bZTestEnabled = false;
		break;
	case ZTEST_WRITE_ON_PASS: glDepthFunc( GL_LEQUAL ); break;
	case ZTEST_WRITE_ON_FAIL: glDepthFunc( GL_GREATER ); break;
	default:
		FAIL_M(ssprintf("Invalid ZTestMode: %i", mode));
	}
	State::bZTestEnabled = true;
}





/*


void RageDisplay_Legacy::SetBlendMode( BlendMode mode )
{
	glEnable(GL_BLEND);

	if (glBlendEquation != NULL)
	{
		if (mode == BLEND_INVERT_DEST)
			glBlendEquation( GL_FUNC_SUBTRACT );
		else if (mode == BLEND_SUBTRACT)
			glBlendEquation( GL_FUNC_REVERSE_SUBTRACT );
		else
			glBlendEquation( GL_FUNC_ADD );
	}

	int iSourceRGB, iDestRGB;
	int iSourceAlpha = GL_ONE, iDestAlpha = GL_ONE_MINUS_SRC_ALPHA;
	switch( mode )
	{
	case BLEND_NORMAL:
		iSourceRGB = GL_SRC_ALPHA; iDestRGB = GL_ONE_MINUS_SRC_ALPHA;
		break;
	case BLEND_ADD:
		iSourceRGB = GL_SRC_ALPHA; iDestRGB = GL_ONE;
		break;
	case BLEND_SUBTRACT:
		iSourceRGB = GL_SRC_ALPHA; iDestRGB = GL_ONE_MINUS_SRC_ALPHA;
		break;
	case BLEND_MODULATE:
		iSourceRGB = GL_ZERO; iDestRGB = GL_SRC_COLOR;
		break;
	case BLEND_COPY_SRC:
		iSourceRGB = GL_ONE; iDestRGB = GL_ZERO;
		iSourceAlpha = GL_ONE; iDestAlpha = GL_ZERO;
		break;
	case BLEND_ALPHA_MASK:
		iSourceRGB = GL_ZERO; iDestRGB = GL_ONE;
		iSourceAlpha = GL_ZERO; iDestAlpha = GL_SRC_ALPHA;
		break;
	case BLEND_ALPHA_KNOCK_OUT:
		iSourceRGB = GL_ZERO; iDestRGB = GL_ONE;
		iSourceAlpha = GL_ZERO; iDestAlpha = GL_ONE_MINUS_SRC_ALPHA;
		break;
	case BLEND_ALPHA_MULTIPLY:
		iSourceRGB = GL_SRC_ALPHA; iDestRGB = GL_ZERO;
		break;
	case BLEND_WEIGHTED_MULTIPLY:
		// output = 2*(dst*src).  0.5,0.5,0.5 is identity; darker colors darken the image,
		// and brighter colors lighten the image.
		iSourceRGB = GL_DST_COLOR; iDestRGB = GL_SRC_COLOR;
		break;
	case BLEND_INVERT_DEST:
		// out = src - dst.  The source color should almost always be #FFFFFF, to make it "1 - dst".
		iSourceRGB = GL_ONE; iDestRGB = GL_ONE;
		break;
	case BLEND_NO_EFFECT:
		iSourceRGB = GL_ZERO; iDestRGB = GL_ONE;
		iSourceAlpha = GL_ZERO; iDestAlpha = GL_ONE;
		break;
	DEFAULT_FAIL( mode );
	}

	if (GLEW_EXT_blend_equation_separate)
		glBlendFuncSeparateEXT( iSourceRGB, iDestRGB, iSourceAlpha, iDestAlpha );
	else
		glBlendFunc( iSourceRGB, iDestRGB );
}

bool RageDisplay_Legacy::IsZWriteEnabled() const
{
	bool a;
	glGetBooleanv( GL_DEPTH_WRITEMASK, (unsigned char*)&a );
	return a;
}


*/

void
RageDisplay_GLES2::ClearZBuffer()
{
	bool write = IsZWriteEnabled();
	SetZWrite( true );
	glClear( GL_DEPTH_BUFFER_BIT );
	SetZWrite( write );
}

void
RageDisplay_GLES2::SetCullMode( CullMode mode )
{
	if (mode != CULL_NONE)
		glEnable(GL_CULL_FACE);
	switch( mode )
	{
	case CULL_BACK:
		glCullFace( GL_BACK );
		break;
	case CULL_FRONT:
		glCullFace( GL_FRONT );
		break;
	case CULL_NONE:
		glDisable( GL_CULL_FACE );
		break;
	default:
		FAIL_M(ssprintf("Invalid CullMode: %i", mode));
	}
}

void
RageDisplay_GLES2::SetAlphaTest( bool b )
{
	void (*toggle)(GLenum) = b ? glEnable : glDisable;
	if (State::bAlphaTestEnabled != b)
	{
		State::bAlphaTestEnabled = b;
		toggle(GL_ALPHA_TEST);
	}
}

void
RageDisplay_GLES2::SetMaterial( 
	const RageColor &emissive,
	const RageColor &ambient,
	const RageColor &diffuse,
	const RageColor &specular,
	float shininess
	)
{
	// TODO
}

void
RageDisplay_GLES2::SetLineWidth(float fWidth)
{
	glLineWidth(fWidth);
}

void
RageDisplay_GLES2::SetPolygonMode(PolygonMode pm)
{
	GLenum m;
	switch (pm)
	{
	case POLYGON_FILL:	m = GL_FILL; break;
	case POLYGON_LINE:	m = GL_LINE; break;
	default:
		FAIL_M(ssprintf("Invalid PolygonMode: %i", pm));
	}
	glPolygonMode(GL_FRONT_AND_BACK, m);
}

void
RageDisplay_GLES2::SetLighting( bool b )
{
	// TODO
}

void
RageDisplay_GLES2::SetLightOff( int index )
{
	// TODO
}

void
RageDisplay_GLES2::SetLightDirectional( 
	int index, 
	const RageColor &ambient, 
	const RageColor &diffuse, 
	const RageColor &specular, 
	const RageVector3 &dir )
{
	// TODO
}

void
RageDisplay_GLES2::SetSphereEnvironmentMapping( TextureUnit tu, bool b )
{
	// TODO
}

void
RageDisplay_GLES2::SetCelShaded( int stage )
{
	// TODO
}

void
RageDisplay_GLES2::DrawQuadsInternal( const RageSpriteVertex v[], int iNumVerts )
{
	// TODO
}

void
RageDisplay_GLES2::DrawQuadStripInternal( const RageSpriteVertex v[], int iNumVerts )
{
	// TODO
}

void
RageDisplay_GLES2::DrawFanInternal( const RageSpriteVertex v[], int iNumVerts )
{
	// TODO
}

void
RageDisplay_GLES2::DrawStripInternal( const RageSpriteVertex v[], int iNumVerts )
{
	// TODO
}

void
RageDisplay_GLES2::DrawTrianglesInternal( const RageSpriteVertex v[], int iNumVerts )
{
	// TODO
}

void
RageDisplay_GLES2::DrawCompiledGeometryInternal( const RageCompiledGeometry *p, int 
	iMeshIndex )
{
	// TODO
}

void
RageDisplay_GLES2::DrawLineStripInternal( const RageSpriteVertex v[], int iNumVerts, float LineWidth )
{
	// TODO
}

// Is this even used?
void
RageDisplay_GLES2::DrawSymmetricQuadStripInternal( const RageSpriteVertex v[], int iNumVerts )
{
	// TODO
}

bool
RageDisplay_GLES2::SupportsSurfaceFormat( RagePixelFormat pixfmt )
{
	switch (g_GLPixFmtInfo[pixfmt].type)
	{
	case GL_UNSIGNED_SHORT_1_5_5_5_REV:
		return false;
		//return GLEW_EXT_bgra && g_bReversePackedPixelsWorks;
	default:
		return true;
	}
}

/*
 * Copyright (c) 2012 Colby Klein
 * All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, and/or sell copies of the Software, and to permit persons to
 * whom the Software is furnished to do so, provided that the above
 * copyright notice(s) and this permission notice appear in all copies of
 * the Software and that both the above copyright notice(s) and this
 * permission notice appear in supporting documentation.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
 * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
 * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
 * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */
