#include "stdafx.h"
#include "basic.h"
#include "Textures.h"
#include "Eroc.h"
#include "assert.h"
#include "OGLState.h"

#ifdef import_globals
#undef import_globals
#endif
#include "globals.h"

// Local screen

//SDL_Surface* gBitmaps[eMaxBitMaps];
Texture gTextures[eMaxBitMaps];
int gNrBitmaps = 0;

/////////////////////////////////////////////////////////////////////////////

// Load an image, convert it to 32-bit RGB and return a pointer to the surface.
// Caller takes ownership
SDL_Surface *SDLLoadImage(char *inFilename)
{
	SDL_Surface *TempSurface;
	SDL_Surface *TextureImage = 0;	// Create Storage Space For The Texture

	// Load The Bitmap, Check For Errors, If Bitmap's Not Found Quit
	if (TempSurface=IMG_Load(inFilename))
	{		
		// Convert to RGB
		SDL_PixelFormat theFormat;
		memset(&theFormat,0,sizeof(SDL_PixelFormat));
		
		// this seems to be the OpenGLs preferred pixelformat
		theFormat.BitsPerPixel = 32;
		theFormat.BytesPerPixel = 4;
		theFormat.Rmask = 0x000000FF;
		theFormat.Gmask = 0x0000FF00;
		theFormat.Bmask = 0x00FF0000;
		theFormat.Amask = 0xFF000000;
		theFormat.Rshift = 0;
		theFormat.Gshift = 8;
		theFormat.Bshift = 16;
		theFormat.Ashift = 24;
	

		TextureImage = SDL_ConvertSurface(TempSurface, &theFormat, SDL_SWSURFACE);

		SDL_FreeSurface(TempSurface);						// Free The Image Structure

		// Check to see if we have an alpha channel image
		SDL_Surface *AlphaTemp;
		char theAlphaName[256];
		int i = -1;
		for (; inFilename[++i] != '.'; theAlphaName[i] = inFilename[i]);
		theAlphaName[i]= 'a';
		for (; inFilename[i++]; theAlphaName[i] = inFilename[i-1]);
		theAlphaName[i]= 0;
		
		if (AlphaTemp=IMG_Load(theAlphaName))
		{
			if (TextureImage->h == AlphaTemp->h &&
				TextureImage->w == AlphaTemp->w)
			{
				// convert the alpha to a format we know
				SDL_Surface *AlphaSurface = SDL_ConvertSurface(AlphaTemp, &theFormat, SDL_SWSURFACE);
				
				unsigned char* theDest = (unsigned char*)TextureImage->pixels;	
				unsigned char* theSource = (unsigned char*)AlphaSurface->pixels;			

				for (int y = 0; y < TextureImage->h; y++)
					for (int x = 0; x < TextureImage->w; x++)
					{
						*(theDest + (y* TextureImage->w + x)*4 + 3) = *(theSource + (y* TextureImage->w + x)*4 );		
					}
				SDL_FreeSurface(AlphaSurface);			
			}

			SDL_FreeSurface(AlphaTemp);
		}		
	}
	 
	return TextureImage;
}

// Make a power-of-2 texture out a 24-bit RGB SDL-surface, owns SDL Surface
int LoadGLTexture(SDL_Surface* inSurface, Texture& ioTex, bool inNeedSurface)
{		
	glDeleteTextures(1, &ioTex.mOpenGLTex);
	glGenTextures(1, &ioTex.mOpenGLTex);		

	// get the power-of-2 sizes
	int Width = 1, Height = 1;
	while (Width < inSurface->w)
		Width *= 2;
	while (Height < inSurface->h)
		Height *= 2;
	ioTex.mScaleX = (float)inSurface->w/Width;
	ioTex.mScaleY = (float)inSurface->h/Height;
	ioTex.mSizeX = inSurface->w;
	ioTex.mSizeY = inSurface->h;

	// Copy to a temporary surface with the right dimensions
	SDL_Surface * TempSurf = SDL_CreateRGBSurface(SDL_SWSURFACE, Width, Height, 32,
		0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);	

	for (int y = 0; y < Height; y++)
	{
		unsigned long* theDestTemp = (unsigned long*)TempSurf->pixels + y* Width;

		if (y < inSurface->h)
		{	// party copy, part fill
			int x;
			unsigned long* theTempSource = (unsigned long*)inSurface->pixels + y* inSurface->w;
			for (x = 0; x < inSurface->w; x++)			
				*(theDestTemp + x) = *( theTempSource + x);					
			for (; x < Width; x++)
				*(theDestTemp + x) = 0;
		}
		else
		{	// complete fill
			for (int x = 0; x < Width; x++)			
				*(theDestTemp + x) = 0;					
		}
	}
	
	// Build The Textures
	glBindTexture(GL_TEXTURE_2D, ioTex.mOpenGLTex);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);
	glTexImage2D(GL_TEXTURE_2D, 0, 4, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, TempSurf->pixels);
	SDL_FreeSurface(TempSurf);

	// only store surface if needed
	if ( inNeedSurface)
		ioTex.mSurface = inSurface;
	else
	{
		SDL_FreeSurface(inSurface);
		ioTex.mSurface = 0;
	}

	return TRUE;
}

// Create a GLTexture from ABGR memory
int CreateTextureFromMem(unsigned int* inABGRmem, int inSizeX, int inSizeY, bool inNeedSurface)
{
	assert(gNrBitmaps < eMaxBitMaps);

	// copy "name"
	strcpy(gTextures[gNrBitmaps].mName, "FromMemory");

	// Create Storage Space For The Texture
	SDL_Surface* theSurf = SDL_CreateRGBSurface(0, inSizeX, inSizeY, 32, 
								0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);
	unsigned int* theDest = (unsigned int*)theSurf->pixels;

	for (int i = 0; i < inSizeX * inSizeY; i++)
		theDest[i] = inABGRmem[i];
							
	LoadGLTexture(theSurf, gTextures[gNrBitmaps], inNeedSurface);

	return gNrBitmaps++;
}

// Replace an existing GLtexture with a new one from ABGR memory
int ReplaceTextureFromMem(int inTextureNr, unsigned int* inABGRmem, int inSizeX,
						  int inSizeY, bool inNeedSurface)
{
	assert(inTextureNr < gNrBitmaps);

	// release previous texture
	glDeleteTextures(1, &(gTextures[inTextureNr].mOpenGLTex));
	if (gTextures[inTextureNr].mSurface)
		SDL_FreeSurface(gTextures[inTextureNr].mSurface);
		
	// copy "name"
	strcpy(gTextures[inTextureNr].mName, "FromMemory");

	// Create Storage Space For The Texture
	SDL_Surface* theSurf = SDL_CreateRGBSurface(0, inSizeX, inSizeY, 32, 
								0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);
	unsigned int* theDest = (unsigned int*)theSurf->pixels;

	for (int i = 0; i < inSizeX * inSizeY; i++)
		theDest[i] = inABGRmem[i];
							
	LoadGLTexture(theSurf, gTextures[inTextureNr], inNeedSurface);

	return inTextureNr;
}


int CreateTextureFromFile(char *inFilename, bool inNeedSurface)
{
	assert(gNrBitmaps < eMaxBitMaps);

	// check if it didn't already exist
	for (int i = 0; i < gNrBitmaps; i++)
	{
		if (strcmp(gTextures[i].mName, inFilename) == 0)		
		{
			// check for surface.
			if (inNeedSurface && ! gTextures[i].mSurface)
				gTextures[i].mSurface = SDLLoadImage(inFilename);
			return i;
		}		
	}

	// copy name
	strcpy(gTextures[gNrBitmaps].mName, inFilename);
	
	SDL_Surface* theSurf = SDLLoadImage(inFilename);	
	LoadGLTexture(theSurf, gTextures[gNrBitmaps], inNeedSurface);
	
	return gNrBitmaps++;
}

void FreeImages()
{
	// Clean up our bitmaps
	for (int i = 0; i < gNrBitmaps; i++)
	{	
		glDeleteTextures(1, &(gTextures[i].mOpenGLTex));
		if (gTextures[i].mSurface)
			SDL_FreeSurface(gTextures[i].mSurface);
		gTextures[i].mSurface = 0;
		gTextures[i].mName[0] = 0;
	}
	gNrBitmaps = 0;
}

// Get a loaded texture
Texture* GetTexture(int inImageNr) // no ownership!
{
	return &(gTextures[inImageNr]);
}

// Get a loaded surface
SDL_Surface* GetSDLSurface(int inSurfaceNr) // no ownership!
{
	assert(gTextures[inSurfaceNr].mSurface); 
	return gTextures[inSurfaceNr].mSurface;
}


/////////////////////////////////////////////////////////////////////////////

static Texture RenderTexture;
static HPBUFFERARB hbuf = 0; // the pixel buffer
static HDC theWindowDC = 0; // the windows device context
static HDC hpbufdc = 0;		 // the pbuffer device context
static HGLRC theWindowRC = 0; // the windows render context
static HGLRC hpbufglrc= 0;	 // the pbuffer render context
static bool PBufferExists = false;
static bool BoundToTexture = false;

bool CreatePBuffer(int inSizeX, int inSizeY)
{
	if (PBufferExists)
		return false;
	strcpy(RenderTexture.mName, "RenderToTexture");

	glDeleteTextures(1, &RenderTexture.mOpenGLTex);
	glGenTextures(1, &RenderTexture.mOpenGLTex);		

	// get the power-of-2 sizes
	int Width = 1, Height = 1;
	while (Width < inSizeX)
		Width *= 2;
	while (Height < inSizeY)
		Height *= 2;
	RenderTexture.mScaleX = (float)inSizeX/Width;
	RenderTexture.mScaleY = (float)inSizeY/Height;
	RenderTexture.mSizeX = inSizeX;
	RenderTexture.mSizeY = inSizeY;
	
	// Build The Textures
	OGLState::SetTextureUsed( RenderTexture.mOpenGLTex);

	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR_MIPMAP_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);

	// Create the Pbuffer
	theWindowDC = wglGetCurrentDC();
	theWindowRC = wglGetCurrentContext();

	float fAttributes[] = {0, 0};
	int pixelFormat;
	UINT numFormats;

	int iAttributes[] = {
		WGL_SUPPORT_OPENGL_ARB,GL_TRUE,
		WGL_ACCELERATION_ARB,WGL_FULL_ACCELERATION_ARB,
		WGL_DOUBLE_BUFFER_ARB, GL_FALSE,
		WGL_COLOR_BITS_ARB,24,
		WGL_ALPHA_BITS_ARB,8,
		WGL_DEPTH_BITS_ARB,16,
		WGL_DRAW_TO_PBUFFER_ARB, GL_TRUE,
		WGL_BIND_TO_TEXTURE_RGBA_ARB, GL_TRUE,
		0,0};

	int theResult = wglChoosePixelFormatARB(theWindowDC, iAttributes, fAttributes,
			1, &pixelFormat, &numFormats);

	if ( theResult == GL_TRUE && numFormats == 1)
	{
		int iAttributes[] = {
			WGL_TEXTURE_FORMAT_ARB, WGL_TEXTURE_RGBA_ARB,
			WGL_TEXTURE_TARGET_ARB, WGL_TEXTURE_2D_ARB,
			WGL_MIPMAP_TEXTURE_ARB, 0,
			WGL_PBUFFER_LARGEST_ARB, 0,
			0, 0};
		hbuf = wglCreatePbufferARB(theWindowDC, pixelFormat, Width, Height, iAttributes );

		int theError = GetLastError();	

		assert(hbuf != 0);

		hpbufdc = wglGetPbufferDCARB( hbuf );

		hpbufglrc = wglCreateContext( hpbufdc );

		bool theResult = wglShareLists(theWindowRC, hpbufglrc);

		int PBufWidth, PBufHeight;
		wglQueryPbufferARB( hbuf, WGL_PBUFFER_WIDTH_ARB, &PBufWidth );
		wglQueryPbufferARB( hbuf, WGL_PBUFFER_HEIGHT_ARB, &PBufHeight );

		assert(PBufWidth == Width);
		assert(PBufHeight == Height);

		PBufferExists = true;
		return true;
	}
	return false;
}

void ReleasePBuffer()
{
	// first unbind from texture, if any
	if (BoundToTexture)
	{
		// unbind pbuffer from texture
		OGLState::SetTextureUsed( RenderTexture.mOpenGLTex);
		int theResult = wglReleaseTexImageARB ( hbuf, WGL_FRONT_LEFT_ARB);
		BoundToTexture = false;
	}
	// release texture
	glDeleteTextures(1, &(RenderTexture.mOpenGLTex));
	RenderTexture.mOpenGLTex = 0;

	// release Pbuffer etc
	PBufferExists = false;
	wglDeleteContext( hpbufglrc );
	hpbufglrc = 0;
	wglReleasePbufferDCARB( hbuf, hpbufdc );
	hpbufdc = 0;
	wglDestroyPbufferARB( hbuf );
	hbuf = 0;
}

void StartRenderToTexture()
{
	if (BoundToTexture)
	{
		// unbind pbuffer from texture
		OGLState::SetTextureUsed( RenderTexture.mOpenGLTex);
		int theResult = wglReleaseTexImageARB ( hbuf, WGL_FRONT_LEFT_ARB);
		BoundToTexture = false;
	}

	// render to buffer
	wglMakeCurrent( hpbufdc, hpbufglrc );
}

// No ownership!
const Texture* EndRenderToTexture()
{
	// render back to screen
	wglMakeCurrent( theWindowDC, theWindowRC );

	// bind pBuffer to texture
	OGLState::SetTextureUsed( RenderTexture.mOpenGLTex);
	int theResult = wglBindTexImageARB ( hbuf, WGL_FRONT_LEFT_ARB );

	return &RenderTexture;
}

/////////////////////////////////////////////////////////////////////////////

// get a 4-bit plane of a texture, shifted to the 0-position, and with alpha 0
int LoadGLTextureBitPlane(SDL_Surface* inSurface, int inPlane, Texture& ioTex ) // no change of ownership
{			
	if (inPlane < 0) inPlane = 0;
	else if (inPlane > 0) inPlane = 4;

	glDeleteTextures(1, &ioTex.mOpenGLTex);
	glGenTextures(1, &ioTex.mOpenGLTex);		

	// get the power-of-2 sizes
	int Width = 1, Height = 1;
	while (Width < inSurface->w)
		Width *= 2;
	while (Height < inSurface->h)
		Height *= 2;
	ioTex.mScaleX = (float)inSurface->w/Width;
	ioTex.mScaleY = (float)inSurface->h/Height;
	ioTex.mSizeX = inSurface->w;
	ioTex.mSizeY = inSurface->h;

	// Copy to a temporary surface with the right dimensions
	SDL_Surface * TempSurf = SDL_CreateRGBSurface(SDL_SWSURFACE, Width, Height, 32,
		0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);	

	for (int y = 0; y < Height; y++)
	{
		unsigned long* theDestTemp = (unsigned long*)TempSurf->pixels + y* Width;

		if (y < inSurface->h)
		{	// party copy, part fill
			int x;
			unsigned long* theTempSource = (unsigned long*)inSurface->pixels + y* inSurface->w;
			for (x = 0; x < inSurface->w; x++)			
				*(theDestTemp + x) = ((*( theTempSource + x)) >> inPlane ) & 0x0F0F0F0F;
			for (; x < Width; x++)
				*(theDestTemp + x) = 0;
		}
		else
		{	// complete fill
			for (int x = 0; x < Width; x++)			
				*(theDestTemp + x) = 0;					
		}
	}
	
	// Build The Textures
	glBindTexture(GL_TEXTURE_2D, ioTex.mOpenGLTex);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);
	glTexImage2D(GL_TEXTURE_2D, 0, 4, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, TempSurf->pixels);
	SDL_FreeSurface(TempSurf);

	return TRUE;
}

