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

 @File         PVRTBackground.cpp

 @Title        PVRTBackground

 @Version      

 @Copyright    Copyright (C)  Imagination Technologies Limited.

 @Platform     ANSI compatible

 @Description  Function to draw a background texture.

******************************************************************************/
#include "PVRTShader.h"
#include "PVRTBackground.h"

// The header that contains the shaders
#include "PVRTBackgroundShaders.h"
#include "PVRTgles2Ext.h"
// Index to bind the attributes to vertex shaders
const int VERTEX_ARRAY = 0;
const int TEXCOORD_ARRAY = 1;

/****************************************************************************
** Structures
****************************************************************************/

// The struct to include various API variables
struct SPVRTBackgroundAPI
{
	GLuint	m_ui32VertexShader;
	GLuint	m_ui32FragShader;
	GLuint	m_ui32ProgramObject;
	GLuint	m_ui32VertexBufferObject;
};

/****************************************************************************
** Class: CPVRTBackground
****************************************************************************/

/*****************************************************************************
 @Function			Background
 @Description		Init some values.
*****************************************************************************/
CPVRTBackground::CPVRTBackground(void)
{
	m_bInit = false;
	m_pAPI  = 0;
}


/*****************************************************************************
 @Function			~Background
 @Description		Calls Destroy()
*****************************************************************************/
CPVRTBackground::~CPVRTBackground(void)
{
	Destroy();
}

/*!***************************************************************************
 @Function		Destroy
 @Description	Destroys the background. It's called by the destructor.
*****************************************************************************/
void CPVRTBackground::Destroy()
{
	if(m_bInit)
	{
		// Delete shaders
		glDeleteProgram(m_pAPI->m_ui32ProgramObject);
		glDeleteShader(m_pAPI->m_ui32VertexShader);
		glDeleteShader(m_pAPI->m_ui32FragShader);

		// Delete buffer objects
		glDeleteBuffers(1, &m_pAPI->m_ui32VertexBufferObject);

		m_bInit = false;
	}

	delete m_pAPI;
	m_pAPI = 0;
}

/*!***************************************************************************
 @Function		Init
 @Input			pContext	A pointer to a PVRTContext
 @Input			bRotate		true to rotate texture 90 degrees.
 @Input			pszError	An option string for returning errors
 @Return 		PVR_SUCCESS on success
 @Description	Initialises the background
*****************************************************************************/
EPVRTError CPVRTBackground::Init(const SPVRTContext * const pContext, bool bRotate, CPVRTString *pszError)
{
	PVRT_UNREFERENCED_PARAMETER(pContext);

	Destroy();

	m_pAPI = new SPVRTBackgroundAPI;

	if(!m_pAPI)
	{
		if(pszError)
			*pszError = "Error: Insufficient memory to allocate SCPVRTBackgroundAPI.";

		return PVR_FAIL;
	}

	m_pAPI->m_ui32VertexShader = 0;
	m_pAPI->m_ui32FragShader = 0;
	m_pAPI->m_ui32ProgramObject = 0;
	m_pAPI->m_ui32VertexBufferObject = 0;

	bool bResult;
	CPVRTString sTmpErrStr;

	// The shader loading code doesn't expect a null pointer for the error string
	if(!pszError)
		pszError = &sTmpErrStr;

	/* Compiles the shaders. For a more detailed explanation, see IntroducingPVRTools */

	// Try binary shaders first
	bResult = (PVRTShaderLoadBinaryFromMemory(_BackgroundFragShader_fsc, _BackgroundFragShader_fsc_size,
					GL_FRAGMENT_SHADER, GL_SGX_BINARY_IMG, &m_pAPI->m_ui32FragShader, pszError) == PVR_SUCCESS)
		       && (PVRTShaderLoadBinaryFromMemory(_BackgroundVertShader_vsc, _BackgroundVertShader_vsc_size,
					GL_VERTEX_SHADER, GL_SGX_BINARY_IMG, &m_pAPI->m_ui32VertexShader, pszError) == PVR_SUCCESS);
	if(!bResult)
	{
		// if binary shaders don't work, try source shaders
		bResult = (PVRTShaderLoadSourceFromMemory(_BackgroundFragShader_fsh, GL_FRAGMENT_SHADER, &m_pAPI->m_ui32FragShader, pszError) == PVR_SUCCESS) &&
				(PVRTShaderLoadSourceFromMemory(_BackgroundVertShader_vsh, GL_VERTEX_SHADER, &m_pAPI->m_ui32VertexShader, pszError)  == PVR_SUCCESS);
	}

	_ASSERT(bResult);

	if(!bResult)
		return PVR_FAIL;

	// Reset the error string
	if(pszError)
		*pszError = "";

	// Create the shader program
	m_pAPI->m_ui32ProgramObject = glCreateProgram();

	// Attach the fragment and vertex shaders to it
	glAttachShader(m_pAPI->m_ui32ProgramObject, m_pAPI->m_ui32FragShader);
	glAttachShader(m_pAPI->m_ui32ProgramObject, m_pAPI->m_ui32VertexShader);

	// Bind the custom vertex attribute "myVertex" to location VERTEX_ARRAY
	glBindAttribLocation(m_pAPI->m_ui32ProgramObject, VERTEX_ARRAY, "myVertex");

	// Bind the custom vertex attribute "myUV" to location TEXCOORD_ARRAY
	glBindAttribLocation(m_pAPI->m_ui32ProgramObject, TEXCOORD_ARRAY, "myUV");

	// Link the program
	glLinkProgram(m_pAPI->m_ui32ProgramObject);
	GLint Linked;
	glGetProgramiv(m_pAPI->m_ui32ProgramObject, GL_LINK_STATUS, &Linked);
	if (!Linked)
	{
		int i32InfoLogLength, i32CharsWritten;
		glGetProgramiv(m_pAPI->m_ui32ProgramObject, GL_INFO_LOG_LENGTH, &i32InfoLogLength);
		char* pszInfoLog = new char[i32InfoLogLength];
		glGetProgramInfoLog(m_pAPI->m_ui32ProgramObject, i32InfoLogLength, &i32CharsWritten, pszInfoLog);
		*pszError = CPVRTString("Failed to link: ") + pszInfoLog + "\n";
		delete [] pszInfoLog;
		bResult = false;
	}

	_ASSERT(bResult);

	if(!bResult)
		return PVR_FAIL;

	// Use the loaded shader program
	glUseProgram(m_pAPI->m_ui32ProgramObject);

	// Set the sampler2D variable to the first texture unit
	glUniform1i(glGetUniformLocation(m_pAPI->m_ui32ProgramObject, "sampler2d"), 0);

	// Create the vertex buffer object
	GLfloat *pVertexData = 0;

	// The vertex data for non-rotated
	GLfloat afVertexData[16] = { -1, -1, 1, -1, -1, 1, 1, 1,
						0, 0, 1, 0, 0, 1, 1, 1};

	// The vertex data for rotated
	GLfloat afVertexDataRotated[16] = {-1, 1, -1, -1, 1, 1, 1, -1,
						1, 1, 0, 1, 1, 0, 0, 0};

	if(!bRotate)
		pVertexData = &afVertexData[0];
	else
		pVertexData = &afVertexDataRotated[0];

	glGenBuffers(1, &m_pAPI->m_ui32VertexBufferObject);
	glBindBuffer(GL_ARRAY_BUFFER, m_pAPI->m_ui32VertexBufferObject);

	glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 16, pVertexData, GL_STATIC_DRAW);

	glBindBuffer(GL_ARRAY_BUFFER, 0);

	m_bInit = true;

	return PVR_SUCCESS;
}


/*!***************************************************************************
 @Function		Draw
 @Input			ui32Texture	Texture to use
 @Return 		PVR_SUCCESS on success
 @Description	Draws a texture on a quad covering the whole screen.
*****************************************************************************/
EPVRTError CPVRTBackground::Draw(const GLuint ui32Texture)
{
	if(!m_bInit)
		return PVR_FAIL;

	glActiveTexture(GL_TEXTURE0);

	glBindTexture(GL_TEXTURE_2D, ui32Texture);

	glDisable(GL_DEPTH_TEST);
	glDisable(GL_CULL_FACE);

	// Use the loaded shader program
	glUseProgram(m_pAPI->m_ui32ProgramObject);

	// Set vertex data
	glBindBuffer(GL_ARRAY_BUFFER, m_pAPI->m_ui32VertexBufferObject);

	glEnableVertexAttribArray(VERTEX_ARRAY);
	glVertexAttribPointer(VERTEX_ARRAY, 2, GL_FLOAT, GL_FALSE, 0, (const void*) 0);

	// Set texture coordinates
	glEnableVertexAttribArray(TEXCOORD_ARRAY);
	glVertexAttribPointer(TEXCOORD_ARRAY, 2, GL_FLOAT, GL_FALSE, 0, (const void*) (8 * sizeof(float)));

	// Render geometry
	glDrawArrays(GL_TRIANGLE_STRIP,0,4);

	glDisableVertexAttribArray(VERTEX_ARRAY);
	glDisableVertexAttribArray(TEXCOORD_ARRAY);

	glBindBuffer(GL_ARRAY_BUFFER, 0);

	glUseProgram(0);

	return PVR_SUCCESS;
}

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

