/******************************************************************************

 @File         KEGL/PVRShellAPI.cpp

 @Title        KEGL/PVRShellAPI

 @Version      

 @Copyright    Copyright (c) Imagination Technologies Limited.

 @Platform     Independent

 @Description  Makes programming for 3D APIs easier by wrapping surface
               initialization, Texture allocation and other functions for use by a demo.

******************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

#include "PVRShell.h"
#include "PVRShellAPI.h"
#include "PVRShellOS.h"
#include "PVRShellImpl.h"

// No Doxygen for CPP files, due to documentation duplication
/// @cond NO_DOXYGEN

#ifndef EGL_CONTEXT_LOST_IMG
/*! Extended error code EGL_CONTEXT_LOST_IMG generated when power management event has occurred. */
#define EGL_CONTEXT_LOST_IMG		0x300E
#endif

#ifndef EGL_CONTEXT_PRIORITY_LEVEL_IMG
/*! An extensions added to the list of attributes for the context to give it a priority hint */
#define EGL_CONTEXT_PRIORITY_LEVEL_IMG		0x3100
/*! Request the context is created with high priority */
#define EGL_CONTEXT_PRIORITY_HIGH_IMG		0x3101
/*! Request the context is created with medium priority */
#define EGL_CONTEXT_PRIORITY_MEDIUM_IMG		0x3102
/*! Request the context is created with low priority */
#define EGL_CONTEXT_PRIORITY_LOW_IMG		0x3103
#endif

/*****************************************************************************
	Declarations
*****************************************************************************/
static bool PVRShellIsExtensionSupported(EGLDisplay dpy, const char *extension);

#if defined GL_ES_VERSION_2_0 && !defined EGL_VERSION_1_3
#error OpenGL ES 2 requires egl.h version 1.3 or higher
#endif

/****************************************************************************
** Class: PVRShellInitAPI
****************************************************************************/

/*****************************************************************************
* Function Name  : ActivatePreferences
* Description    : Activates the user set preferences (like v-sync)
*****************************************************************************/
void PVRShellInit::ApiActivatePreferences()
{

#ifdef EGL_VERSION_1_1
	eglSwapInterval(m_EGLDisplay, m_pShell->m_pShellData->nSwapInterval);
#endif
}

/*****************************************************************************
* Function Name  : ApiInitAPI
* Returns        : true for success
* Description    : Initialise the 3D API
*****************************************************************************/
bool PVRShellInit::ApiInitAPI()
{
	int					bDone;

	m_NDT = (EGLNativeDisplayType)OsGetNativeDisplayType();
	m_NPT = (EGLNativePixmapType) OsGetNativePixmapType();
	m_NWT = (EGLNativeWindowType) OsGetNativeWindowType();

	m_EGLContext = 0;

	do
	{
		bDone = true;

		m_EGLDisplay = eglGetDisplay(m_NDT);

		if(m_EGLDisplay == EGL_NO_DISPLAY)
		{
#if defined(BUILD_OGLES2) || defined(BUILD_OGLES3)
			m_EGLDisplay = eglGetDisplay((EGLNativeDisplayType)EGL_DEFAULT_DISPLAY);
#else
			m_EGLDisplay = eglGetDisplay((NativeDisplayType)EGL_DEFAULT_DISPLAY);
#endif
		}

		if(!eglInitialize(m_EGLDisplay, &m_MajorVersion, &m_MinorVersion))
		{
			m_pShell->PVRShellSet(prefExitMessage, "PVRShell: Unable to initialise EGL\n");
			m_pShell->PVRShellOutputDebug("PVRShell: EGL Error (%s)\n", StringFrom_eglGetError());
			return false;
		}

		m_pShell->PVRShellOutputDebug("PVRShell: EGL %d.%d initialized\n", m_MajorVersion, m_MinorVersion);

		// Check Extension availability after EGL initialization
		if (m_MajorVersion > 1 || (m_MajorVersion == 1 && m_MinorVersion >= 1))
		{
			m_bPowerManagementSupported = true;
		}
		else
		{
			m_bPowerManagementSupported = PVRShellIsExtensionSupported(m_EGLDisplay,"EGL_IMG_power_management");
		}

		do
		{
#if defined(BUILD_OGL)
			if(!eglBindAPI(EGL_OPENGL_API))
			{
				m_pShell->PVRShellSet(prefExitMessage, "PVRShell: Failed to bind OpenGL API\n");
				return false;
			}
#else
#if defined EGL_VERSION_1_3 && defined GL_ES_VERSION_2_0
			if(!eglBindAPI(EGL_OPENGL_ES_API))
			{
				m_pShell->PVRShellSet(prefExitMessage, "PVRShell: Failed to bind OpenGL ES API\n");
				return false;
			}
#endif
#endif
			// Find an EGL config
			m_EGLConfig = SelectEGLConfiguration(m_pShell->m_pShellData);
			eglGetConfigAttrib(m_EGLDisplay, m_EGLConfig, EGL_CONFIG_ID, &m_iConfig);

			// Destroy the context if we already created one
			if (m_EGLContext)
			{
				eglDestroyContext(m_EGLDisplay, m_EGLContext);
			}

			// Attempt to create a context
			EGLint ai32ContextAttribs[48];
			int	i = 0;

#if defined(BUILD_OGLES3)
			ai32ContextAttribs[i++] = EGL_CONTEXT_CLIENT_VERSION;
			ai32ContextAttribs[i++] = 3;
#else
#if defined(EGL_VERSION_1_3) && defined(GL_ES_VERSION_2_0)
			ai32ContextAttribs[i++] = EGL_CONTEXT_CLIENT_VERSION;
			ai32ContextAttribs[i++] = 2;
#endif
#endif

#if defined(BUILD_OGL)
			//Attempt to create an OpenGL 3.2 context.
			if (PVRShellIsExtensionSupported(m_EGLDisplay, "EGL_KHR_create_context"))
			{
				ai32ContextAttribs[i++] = EGL_CONTEXT_MAJOR_VERSION_KHR;
				ai32ContextAttribs[i++] = 3;
				ai32ContextAttribs[i++] = EGL_CONTEXT_MINOR_VERSION_KHR;
				ai32ContextAttribs[i++] = 2;
				ai32ContextAttribs[i++] = EGL_CONTEXT_FLAGS_KHR;
				ai32ContextAttribs[i++] = EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR;
				ai32ContextAttribs[i++] = EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR;
				ai32ContextAttribs[i++] = EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR;
			}
#endif

#if defined(BUILD_OGLES) || defined(BUILD_OGLES2) || defined(BUILD_OGLES3)
			if(PVRShellIsExtensionSupported(m_EGLDisplay,"EGL_IMG_context_priority"))
			{
				ai32ContextAttribs[i++] = EGL_CONTEXT_PRIORITY_LEVEL_IMG;
				switch(m_pShell->PVRShellGet(prefPriority))
				{
					case 0: ai32ContextAttribs[i++] = EGL_CONTEXT_PRIORITY_LOW_IMG; break;
					case 1: ai32ContextAttribs[i++] = EGL_CONTEXT_PRIORITY_MEDIUM_IMG; break;
					default:ai32ContextAttribs[i++] = EGL_CONTEXT_PRIORITY_HIGH_IMG; break;
				}
			}
#endif
			ai32ContextAttribs[i] = EGL_NONE;

			if (m_EGLContext == EGL_NO_CONTEXT)
			{
				m_EGLContext = eglCreateContext(m_EGLDisplay, m_EGLConfig, NULL, ai32ContextAttribs);
			}

			if(m_EGLContext == EGL_NO_CONTEXT)
			{
				if(m_iRequestedConfig > 0)
				{
					// We failed to create a context
					m_pShell->PVRShellSet(prefExitMessage, "PVRShell: Unable to create a context\n");
					return false;
				}
				else if(m_pShell->m_pShellData->bNeedPbuffer)
				{
					// Disable P-buffer and try again
					m_pShell->m_pShellData->bNeedPbuffer = false;
				}
				else if(m_pShell->m_pShellData->bNeedStencilBuffer)
				{
                    // Disable Stencil Buffer and try again
					m_pShell->m_pShellData->bNeedStencilBuffer = false;
				}
				else if(m_pShell->m_pShellData->nAASamples > 0)
				{
					// Still failing, reduce the AA samples and try again
					--m_pShell->m_pShellData->nAASamples;
				}
				else
				{
					m_pShell->PVRShellSet(prefExitMessage, "PVRShell: Unable to create a context\n");
					return false;
				}
			}
		} while(m_EGLContext == EGL_NO_CONTEXT);

#if defined(__QNXNTO__)
		int format = SCREEN_FORMAT_RGBX8888;
	    if(screen_set_window_property_iv((_screen_window*) m_NWT, SCREEN_PROPERTY_FORMAT, &format))
	    {
	    	m_pShell->PVRShellSet(prefExitMessage, "PVRShell: Failed to set window property SCREEN_PROPERTY_FORMAT\n");
	    	return false;
	    }

#if defined(BUILD_OGLES2)
	    int usage = SCREEN_USAGE_OPENGL_ES2;
#else
#if defined(BUILD_OGLES)
	    int usage = SCREEN_USAGE_OPENGL_ES1;
#endif
#endif
	    if(screen_set_window_property_iv((_screen_window*) m_NWT, SCREEN_PROPERTY_USAGE, &usage))
	    {
	    	m_pShell->PVRShellSet(prefExitMessage, "PVRShell: Failed to set window property SCREEN_PROPERTY_USAGE\n");
	    	return false;
	    }

	    if(screen_create_window_buffers((_screen_window*) m_NWT, 2))
	    {
	    	m_pShell->PVRShellSet(prefExitMessage, "PVRShell: Failed to create window buffers\n");
	    	return false;
	    }
#endif
		EGLint		attrib_list[16];
		int	i = 0;
#if defined(EGL_VERSION_1_2)
		if(m_pShell->m_pShellData->bNeedAlphaFormatPre) // The default is EGL_ALPHA_FORMAT_NONPRE
		{
			attrib_list[i++] = EGL_ALPHA_FORMAT;
			attrib_list[i++] = EGL_ALPHA_FORMAT_PRE;
		}
#endif
		// Terminate the attribute list with EGL_NONE
		attrib_list[i] = EGL_NONE;

		if(m_pShell->m_pShellData->bNeedPixmap)
		{
			m_pShell->PVRShellOutputDebug("InitAPI() Using pixmaps, about to create egl surface\n");
			m_EGLWindow = eglCreatePixmapSurface(m_EGLDisplay, m_EGLConfig, m_NPT, attrib_list);
		}
		else
		{
#if defined(ANDROID)
			EGLint visualID;
		    eglGetConfigAttrib(m_EGLDisplay, m_EGLConfig, EGL_NATIVE_VISUAL_ID, &visualID);

		    // Change the format of our window to match our config
    		ANativeWindow_setBuffersGeometry(m_NWT, 0, 0, visualID);
#endif
			m_EGLWindow = eglCreateWindowSurface(m_EGLDisplay, m_EGLConfig, m_NWT, attrib_list);

            // If we have failed to create a surface then try using Null
			if(m_EGLWindow == EGL_NO_SURFACE)
			{
				m_EGLWindow = eglCreateWindowSurface(m_EGLDisplay, m_EGLConfig, NULL, attrib_list);
			}
		}

		if (m_EGLWindow == EGL_NO_SURFACE)
		{
			m_pShell->PVRShellSet(prefExitMessage, "PVRShell: Unable to create surface\n");
			return false;
		}

		if (!eglMakeCurrent(m_EGLDisplay, m_EGLWindow, m_EGLWindow, m_EGLContext))
		{
#ifdef EGL_VERSION_1_3
			if((eglGetError() == EGL_CONTEXT_LOST))
#else
			if((eglGetError() == EGL_CONTEXT_LOST_IMG) && m_bPowerManagementSupported)
#endif
			{
				bDone = false;
			}
			else
			{
				m_pShell->PVRShellSet(prefExitMessage, "PVRShell: Unable to make context current\n");
				return false;
			}
		}
	} while(!bDone);

	/*
	 	Get correct screen width and height and
		save them into
		m_pShell->m_pShellData->nShellDimX and
		m_pShell->m_pShellData->nShellDimY
	*/
	eglQuerySurface(m_EGLDisplay, m_EGLWindow,
			EGL_WIDTH,  (EGLint*)&m_pShell->m_pShellData->nShellDimX
		);
	eglQuerySurface(m_EGLDisplay, m_EGLWindow,
			EGL_HEIGHT, (EGLint*)&m_pShell->m_pShellData->nShellDimY
		);

#if defined(ANDROID)
	glViewport(0, 0, m_pShell->m_pShellData->nShellDimX, m_pShell->m_pShellData->nShellDimY);
#endif
	/*
		Done - activate requested features
	*/

#if defined(BUILD_OGLES) || defined(BUILD_OGLES2)
	//Get the discardframebufferEXT function.
	{
		//Get the gl extension string
		const char* strExtensions = (const char*)glGetString(GL_EXTENSIONS);

		//Get the length of the string we're searching for
		const size_t strLength = strlen("GL_EXT_discard_framebuffer");

		//Get the string position
		const char* position = strstr(strExtensions,"GL_EXT_discard_framebuffer");

		//Loop through until we find the actual extension, avoiding substrings.
		while (position!=NULL && position[strLength]!='\0' && position[strLength]!=' ')
		{
			position = strstr(position+strLength,"GL_EXT_discard_framebuffer");
		}

		//Initialise the extension if it's found.
		if (position != NULL)
		{
			glDiscardFramebufferEXT = (PFNGLDISCARDFRAMEBUFFEREXTPROC)eglGetProcAddress("glDiscardFramebufferEXT");
		}
		else
		{
			glDiscardFramebufferEXT = NULL;
		}
	}
#endif
	ApiActivatePreferences();
	return true;
}

/*!***********************************************************************
@Function		OutputAPIInfo
@description	When prefOutputInfo is set to true this function outputs
				various pieces of API dependent information via
				PVRShellOutputDebug.
*************************************************************************/
void PVRShellInit::OutputAPIInfo()
{
	// Output API dependent information
	if(m_pShell->PVRShellGet(prefOutputInfo))
	{
		EGLint i32Values[5];

		m_pShell->PVRShellOutputDebug("\n");

		m_pShell->PVRShellOutputDebug("GL:\n");
		m_pShell->PVRShellOutputDebug("  Vendor:   %s\n", (char*) glGetString(GL_VENDOR));
		m_pShell->PVRShellOutputDebug("  Renderer: %s\n", (char*) glGetString(GL_RENDERER));
		m_pShell->PVRShellOutputDebug("  Version:  %s\n", (char*) glGetString(GL_VERSION));
		m_pShell->PVRShellOutputDebug("  Extensions:  ");

#if defined(BUILD_OGL)
		//Get the glGetString process.
		typedef const GLubyte* (KHRONOS_APIENTRY * PFNGLGETSTRINGIPROC)(GLenum name, GLuint index);
		PFNGLGETSTRINGIPROC glGetStringi = (PFNGLGETSTRINGIPROC)eglGetProcAddress("glGetStringi");

		//If we've successfully got the new way to query the string, then go ahead and use this.
		if (glGetStringi)
		{
#ifndef GL_NUM_EXTENSIONS
#define GL_NUM_EXTENSIONS                 0x821D
#endif
			GLint numExtensions;
			glGetIntegerv(GL_NUM_EXTENSIONS,&numExtensions);

			for (GLint i=0; i<numExtensions; ++i)
			{
				m_pShell->PVRShellOutputDebug((const char*)glGetStringi(GL_EXTENSIONS,i));
				m_pShell->PVRShellOutputDebug(" ");
			}
		}
#else
		m_pShell->PVRShellOutputDebug("%s\n", (char*) glGetString(GL_EXTENSIONS));
#endif

		m_pShell->PVRShellOutputDebug("\n");

		m_pShell->PVRShellOutputDebug("\n");
		m_pShell->PVRShellOutputDebug("EGL:\n");
		m_pShell->PVRShellOutputDebug("  Vendor:   %s\n" , (char*) eglQueryString(m_EGLDisplay, EGL_VENDOR));
		m_pShell->PVRShellOutputDebug("  Version:  %s\n" , (char*) eglQueryString(m_EGLDisplay, EGL_VERSION));
		m_pShell->PVRShellOutputDebug("  Extensions:  %s\n" , (char*) eglQueryString(m_EGLDisplay, EGL_EXTENSIONS));

		if(eglQueryContext(m_EGLDisplay, m_EGLContext, EGL_CONTEXT_PRIORITY_LEVEL_IMG, &i32Values[0]))
		{
			switch(i32Values[0])
			{
				case EGL_CONTEXT_PRIORITY_HIGH_IMG:   m_pShell->PVRShellOutputDebug("  Context priority: High\n");  break;
				case EGL_CONTEXT_PRIORITY_MEDIUM_IMG: m_pShell->PVRShellOutputDebug("  Context priority: Medium\n");break;
				case EGL_CONTEXT_PRIORITY_LOW_IMG:    m_pShell->PVRShellOutputDebug("  Context priority: Low\n");   break;
				default: m_pShell->PVRShellOutputDebug("  Context priority: Unrecognised.\n"); break;
			}
		}
		else
		{
			eglGetError(); // Clear error
			m_pShell->PVRShellOutputDebug("  Context priority: Unsupported\n");
		}

#ifdef EGL_VERSION_1_2
		m_pShell->PVRShellOutputDebug("  Client APIs:  %s\n" , (char*) eglQueryString(m_EGLDisplay, EGL_CLIENT_APIS));
#endif

		m_pShell->PVRShellOutputDebug("\n");
		m_pShell->PVRShellOutputDebug("Window Width:  %i\n" , m_pShell->PVRShellGet(prefWidth));
		m_pShell->PVRShellOutputDebug("Window Height: %i\n" , m_pShell->PVRShellGet(prefHeight));
		m_pShell->PVRShellOutputDebug("Is Rotated: %s\n", m_pShell->PVRShellGet(prefIsRotated) ? "Yes" : "No");
		m_pShell->PVRShellOutputDebug("\n");

		// EGLSurface details
		m_pShell->PVRShellOutputDebug("EGL Surface:\n");
		eglGetConfigAttrib(m_EGLDisplay, m_EGLConfig, EGL_CONFIG_ID , &i32Values[0]);
		m_pShell->PVRShellOutputDebug("  Config ID:      %i\n", i32Values[0]);

		// Colour buffer
		eglGetConfigAttrib(m_EGLDisplay, m_EGLConfig, EGL_BUFFER_SIZE , &i32Values[0]);
		eglGetConfigAttrib(m_EGLDisplay, m_EGLConfig, EGL_RED_SIZE    , &i32Values[1]);
		eglGetConfigAttrib(m_EGLDisplay, m_EGLConfig, EGL_GREEN_SIZE  , &i32Values[2]);
		eglGetConfigAttrib(m_EGLDisplay, m_EGLConfig, EGL_BLUE_SIZE   , &i32Values[3]);
		eglGetConfigAttrib(m_EGLDisplay, m_EGLConfig, EGL_ALPHA_SIZE  , &i32Values[4]);
		m_pShell->PVRShellOutputDebug("  Colour Buffer:  %i bits (R%i G%i B%i A%i)\n", i32Values[0],i32Values[1],i32Values[2],i32Values[3],i32Values[4]);

		// Depth buffer
		eglGetConfigAttrib(m_EGLDisplay, m_EGLConfig, EGL_DEPTH_SIZE , &i32Values[0]);
		m_pShell->PVRShellOutputDebug("  Depth Buffer:   %i bits\n", i32Values[0]);

		// Stencil Buffer
		eglGetConfigAttrib(m_EGLDisplay, m_EGLConfig, EGL_STENCIL_SIZE , &i32Values[0]);
		m_pShell->PVRShellOutputDebug("  Stencil Buffer: %i bits\n", i32Values[0]);

		// EGL surface bits support
		eglGetConfigAttrib(m_EGLDisplay, m_EGLConfig, EGL_SURFACE_TYPE , &i32Values[0]);
		m_pShell->PVRShellOutputDebug("  Surface type:   %s%s%s\n",	i32Values[0] & EGL_WINDOW_BIT  ? "WINDOW " : "",
																		i32Values[1] & EGL_PBUFFER_BIT ? "PBUFFER " : "",
																		i32Values[2] & EGL_PIXMAP_BIT  ? "PIXMAP " : "");
		// EGL renderable type
#ifdef EGL_VERSION_1_2
		eglGetConfigAttrib(m_EGLDisplay, m_EGLConfig, EGL_RENDERABLE_TYPE , &i32Values[0]);
		m_pShell->PVRShellOutputDebug("  Renderable type: %s%s%s%s\n", i32Values[0] & EGL_OPENVG_BIT ? "OPENVG " : "",
															i32Values[0] & EGL_OPENGL_ES_BIT ? "OPENGL_ES " : "",
#ifdef EGL_OPENGL_BIT
															i32Values[0] & EGL_OPENGL_BIT ? "OPENGL " :
#endif
															"",
															i32Values[0] & EGL_OPENGL_ES2_BIT ? "OPENGL_ES2 " : "");
#endif

		eglGetConfigAttrib(m_EGLDisplay, m_EGLConfig, EGL_SAMPLE_BUFFERS , &i32Values[0]);
		eglGetConfigAttrib(m_EGLDisplay, m_EGLConfig, EGL_SAMPLES , &i32Values[1]);
		m_pShell->PVRShellOutputDebug("  Sample buffer No.: %i\n", i32Values[0]);
		m_pShell->PVRShellOutputDebug("  Samples per pixel: %i\n", i32Values[1]);
	}
}

/*!***********************************************************************
 @Function		ApiReleaseAPI
 @description	Releases all resources allocated by the API.
*************************************************************************/
void PVRShellInit::ApiReleaseAPI()
{
	eglSwapBuffers(m_EGLDisplay, m_EGLWindow);
	eglMakeCurrent(m_EGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
 	eglDestroyContext(m_EGLDisplay, m_EGLContext);
	eglDestroySurface(m_EGLDisplay, m_EGLWindow);
   	eglTerminate(m_EGLDisplay);
}

/*******************************************************************************
 * Function Name  : SelectEGLConfiguration
 * Inputs		  : pData
 * Returns        : EGLConfig
 * Description    : Find the config to use for EGL initialisation
 *******************************************************************************/
EGLConfig PVRShellInitAPI::SelectEGLConfiguration(const PVRShellData * const pData)
{
    EGLint		num_config;
    EGLint		conflist[32];
	EGLConfig	conf = (EGLConfig) 0;
    int			i = 0;

	// Specific config ID requested?
	if (m_iRequestedConfig > 0)
	{
		conflist[i++] = EGL_CONFIG_ID;
		conflist[i++] = m_iRequestedConfig;
		conflist[i++] = EGL_NONE;
		if(!eglChooseConfig(m_EGLDisplay, conflist, &conf, 1, &num_config) || num_config != 1)
		{
			return 0;
		}
		return conf;
	}

	// Select default configuration
#if defined(ANDROID)
	if(pData->nColorBPP == 32)
	{
		conflist[i++] = EGL_RED_SIZE;
		conflist[i++] = 8;
		conflist[i++] = EGL_GREEN_SIZE;
		conflist[i++] = 8;
		conflist[i++] = EGL_BLUE_SIZE;
		conflist[i++] = 8;
		conflist[i++] = EGL_ALPHA_SIZE;
		conflist[i++] = 8;
	}
	else
	{
		conflist[i++] = EGL_RED_SIZE;
		conflist[i++] = 5;
		conflist[i++] = EGL_GREEN_SIZE;
		conflist[i++] = 6;
		conflist[i++] = EGL_BLUE_SIZE;
		conflist[i++] = 5;
		conflist[i++] = EGL_ALPHA_SIZE;
		conflist[i++] = 0;
	}
#else
	conflist[i++] = EGL_BUFFER_SIZE;
	conflist[i++] = pData->nColorBPP;
#endif

	if(pData->bNeedZbuffer || pData->nDepthBPP > 0)
	{
		conflist[i++] = EGL_DEPTH_SIZE;
		conflist[i++] = (pData->nDepthBPP > 0) ? pData->nDepthBPP : 16;
	}

	if(pData->bNeedStencilBuffer)
	{
		conflist[i++] = EGL_STENCIL_SIZE;
		conflist[i++] = 8;
	}

	conflist[i++] = EGL_SURFACE_TYPE;
	conflist[i] = EGL_WINDOW_BIT;

	if(pData->bNeedPbuffer)
	{
		conflist[i] |= EGL_PBUFFER_BIT;
	}

	if(pData->bNeedPixmap)
	{
		conflist[i] |= EGL_PIXMAP_BIT;
	}

	++i;

#if defined(BUILD_OGL)
	conflist[i++] = EGL_RENDERABLE_TYPE;
	conflist[i++] = EGL_OPENGL_BIT;
#elif defined(EGL_VERSION_1_3) && defined(GL_ES_VERSION_2_0)
	conflist[i++] = EGL_RENDERABLE_TYPE;
	conflist[i++] = EGL_OPENGL_ES2_BIT;
#endif

	// Append number of number of samples depending on AA samples value set
	if(pData->nAASamples > 0)
	{
		conflist[i++] = EGL_SAMPLE_BUFFERS;
		conflist[i++] = 1;
		conflist[i++] = EGL_SAMPLES;
		conflist[i++] = pData->nAASamples;
	}
	else
	{
		conflist[i++] = EGL_SAMPLE_BUFFERS;
		conflist[i++] = 0;
	}

#if defined(EWS) || defined(__QNXNTO__)
	if(m_NWT != NULL)
	{
		EGLint r,g,b,a, value;
		EGLint i32Total_num_configs, j;
		EGLConfig	*pConfigs;

		// Some platforms require an egl config to have the same pixel format as the native window because
		// pixel format conversion is prohibited.
#if defined(EWS)
		int format = EWS_PIXEL_FORMAT_RGB_565;
		r = 5; g = 6; b = 5; a = 0;
#else
		r = g = b = a = 8;
#endif

		conflist[i++] = EGL_RED_SIZE;
		conflist[i++] = r;

		conflist[i++] = EGL_GREEN_SIZE;
		conflist[i++] = g;

		conflist[i++] = EGL_BLUE_SIZE;
		conflist[i++] = b;

		conflist[i++] = EGL_ALPHA_SIZE;
		conflist[i++] = a;

		// Terminate the list with EGL_NONE
		conflist[i++] = EGL_NONE;

		// Find out how many configs there are in total that match our criteria
		if(!eglChooseConfig(m_EGLDisplay, conflist, NULL, 0, &i32Total_num_configs) || i32Total_num_configs == 0)
			return 0;

		// Allocate an array large enough to store all the possible configs that may be returned
		pConfigs = new EGLConfig[i32Total_num_configs];

		if(!pConfigs)
			return 0;

		// Get all the configs that match our criteria
		if(!eglChooseConfig(m_EGLDisplay, conflist, pConfigs, i32Total_num_configs, &num_config))
		{
			delete[] pConfigs;
			return 0;
		}

		// Go through the returned configs and try and find a suitable match
		for(j = 0; j < num_config; ++j)
		{
#if defined(__QNXNTO__)
			if((eglGetConfigAttrib(m_EGLDisplay, pConfigs[j], EGL_RED_SIZE,   &value) && value == r)
			&& (eglGetConfigAttrib(m_EGLDisplay, pConfigs[j], EGL_GREEN_SIZE, &value) && value == g)
			&& (eglGetConfigAttrib(m_EGLDisplay, pConfigs[j], EGL_BLUE_SIZE,  &value) && value == b)
			&& (eglGetConfigAttrib(m_EGLDisplay, pConfigs[j], EGL_ALPHA_SIZE, &value) && value == a))
			{
				conf = pConfigs[j];
				break;
			}
#else
#if defined (EWS)
			eglGetConfigAttrib(m_EGLDisplay, pConfigs[j], EGL_NATIVE_VISUAL_ID, &value);
			if (value == format)
			{
				conf = pConfigs[j];
				break;
			}
#endif
#endif
		}

		// Tidy up
		delete[] pConfigs;
	}
	else
#endif
	{
		// Terminate the list with EGL_NONE
		conflist[i++] = EGL_NONE;

		// Return null config if config is not found
		if(!eglChooseConfig(m_EGLDisplay, conflist, &conf, 1, &num_config) || num_config != 1)
		{
			return 0;
		}
	}

	// Return config index
	return conf;
}

/*******************************************************************************
 * Function Name  : StringFrom_eglGetError
 * Returns        : A string
 * Description    : Returns a string representation of an egl error
 *******************************************************************************/
const char *PVRShellInitAPI::StringFrom_eglGetError() const
{
	EGLint nErr = eglGetError();

	switch(nErr)
	{
		case EGL_SUCCESS:
			return "EGL_SUCCESS";
		case EGL_BAD_DISPLAY:
			return "EGL_BAD_DISPLAY";
		case EGL_NOT_INITIALIZED:
			return "EGL_NOT_INITIALIZED";
		case EGL_BAD_ACCESS:
			return "EGL_BAD_ACCESS";
		case EGL_BAD_ALLOC:
			return "EGL_BAD_ALLOC";
		case EGL_BAD_ATTRIBUTE:
			return "EGL_BAD_ATTRIBUTE";
		case EGL_BAD_CONFIG:
			return "EGL_BAD_CONFIG";
		case EGL_BAD_CONTEXT:
			return "EGL_BAD_CONTEXT";
		case EGL_BAD_CURRENT_SURFACE:
			return "EGL_BAD_CURRENT_SURFACE";
		case EGL_BAD_MATCH:
			return "EGL_BAD_MATCH";
		case EGL_BAD_NATIVE_PIXMAP:
			return "EGL_BAD_NATIVE_PIXMAP";
		case EGL_BAD_NATIVE_WINDOW:
			return "EGL_BAD_NATIVE_WINDOW";
		case EGL_BAD_PARAMETER:
			return "EGL_BAD_PARAMETER";
		case EGL_BAD_SURFACE:
			return "EGL_BAD_SURFACE";
		default:
			return "unknown";
	}
}

/*!***********************************************************************
@Function		ApiScreenCaptureBuffer
@Input			Width Width of the region to capture
@Input			Height Height of the region to capture
@Input			pBuf A buffer to put the screen capture into
@description	API-specific function to store the current content of the
				FrameBuffer into the memory allocated by the user.
*************************************************************************/
bool PVRShellInit::ApiScreenCaptureBuffer(int Width,int Height,unsigned char *pBuf)
{
	unsigned char	*pLines2;
	int				i, j;
	bool			bRet = true;

	/* Allocate memory for line */
	pLines2 = (unsigned char *)calloc(4 * Width * Height, sizeof(unsigned char));
	if (!pLines2) return false;

	while (glGetError());
	/* Read line from frame buffer */
	glReadPixels(0, 0, Width, Height, GL_RGBA, GL_UNSIGNED_BYTE, pLines2);

	if(glGetError())
	{
		bRet = false;
	}
	else
	{
		/* Convert RGB to BGR in line */
		for (j = 0, i = 0; j < 4 * Width * Height; j += 4, i += 3)
		{
			pBuf[i] = pLines2[j+2];
			pBuf[i+1] = pLines2[j+1];
			pBuf[i+2] = pLines2[j];
		}
	}

	free(pLines2);
	return bRet;
}

/*!***********************************************************************
 @Function		ApiRenderComplete
 @description	Perform API operations required after a frame has finished (e.g., flipping).
*************************************************************************/
void PVRShellInit::ApiRenderComplete()
{

#if defined(BUILD_OGLES) || defined(BUILD_OGLES2) || defined(BUILD_OGLES3)
	//Discard the framebuffer if set.
#if !defined(BUILD_OGLES3)
	if (glDiscardFramebufferEXT)
#endif
	{
		const GLint numAttachments=3;
		GLenum attachments[numAttachments];
		GLint currentAttachment=0;
		if (m_pShell->PVRShellGet(prefDiscardColor))
		{
			attachments[currentAttachment] = GL_COLOR_EXT;
			currentAttachment++;
		}
		if (m_pShell->PVRShellGet(prefDiscardDepth))
		{
			attachments[currentAttachment] = GL_DEPTH_EXT;
			currentAttachment++;
		}
		if (m_pShell->PVRShellGet(prefDiscardStencil))
		{
			attachments[currentAttachment] = GL_STENCIL_EXT;
			currentAttachment++;
		}
		//Assuming some attachments have been chosen, discard/invalidate them.
		if (currentAttachment!=0)
		{
#if defined(BUILD_OGLES)
			glDiscardFramebufferEXT(GL_FRAMEBUFFER_OES, currentAttachment, attachments);
#elif defined(BUILD_OGLES2)
			glDiscardFramebufferEXT(GL_FRAMEBUFFER, currentAttachment, attachments);
#elif defined(BUILD_OGLES3)
			glInvalidateFramebuffer(GL_FRAMEBUFFER, currentAttachment, attachments);
#endif
		}
	}
#endif

	bool bRes;

	if(m_pShell->m_pShellData->bNeedPixmap)
	{
		/*
			"Clients rendering to single buffered surfaces (e.g. pixmap surfaces)
			should call eglWaitGL before accessing the native pixmap from the client."
		*/
		eglWaitGL();

		// Pixmap support: Copy the rendered pixmap to the display
		if(m_pShell->m_pShellData->bNeedPixmapDisableCopy)
		{
			bRes = true;
		}
		else
		{
			bRes = OsPixmapCopy();
		}
	}
	else
	{
		if(m_pShell->m_pShellData->bNoShellSwapBuffer)
			return;

		bRes = (eglSwapBuffers (m_EGLDisplay, m_EGLWindow) == EGL_TRUE);
	}

	if(!bRes)
	{
		// check for context loss
#ifdef EGL_VERSION_1_3
		if(eglGetError() == EGL_CONTEXT_LOST)
#else
		if((eglGetError() == EGL_CONTEXT_LOST_IMG) && m_bPowerManagementSupported)
#endif
		{
			m_pShell->ReleaseView();

			OsDoReleaseAPI();
			if(ApiInitAPI())
			{
				m_pShell->InitView();
			}
		}
		else
		{
			if(m_pShell->m_pShellData->bNeedPixmap)
				m_pShell->PVRShellOutputDebug("failed to copy pixmap\n");
			else
				m_pShell->PVRShellOutputDebug("eglSwapBuffers failed\n");
		}
	}
}

/*!***********************************************************************
 @Function		ApiSet
 @Input			prefName	Name of value to set
 @Modified		i32Value	Value to set it to
 @description	Set parameters which are specific to the API.
*************************************************************************/
bool PVRShellInit::ApiSet(const prefNameIntEnum prefName, const int i32Value)
{
	switch(prefName)
	{
#ifdef EGL_VERSION_1_1
	case prefSwapInterval:
		m_pShell->m_pShellData->nSwapInterval = i32Value;
		return true;
#endif
#if defined(BUILD_OGLES) || defined(BUILD_OGLES2) || defined(BUILD_OGLES3)
	case prefPriority:
		m_pShell->m_pShellData->nPriority = i32Value;
		return true;
#endif
	case prefRequestedConfig:
		m_iRequestedConfig = (EGLint) i32Value;
		return true;

	default:
		return false;
	}
}

/*!***********************************************************************
 @Function		ApiGet
 @Input			prefName	Name of value to get
 @Modified		pn A pointer set to the value asked for
 @description	Get parameters which are specific to the API.
*************************************************************************/
bool PVRShellInit::ApiGet(const prefNameIntEnum prefName, int *pn)
{
	switch(prefName)
	{
		case prefEGLMajorVersion:
			*pn = (int) m_MajorVersion;
			return true;

		case prefEGLMinorVersion:
			*pn = (int) m_MinorVersion;
			return true;

		case prefRequestedConfig:
			*pn = (int) m_iRequestedConfig;
			return true;

		case prefConfig:
			*pn = (int) m_iConfig;
			return true;

		default:
			return false;
	}
}

/*!***********************************************************************
 @Function		ApiGet
 @Input			prefName	Name of value to get
 @Modified		pp A pointer set to the value asked for
 @description	Get parameters which are specific to the API.
*************************************************************************/
bool PVRShellInit::ApiGet(const prefNamePtrEnum prefName, void **pp)
{
		switch(prefName)
		{
		case prefEGLDisplay:
			*pp = (void*)m_EGLDisplay;
			return true;
		case prefEGLSurface:
			*pp = (void*)m_EGLWindow;
			return true;
		default:
			return false;
		}
}

/****************************************************************************
** Local code
****************************************************************************/

// The recommended technique for querying OpenGL extensions;
// adapted from http://opengl.org/resources/features/OGLextensions/
static bool PVRShellIsExtensionSupported(EGLDisplay dpy, const char *extension)
{
	// The recommended technique for querying EGL extensions matches OpenGLES;
	// from http://opengl.org/resources/features/OGLextensions/
    const char *extensions = NULL;
    const char *start;
    char *terminator;

    /* Extension names should not have spaces. */
    char* where = (char *) strchr(extension, ' ');
    if (where || *extension == '\0')
        return 0;

    extensions = eglQueryString(dpy, EGL_EXTENSIONS);
	if(!extensions)
		return false;

    /* It takes a bit of care to be fool-proof about parsing the
    OpenGL extensions string. Don't be fooled by sub-strings, etc. */
    start = extensions;
    for (;;) {
        where = (char *) strstr((const char *) start, extension);
        if (!where)
            break;
        terminator = where + strlen(extension);
        if (where == start || *(where - 1) == ' ')
            if (*terminator == ' ' || *terminator == '\0')
                return true;
        start = terminator;
    }
    return false;
}

/// @endcond

/*****************************************************************************
 End of file (PVRShellAPI.cpp)
*****************************************************************************/

