/*
 =======================================================================================
  Name        : OGL_Lock.c
  Author      : Bart     
  Description :
 =======================================================================================
 */



/*
 =======================================================================================
    Lock / unlock backbuffer 
 =======================================================================================
 */

/*
 ----------------------------------------------------------------------------------------
  Description   : Lock (return pointer on virtual screen and pitch value)
 ----------------------------------------------------------------------------------------
 */


#define MAX_TEX_WIDTH 1024 /*256*/
#define MAX_TEX_HEIGHT 512 /*128*/

//unsigned long g_aulTmpBuffer[ MAX_TEX_WIDTH * MAX_TEX_HEIGHT ]; // RGBA 8888
unsigned char g_a3ucTmpBuffer[ 3 * MAX_TEX_WIDTH * MAX_TEX_HEIGHT ]; // RGB 888

unsigned short g_auwBuffer[ MAX_TEX_WIDTH * MAX_TEX_HEIGHT ]; // RGB 565

void* g_p_vVirtualScreen;
long g_lPitch;


void vConvertBuffer888To565_InvY(unsigned short* _puwDst, unsigned char* _pucSrc, long _lWidth, long _lHeight, long _lYOffset)
{
	int Xpos, Ypos;
	unsigned char cR, cG, cB;
	unsigned char* pucSrc;
	unsigned char* pucDst;

	for (Ypos = 0; Ypos<_lHeight-_lYOffset ; Ypos++)
	{
		pucSrc = (unsigned char*)(_pucSrc+3*(_lHeight-_lYOffset-Ypos-1)*_lWidth);
		pucDst = (unsigned char*)(_puwDst+Ypos*_lWidth);
		for (Xpos = 0; Xpos<_lWidth ; Xpos++)
		{
			cR = *pucSrc++;
			cG = *pucSrc++;
			cB = *pucSrc++;

			*pucDst++ = ((cG&0x1C)<<3)+((cB&0xF8)>>3);
			*pucDst++ = (cR&0xF8)+((cG&0xE0)>>5);
		}
	}
	
	memset( _puwDst + (_lHeight-_lYOffset) * _lWidth , 0, _lYOffset*_lWidth*sizeof(unsigned short));
}

void vConvertBuffer565To888(unsigned char* _pucDst, unsigned short* _puwSrc, long _lWidth, long _lHeight)
{
	int Xpos, Ypos;
	unsigned char cR, cG, cB;
	unsigned char* pucSrc;
	unsigned char* pucDst;

	for (Ypos = 0; Ypos<_lHeight ; Ypos++)
	{
		pucSrc = (unsigned char*)(_puwSrc+Ypos*_lWidth);
		pucDst = (unsigned char*)(_pucDst+3*Ypos*_lWidth);
		for (Xpos = 0; Xpos<_lWidth ; Xpos++)
		{
			cB = (*pucSrc & 0x1F)<<3;
			cG = ((*pucSrc & 0xE0)>>3) + ((*(pucSrc+1) & 0x07)<<5);
			cR = (*(pucSrc+1) & 0xF8);
			pucSrc+=2;

			*pucDst++ = cR;
			*pucDst++ = cG;
			*pucDst++ = cB;
		}
	}
}




long fn_lSmallestPowerOfTwoGreaterThan(long lNb, GLint _nMaxSize)
{
	long lRet = 1;

	while (lRet < _nMaxSize)
	{
		if (lNb <= lRet)
			return lRet;
		lRet <<= 1;
	}

	return _nMaxSize;
}


void fn_OGL_vDisplayTexture(long _lWScreen, long _lHScreen, long _lWTexture, long _lHTexture)
{
	float fWRatio, fHRatio;
	float fWTextureCoord, fHTextureCoord, fWVertexCoord, fHVertexCoord;
	float fCoordLeft, fCoordRight, fCoordTop, fCoordBottom;

	fWRatio = (float)_lWScreen / (float)_lWTexture;
	fHRatio = (float)_lHScreen / (float)_lHTexture;

	if (fWRatio < 1) {
		fWTextureCoord = fWRatio;
		fWVertexCoord = 1;
	} else {
		fWTextureCoord = 1;
		fWVertexCoord = 1/fWRatio;
	}
	
	if (fHRatio < 1) {
		fHTextureCoord = fHRatio;
		fHVertexCoord = 1;
	} else {
		fHTextureCoord = 1;
		fHVertexCoord = 1/fHRatio;
	}

	fCoordLeft = -1;
	fCoordRight = -1 + 2*fWVertexCoord;
	fCoordTop = -(-1);
	fCoordBottom = -(-1 + 2*fHVertexCoord);

	M_OGL_ASSERT( glEnable( GL_TEXTURE_2D); )
	M_OGL_ASSERT( glDisable( GL_BLEND); )
	M_OGL_ASSERT( glDisable(GL_DEPTH_TEST); )

	M_OGL_ASSERT( glMatrixMode(GL_MODELVIEW); )
	M_OGL_ASSERT( glPushMatrix(); )
	M_OGL_ASSERT( glLoadIdentity(); )

	//M_OGL_ASSERT( glRasterPos2f(-1.0f, -1.0f); )

	M_OGL_ASSERT_FAKE( glBegin (GL_QUADS); )

	M_OGL_ASSERT_FAKE( glColor4f( 1, 1, 1, 1); )
	M_OGL_ASSERT_FAKE( glTexCoord2f(0, /*1*/fHTextureCoord);  )
	M_OGL_ASSERT_FAKE( glVertex3f(fCoordLeft, fCoordBottom, 0);  )

	M_OGL_ASSERT_FAKE( glTexCoord2f(0, /*1-fHTextureCoord*/0);  )
	M_OGL_ASSERT_FAKE( glVertex3f(fCoordLeft, fCoordTop, 0);  )

	M_OGL_ASSERT_FAKE( glTexCoord2f(fWTextureCoord, /*1-fHTextureCoord*/0);  )
	M_OGL_ASSERT_FAKE( glVertex3f(fCoordRight, fCoordTop, 0);  )

	M_OGL_ASSERT_FAKE( glTexCoord2f(fWTextureCoord, /*1*/fHTextureCoord);  )
	M_OGL_ASSERT_FAKE( glVertex3f(fCoordRight, fCoordBottom, 0);  )

	M_OGL_ASSERT( glEnd(); )
	M_OGL_ASSERT( glPopMatrix(); )

	M_OGL_ASSERT( glEnable(GL_DEPTH_TEST); )
	M_OGL_ASSERT( glEnable(GL_BLEND); )
}

void fn_OGL_vDisplayTextureWithCoord(long _lWScreen, long _lHScreen, long _lWTexture, long _lHTexture, long _lCoordLeft, long _lCoordTop, long _lCoordRight, long _lCoordBottom)
{
	float fWRatio, fHRatio;

	fWRatio = (float)_lWScreen / (float)_lWTexture;
	fHRatio = (float)_lHScreen / (float)_lHTexture;

	assert (fWRatio < 1);
	assert (fHRatio < 1);

	M_OGL_ASSERT( glEnable( GL_TEXTURE_2D); )

	M_OGL_ASSERT( glDisable( GL_BLEND); )
	M_OGL_ASSERT( glDisable(GL_DEPTH_TEST); )

	M_OGL_ASSERT( glMatrixMode(GL_MODELVIEW); )

	/*	
	M_OGL_ASSERT( glPushMatrix(); )
	M_OGL_ASSERT( glLoadIdentity(); )
	M_OGL_ASSERT( glOrtho(0, (GLdouble)(_lCoordRight - _lCoordLeft), (GLdouble)(_lCoordBottom - _lCoordTop), 0, 0, -0.5); )
*/
	M_OGL_ASSERT_FAKE( glBegin (GL_QUADS); )

	M_OGL_ASSERT_FAKE( glColor4f( 1, 1, 1, 1); )
	M_OGL_ASSERT_FAKE( glTexCoord2f(0, fHRatio);  )
	M_OGL_ASSERT_FAKE( glVertex3f((float)_lCoordLeft, (float)_lCoordBottom, VIG_ZPOS);  )

	M_OGL_ASSERT_FAKE( glTexCoord2f(0, 0);  )
	M_OGL_ASSERT_FAKE( glVertex3f((float)_lCoordLeft, (float)_lCoordTop, VIG_ZPOS);  )

	M_OGL_ASSERT_FAKE( glTexCoord2f(fWRatio, 0);  )
	M_OGL_ASSERT_FAKE( glVertex3f((float)_lCoordRight, (float)_lCoordTop, VIG_ZPOS);  )

	M_OGL_ASSERT_FAKE( glTexCoord2f(fWRatio, fHRatio);  )
	M_OGL_ASSERT_FAKE( glVertex3f((float)_lCoordRight, (float)_lCoordBottom, VIG_ZPOS);  )

	M_OGL_ASSERT( glEnd(); )
	//M_OGL_ASSERT( glPopMatrix(); )

    M_OGL_ASSERT( glEnable(GL_DEPTH_TEST); )	// Hidden surface removal
	M_OGL_ASSERT( glEnable(GL_BLEND); )
}






 
long g_lScreenWidth, g_lScreenHeight, g_lTextureWidth, g_lTextureHeight;

void fn_OGLThread_vLockDevice(void)
{
	GLuint uiTextureId;
	RECT rcScreenRect;
	GLint nMaxSize;
	long lYPos;


    GetClientRect( gs_hWnd, &rcScreenRect );
	g_lScreenWidth = rcScreenRect.right - rcScreenRect.left;
	g_lScreenHeight = rcScreenRect.bottom - rcScreenRect.top;

	glGetIntegerv(GL_MAX_TEXTURE_SIZE, &nMaxSize);
	g_lTextureWidth = fn_lSmallestPowerOfTwoGreaterThan(g_lScreenWidth, (nMaxSize>MAX_TEX_WIDTH) ? MAX_TEX_WIDTH : nMaxSize);
	g_lTextureHeight = fn_lSmallestPowerOfTwoGreaterThan(g_lScreenHeight, (nMaxSize>MAX_TEX_HEIGHT) ? MAX_TEX_HEIGHT : nMaxSize);

		//M_OGL_ASSERT( glEnable(GL_TEXTURE_2D); ) // don't need ? ! ! !

	lYPos = g_lScreenHeight-g_lTextureHeight;

	M_OGL_ASSERT( glGenTextures(1, &uiTextureId ); ) // to prevent from destroying an existing texture
	M_OGL_ASSERT( glBindTexture(GL_TEXTURE_2D, uiTextureId);  )

		M_OGL_ASSERT( glCopyTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA/*GL_RGB5_A1*/, 0, (lYPos<0) ? 0 : lYPos, g_lTextureWidth, g_lTextureHeight, 0 ); )
		//M_OGL_ASSERT( glCopyTexImage2D( GL_TEXTURE_2D, 0, GL_RGB/*GL_RGB5_A1*/, 0, lYPos, g_lTextureWidth, g_lTextureHeight, 0 ); )
 
		//M_OGL_ASSERT( glGetTexImage( GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, g_aulTmpBuffer ); )
		M_OGL_ASSERT( glGetTexImage( GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, g_a3ucTmpBuffer ); )

	M_OGL_ASSERT( glDeleteTextures(1, &uiTextureId); )

	//vConvertBuffer8888To565_InvY(g_auwBuffer, g_aulTmpBuffer, g_lTextureWidth, g_lTextureHeight, (lYPos<0) ? -lYPos : 0 );
	vConvertBuffer888To565_InvY(g_auwBuffer, g_a3ucTmpBuffer, g_lTextureWidth, g_lTextureHeight, (lYPos<0) ? -lYPos : 0 );

	g_p_vVirtualScreen = g_auwBuffer;
	g_lPitch = g_lTextureWidth*2;
}

void fn_OGLThread_vUnlockDevice(void)
{
	GLuint uiTextureId;

	//vConvertBuffer565To8888(g_aulTmpBuffer, g_auwBuffer, g_lTextureWidth, g_lTextureHeight);
	vConvertBuffer565To888(g_a3ucTmpBuffer, g_auwBuffer, g_lTextureWidth, g_lTextureHeight);

		//M_OGL_ASSERT( glEnable(GL_TEXTURE_2D); ) // don't need ? ! ! !

	M_OGL_ASSERT( glGenTextures(1, &uiTextureId ); )

	M_OGL_ASSERT( glBindTexture(GL_TEXTURE_2D, uiTextureId);  )

	M_OGL_ASSERT( glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S, GL_CLAMP/*GL_REPEAT*/); )
	M_OGL_ASSERT( glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_CLAMP/*GL_REPEAT*/); )
	M_OGL_ASSERT( glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_LINEAR); )
	M_OGL_ASSERT( glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_LINEAR); )

		//M_OGL_ASSERT( glTexImage2D(GL_TEXTURE_2D, 0, 4/*GL_RGB5_A1*/, g_lTextureWidth, g_lTextureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, g_aulTmpBuffer); )
		M_OGL_ASSERT( glTexImage2D(GL_TEXTURE_2D, 0, 3/*GL_RGB5_A1*/, g_lTextureWidth, g_lTextureHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, g_a3ucTmpBuffer); )


	// display the texture all over the ScreenRect :
	M_OGL_ASSERT( glBindTexture(GL_TEXTURE_2D, uiTextureId);  )
		fn_OGL_vDisplayTexture(g_lScreenWidth, g_lScreenHeight, g_lTextureWidth, g_lTextureHeight);
	M_OGL_ASSERT( glDeleteTextures(1, &uiTextureId); )

}











/*
void vCopyBufferToTexture8888(unsigned long* _pulDst, unsigned long* _pulSrc, long _lWDst, long _lHDst, long _lWSrc, long _lHSrc)
{
	long lYPos;
	if ( (_lWSrc > _lWDst) || (_lHSrc > _lHDst) )
		return;

	for (lYPos = 0 ; lYPos<_lHSrc ; lYPos++) {
		memcpy(_pulDst + lYPos*_lWDst, _pulSrc+lYPos*_lWSrc, _lWSrc*sizeof(unsigned long));
		memset(_pulDst + lYPos*_lWDst + _lWSrc, 0, (_lWDst-_lWSrc)*sizeof(unsigned long));
	}

	//for (lYPos = _lHSrc ; lYPos<_lHDst ; lYPos++)
	//	memset(_pulDst + lYPos*_lWDst, 0, _lWDst*sizeof(unsigned long));
	memset(_pulDst + _lHSrc*_lWDst, 0, (_lHDst-_lHSrc)*_lWDst*sizeof(unsigned long));
}
*/
void vCopyBufferToTexture16b(unsigned short* _pulDst, unsigned short* _pulSrc, long _lWDst, long _lHDst, long _lWSrc, long _lHSrc)
{
	long lYPos;
	if ( (_lWSrc > _lWDst) || (_lHSrc > _lHDst) )
		return;

	for (lYPos = 0 ; lYPos<_lHSrc ; lYPos++) {
		memcpy(_pulDst + lYPos*_lWDst, _pulSrc+lYPos*_lWSrc, _lWSrc*sizeof(unsigned short));
		memset(_pulDst + lYPos*_lWDst + _lWSrc, 0, (_lWDst-_lWSrc)*sizeof(unsigned short));
	}

	//for (lYPos = _lHSrc ; lYPos<_lHDst ; lYPos++)
	//	memset(_pulDst + lYPos*_lWDst, 0, _lWDst*sizeof(unsigned short));
	memset(_pulDst + _lHSrc*_lWDst, 0, (_lHDst-_lHSrc)*_lWDst*sizeof(unsigned short));
}

void vStretchBufferToTexture8888(unsigned long* _pulDst, unsigned long* _pulSrc, long _lWDst, long _lHDst, long _lWSrc, long _lHSrc)
{
	long lXPos, lYPos;
	long lXStep, lYStep;

	lXStep = 1;
	lYStep = 1;
	while (_lWSrc > _lWDst) {
		lXStep <<= 1; // *2
		_lWSrc >>= 1; // /2
	}
	while (_lHSrc > _lHDst) {
		lYStep <<= 1; // *2
		_lHSrc >>= 1; // /2
	}

	for (lYPos = 0 ; lYPos<_lHSrc ; lYPos+=lYStep) {
		//memcpy(_pulDst + lYPos*_lWDst, _pulSrc+lYPos*_lWSrc, _lWSrc*sizeof(unsigned long));
		for (lXPos = 0 ; lXPos<_lWSrc ; lXPos+=lXStep)
			*(_pulDst + lYPos*_lWDst + lXPos) = *(_pulSrc + lYPos*_lWSrc + lXPos);
		memset(_pulDst + lYPos*_lWDst + _lWSrc, 0, (_lWDst-_lWSrc)*sizeof(unsigned long));
	}

	//for (lYPos = _lHSrc ; lYPos<_lHDst ; lYPos++)
	//	memset(_pulDst + lYPos*_lWDst, 0, _lWDst*sizeof(unsigned long));
	memset(_pulDst + _lHSrc*_lWDst, 0, (_lHDst-_lHSrc)*_lWDst*sizeof(unsigned long));
}


extern void *g_p_vSourceBuffer;
extern RECT g_DestBitmapRect;

void fn_OGLThread_vWrite16bBitmapToBackBuffer(long _lWidth, long _lHeight)
{
	GLint nMaxSize;
	long lTextureWidth, lTextureHeight;
	GLuint uiTextureId;

	glGetIntegerv(GL_MAX_TEXTURE_SIZE, &nMaxSize);
	lTextureWidth = fn_lSmallestPowerOfTwoGreaterThan(_lWidth, (nMaxSize>MAX_TEX_WIDTH) ? MAX_TEX_WIDTH : nMaxSize);
	lTextureHeight = fn_lSmallestPowerOfTwoGreaterThan(_lHeight, (nMaxSize>MAX_TEX_HEIGHT) ? MAX_TEX_HEIGHT : nMaxSize);

	vCopyBufferToTexture16b(g_auwBuffer, (unsigned short*)g_p_vSourceBuffer, lTextureWidth, lTextureHeight, _lWidth, _lHeight);
	//vConvertBuffer565To8888(g_aulTmpBuffer, g_auwBuffer, lTextureWidth, lTextureHeight);
	vConvertBuffer565To888(g_a3ucTmpBuffer, g_auwBuffer, lTextureWidth, lTextureHeight);

		//M_OGL_ASSERT( glEnable(GL_TEXTURE_2D); ) // don't need ? ! ! !

	M_OGL_ASSERT( glGenTextures(1, &uiTextureId ); )

	M_OGL_ASSERT( glBindTexture(GL_TEXTURE_2D, uiTextureId);  )

	M_OGL_ASSERT( glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S, GL_CLAMP/*GL_REPEAT*/); )
	M_OGL_ASSERT( glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_CLAMP/*GL_REPEAT*/); )
	M_OGL_ASSERT( glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_LINEAR); )
	M_OGL_ASSERT( glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_LINEAR); )

		//M_OGL_ASSERT( glTexImage2D(GL_TEXTURE_2D, 0, 4, lTextureWidth, lTextureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, g_aulTmpBuffer); )
		M_OGL_ASSERT( glTexImage2D(GL_TEXTURE_2D, 0, 3, lTextureWidth, lTextureHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, g_a3ucTmpBuffer); )

	M_OGL_ASSERT( glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); )

	// display the texture all over the ScreenRect :
	M_OGL_ASSERT( glBindTexture(GL_TEXTURE_2D, uiTextureId);  )
	//M_OGL_ASSERT( glDisable(GL_CULL_FACE); )
		fn_OGL_vDisplayTextureWithCoord(_lWidth, _lHeight, lTextureWidth, lTextureHeight, g_DestBitmapRect.left, g_DestBitmapRect.top, g_DestBitmapRect.right, g_DestBitmapRect.bottom);
		//fn_OGL_vDisplayTexture(_lWidth, _lHeight, lTextureWidth, lTextureHeight);

	M_OGL_ASSERT( glDeleteTextures(1, &uiTextureId); )

}
