/****************************************************************************************/
/*  OglDrv.c                                                                            */
/*                                                                                      */
/*  Author:  Christopher Plymire (chrisjp@eudoramail.com)      						    */
/*																						*/
/*																						*/
/*  Description: 											          				    */
/*                                                                                      */
/*  The contents of this file are subject to the Genesis3D Public License               */
/*  Version 1.01 (the "License"); you may not use this file except in                   */
/*  compliance with the License. You may obtain a copy of the License at                */
/*  http://www.genesis3d.com                                                            */
/*                                                                                      */
/*  Software distributed under the License is distributed on an "AS IS"                 */
/*  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See                */
/*  the License for the specific language governing rights and limitations              */
/*  under the License.                                                                  */
/*                                                                                      */
/*                                                                                      */
/****************************************************************************************/


#include <alloc.h>
#include <memory.h>
#include <stdio.h>
#include <math.h>
#include <GL/gl.h>
#include <stdlib.h>

#include "G3DView.h"
#include "OGLDrv.h"
#include "OglMisc.h"
#include "THandle.h"
#include "Render.h"
#include "PCache.h"

#include "BeSpec.h" // Be Specific include..
#include "JetDirect.h"

int32 LastError;
char LastErrorStr[255];		
GLfloat	CurrentGamma = 1.0f;
DRV_Window	ClientWindow;
DRV_CacheInfo	CacheInfo; // static and extern.. cjp?
JetDirectWindow* pDirectGLWindow = NULL;

GLint maxTextureSize = 0;

GLboolean multitexture = JE_FALSE;

static jeBoolean DRIVERCC DrvGetDeviceCaps(jeDeviceCaps *DeviceCaps)
{
	DeviceCaps->SuggestedDefaultRenderFlags = JE_RENDER_FLAG_BILINEAR_FILTER;
	DeviceCaps->CanChangeRenderFlags = 0xFFFFFFFF;

	return JE_TRUE;
}


DRV_EngineSettings EngineSettings;

extern "C" {

DllExport jeBoolean DriverHook(DRV_Driver **Driver)
{

	printf("OGL : Driver hook called\n");
	EngineSettings.CanSupportFlags = (DRV_SUPPORT_ALPHA | DRV_SUPPORT_COLORKEY);
	EngineSettings.PreferenceFlags = 0;

	OGLDRV.EngineSettings = &EngineSettings;
    
	*Driver = &OGLDRV;

	// Make sure the error string ptr is not null, or invalid!!!
    OGLDRV.LastErrorStr = LastErrorStr;

	SetLastDrvError(DRV_ERROR_NONE, "OGL:  No error.");

	return JE_TRUE;
}

};

DRV_Driver OGLDRV = 
{

	"OpenGL driver. v"DRV_VMAJS"."DRV_VMINS". Copyright 1999, Eclipse Inc.; All Rights Reserved.",

	DRV_VERSION_MAJOR,
	DRV_VERSION_MINOR,

	DRV_ERROR_NONE,
	NULL,
	
	EnumSubDrivers,
	EnumModes,
	
	EnumPixelFormats,

	// NEW IN JET 2.0
	DrvGetDeviceCaps,
	
	DrvInit,
	DrvShutdown,
	DrvResetAll,
	DrvUpdateWindow,
	DrvSetActive,

	THandle_Create,
	THandle_Destroy,

	THandle_Lock,
	THandle_UnLock,

	THandle_SetPalette, 
	THandle_GetPalette, 

	NULL, 
	NULL, 

	THandle_GetInfo,

	BeginScene,
	EndScene,

	PCache_BeginBatch,
	PCache_EndBatch,

	Render_GouraudPoly,
	Render_WorldPoly,
	Render_MiscTexturePoly,

	DrawDecal,

	0,0,0,

	&CacheInfo,

	ScreenShot,

	SetGamma, 
	GetGamma,

	// Driver preferences
	&EngineSettings,

	// The engine supplies these for the drivers misc use
	NULL,
};

jeBoolean DRIVERCC DrvInit(DRV_DriverHook *Hook)
{
	printf("OGLDriver : Init()\n");
	BRect		WRect;
	
	pDirectGLWindow = new JetDirectWindow(Hook->hWnd->Window()->Title() , &BRect(100,100, 420 , 340));
	
	WindowSetup(Hook);
	
	if(Hook->Width == -1 && Hook->Height == -1)
	{
		WRect = pDirectGLWindow->Bounds(); //GetClientRect(Hook->hWnd, &WRect);
		
		Hook->Width = (WRect.right - WRect.left);
		Hook->Height = (WRect.bottom - WRect.top);
	}
	
// This is done in WindowSetup now..	
//	else if(!SetFullscreen(Hook))
//	{
//		return JE_FALSE;
//	} 

//	SetGLPixelFormat(Hook);

	pDirectGLWindow->MakeCurrent();
	
	if(!pDirectGLWindow->IsCurrent())
		printf("Couldn't make window current\n");
	
	ClientWindow.Width = Hook->Width;
	ClientWindow.Height = Hook->Height;
	ClientWindow.hWnd = Hook->hWnd;
			
	printf("ClientWindow.Width = %i ,ClientWindow.Height = %i\n" , ClientWindow.Width , ClientWindow.Height);

	if(ExtensionExists("GL_ARB_multitexture"))
	{
			multitexture = GL_TRUE;
	}
	else 
	{
		multitexture = GL_FALSE;
	} 


	glPixelStorei(GL_PACK_ALIGNMENT, 1);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

	glEnable(GL_TEXTURE_2D);
	glEnable(GL_DEPTH_TEST);

	glEnable(GL_BLEND);    
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	glShadeModel(GL_SMOOTH);
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

	if(multitexture)
	{
		glActiveTextureARB(GL_TEXTURE1_ARB);

		glDisable(GL_TEXTURE_1D);
		glDisable(GL_TEXTURE_2D);		

		glActiveTextureARB(GL_TEXTURE0_ARB);
	} 

//	SetFogEnable(JE_FALSE, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);

	InitMatrices(ClientWindow.Width, ClientWindow.Height);

	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

	glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);

	if (!THandle_Startup())
	{
		SetLastDrvError(DRV_ERROR_GENERIC, "OGL_DrvInit:  THandle_Startup failed...\n");
		return JE_FALSE;
	}

	PCache_InitStaticsAndGlobals();

	glViewport( 0, 0, Hook->Width, Hook->Height );

	pDirectGLWindow->ReleaseCurrent();
	
	pDirectGLWindow->Lock(); // If we change the owner of the window we must lock at the end of init.. they are responsible
							 //    to unlock it
	return JE_TRUE;
}


jeBoolean DRIVERCC DrvShutdown(void)
{
	// Tell OpenGL to finish whatever is in the pipe, because we're closing up shop.
	glFinish();
	
	return JE_TRUE;
}


// Should handle a resize and re-InitMatrices here...
jeBoolean DRIVERCC DrvUpdateWindow(void)
{

	return	JE_TRUE;
}


jeBoolean	DRIVERCC DrvSetActive(jeBoolean Active)
{

	return	JE_TRUE;
}


jeBoolean DRIVERCC SetGamma(float Gamma)
{	
 	GLfloat lut[256];
	GLint	i;
	
	CurrentGamma = Gamma;
	
 	for(i = 0; i < 256; i++)
	{
		lut[i] = (GLfloat)pow(i / 255.0, 1.0 / CurrentGamma);
	}

	glPixelTransferi(GL_MAP_COLOR, GL_TRUE);
	glPixelMapfv(GL_PIXEL_MAP_R_TO_R, 256, lut);
	glPixelMapfv(GL_PIXEL_MAP_G_TO_G, 256, lut);
	glPixelMapfv(GL_PIXEL_MAP_B_TO_B, 256, lut); 

	return JE_TRUE;
}


jeBoolean DRIVERCC GetGamma(float *Gamma)
{
	*Gamma = CurrentGamma;
	
	return JE_TRUE;
}

// For enum sub-drivers and EnumModes we create a temporary DirectGL and then destory it (what other choices do we have!?)


jeBoolean DRIVERCC EnumModes(int32 Driver, char *DriverName, DRV_ENUM_MODES_CB *Cb, 
							 void *Context)
{
	char Description[256];
	GLint modeCount = 0;

	GfxDevice* pDevice = pDirectGLWindow->GetGfxDevice(Driver);
	ModeInfo* pInfo = pDirectGLWindow->GetModeInfo(pDevice,NULL);

	while(pInfo)
	{
	
	if(pInfo->xres != BGL_WINDOWED)
	{
		sprintf(Description, "%dx%dx%d" , pInfo->xres , pInfo->yres, pInfo->color);
		Cb(modeCount,Description, pInfo->xres , pInfo->yres, pInfo->color,Context);
	}
	else
	{
		sprintf(Description, "Windowed Mode");
		Cb(modeCount,Description, -1 , -1, pInfo->color,Context);
	}
		
		pInfo = pDirectGLWindow->GetModeInfo(pDevice,pInfo);
		modeCount++;
	}

	return JE_TRUE;
}


jeBoolean DRIVERCC EnumSubDrivers(DRV_ENUM_DRV_CB *Cb, void *Context)
{	
	// This SHOULD be the first thing called in the DLL, so set up our tempWindow..
if(!pDirectGLWindow)
	pDirectGLWindow = new JetDirectWindow("Temp" , &BRect(10,10,400,400)  );
	
	char driverString[512];
	assert(pDirectGLWindow != NULL);
	GfxDevice* pDevice = NULL;
	
	for(int i = 0 ; i < MAX_DEVICES; i ++)
	{	
		pDevice = pDirectGLWindow->GetGfxDevice(i);
		if(!pDevice)
		{
//			printf("!Device\n");
			break;
		}
			
		sprintf(driverString , "(OpenGL) %s" , pDevice->name);
		
		if(!Cb(i, driverString,  Context))
			continue;
	}
	
	delete pDirectGLWindow;
	pDirectGLWindow = NULL;
	
#if 0
	if(!Cb(0, "OpenGL Driver v"DRV_VMAJS"."DRV_VMINS".", Context))
	{
		return JE_TRUE;
	}
#endif

	return JE_TRUE;
}


// For now, we keep it simple. In the future, we may want to added paletted support 
// (or maybe not)...and also check for OpenGL extentions, like GL_EXT_abgr, GL_EXT_bgra, etc.
// Note: OpenGL is traditionally RGBA based.  ABGR is used here because of endian-oddness in 
// the naming of Genesis's pixel formats.
// See: (Genesis Engine's) Bitmap/pixelformat.h for more information. 
jeRDriver_PixelFormat	PixelFormats[] =
{
//	{JE_PIXELFORMAT_8BIT ,			RDRIVER_PF_3D },
	{JE_PIXELFORMAT_32BIT_ABGR,		RDRIVER_PF_3D | RDRIVER_PF_COMBINE_LIGHTMAP},
	{JE_PIXELFORMAT_24BIT_RGB,		RDRIVER_PF_2D | RDRIVER_PF_CAN_DO_COLORKEY},
	{JE_PIXELFORMAT_24BIT_RGB,		RDRIVER_PF_LIGHTMAP},
//	{JE_PIXELFORMAT_16BIT_1555_ARGB, RDRIVER_PF_3D },
//	{JE_PIXELFORMAT_32BIT_ABGR,		RDRIVER_PF_PALETTE }, // defines the palette that the top pf will use.
};

#define NUM_PIXEL_FORMATS	(sizeof(PixelFormats)/sizeof(jeRDriver_PixelFormat))


jeBoolean DRIVERCC EnumPixelFormats(DRV_ENUM_PFORMAT_CB *Cb, void *Context)
{
	GLint i;

	for(i = 0; i < NUM_PIXEL_FORMATS; i++)
	{
		if(!Cb(&PixelFormats[i], Context))
		{
			return JE_TRUE;
		}
	}

	return JE_TRUE;
}


void SetLastDrvError(int32 Error, char *ErrorStr)
{
	LastError = Error;
	
	if(ErrorStr)
	{
		strcpy(LastErrorStr, ErrorStr);
	}
	else
	{
		LastErrorStr[0] = 0;
	}

	OGLDRV.LastErrorStr = LastErrorStr;
	OGLDRV.LastError = LastError;
}


// I break the rules here.  There's an implied assumption that screenshot will produce
// a BMP, I use TGA instead.  BMP is so...Windows.  This screws up GTest a bit, as GTest looks
// for files ending in .bmp before writing, and will thus keep overwriting one screenshot.
jeBoolean DRIVERCC ScreenShot(const char *Name)
{
	unsigned char tgaHeader[18];
	GLubyte *buffer;
	FILE *fp;
	char *newName;
	int nameLen;

	buffer = (GLubyte *)malloc(sizeof(GLubyte) * ClientWindow.Width * ClientWindow.Height * 3);	

	glFinish();

	glReadPixels(0, 0, ClientWindow.Width, ClientWindow.Height, 
		GL_BGR, GL_UNSIGNED_BYTE, buffer);
 
	memset(tgaHeader, 0, sizeof(tgaHeader));
	tgaHeader[2] = 2;
	tgaHeader[12] = (unsigned char)ClientWindow.Width;
	tgaHeader[13] = (unsigned char)((unsigned long)ClientWindow.Width >> 8);
	tgaHeader[14] = (unsigned char)ClientWindow.Height;
	tgaHeader[15] = (unsigned char)((unsigned long)ClientWindow.Height >> 8);
	tgaHeader[16] = 24;
 
	// Convert the extention (if one exists) to .tga.  They probably expect a .bmp.
	newName = strdup(Name);

	nameLen = strlen(newName);

	if(nameLen > 3)
	{
		if(newName[nameLen - 4] == '.')
		{
			strcpy(newName + nameLen - 3, "tga");
		}
	}

    fp = fopen(newName, "wb");
    
	free(newName);

	if(fp == NULL) 
	{
		free(buffer);
        return JE_FALSE;
    }
 
    fwrite(tgaHeader, 1, 18, fp);
    fwrite(buffer, 3, ClientWindow.Width * ClientWindow.Height, fp);
    fclose(fp);
 
	free(buffer);
	
	return JE_TRUE;
}

