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

/*  DIBDisplay.C                                                                        */

/*                                                                                      */

/*  Author:  Mike Sandige                                                               */

/*  Description:  display surface manager for windows with a DIB as the frame buffer    */

/*                Code fragments contributed by John Miles                              */

/*                                                                                      */

/*  The contents of this file are subject to the Jet3D Public License                   */

/*  Version 1.02 (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.jet3d.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.                                                                  */

/*                                                                                      */

/*  The Original Code is Jet3D, released December 12, 1999.                             */

/*  Copyright (C) 1996-1999 Eclipse Entertainment, L.L.C. All Rights Reserved           */

/*                                                                                      */

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



#include <Window.h>

#include "G3DView.h"

#include <OS.h>

#define OutputDebugString debugger





#include <debugger.h>

#include <memory.h>

#include <Bitmap.h>

#include <Screen.h>



#include "DIBDisplay.h"

#include "CPUInfo.h"

#include <assert.h>

#include <stdio.h>			// vsprintf()

#include <malloc.h>			// malloc(),free()

#include "Errorlog.h"



#ifdef DO_TIME_TEST

#include <StopWatch.h>

#endif



#define DIBDISPLAY_DESCRIPTION_STRING "Software (Window)"



typedef struct

{

   uint32 r;

   uint32 g;

   uint32 b;

   int32 alpha;

}

DIBDisplay_RGB32;





	// data needed for DIB section (and some cached info about bitmasks

typedef struct

{

	uint32		DIB_R_bitmask;			// DIB high-color pixel format

	uint32		DIB_G_bitmask;

	uint32		DIB_B_bitmask;



//	BITMAPINFO  *pbmi;					// BITMAPINFO structure

//	HBITMAP		hDIB;					// Handle returned from CreateDIBSection



	uint8		*lpDIBBuffer;			// DIB image buffer

	BBitmap		*backBuffer;

	

} DIBDisplay_DIBInfo;	







typedef struct DIBDisplay 

{

	G3DView*			hWnd;					// Handle to application window

	jeBoolean			Locked;					// is display 'locked'

	int32				BitsPerPixel;			// Mode info set by last call to 

	int32				Pitch;					// (BitsPerPixel/8) * Size_X

	int32				Size_X;					// size of window rounded up to the nearest multiple of 4

	int32				Size_Y;

	uint32				Flags;					// display flags (currently unused)

	DIBDisplay_DIBInfo	DIBInfo;

} DIBDisplay;





void DIBDisplay_GetDisplayFormat(	const DIBDisplay *D,

									int32   *Width, 

									int32   *Height,

									int32   *BitsPerPixel,

									uint32  *Flags)

{

	assert( D            != NULL );

	assert( Width        != NULL );

	assert( Height       != NULL );

	assert( BitsPerPixel != NULL );

	assert( Flags        != NULL );



	*Width        = D->Size_X;

	*Height       = D->Size_Y;

	*BitsPerPixel = D->BitsPerPixel;

	*Flags        = D->Flags;

	

}	





jeBoolean DIBDisplay_GetDisplayInfo(	char			*DescriptionString, 

										unsigned int	 DescriptionStringMaxLength,

										DisplayModeInfo *Info)

{

	int BitsPerPixel;



	assert( Info != NULL );

	assert( DescriptionString != NULL );

	assert( DescriptionStringMaxLength > 0 );

	if (strlen(DIBDISPLAY_DESCRIPTION_STRING) >= DescriptionStringMaxLength)

		{

			jeErrorLog_AddString(JE_ERR_BAD_PARAMETER,"DIBDisplay_GetDescriptionString: description string too short",NULL);

			return JE_FALSE;

		}



	strcpy(DescriptionString,DIBDISPLAY_DESCRIPTION_STRING);



//	if ( CPUInfo_TestFor3DNow()==JE_TRUE)

//		BitsPerPixel = 32;

//	else

		BitsPerPixel = 16;



	if (DisplayModeInfo_AddEntry(Info,-1,-1,BitsPerPixel,0)==JE_FALSE)

		{

			jeErrorLog_AddString(JE_ERR_SUBSYSTEM_FAILURE,"DIBDisplay_GetDescriptionString: unable to add mode entry",NULL);

			return JE_FALSE;

		}

	return JE_TRUE;

}





//------------------------------------------------

static void DIBDisplay_DebugPrintf(char *fmt, ...)

{

	static char work_string[4096];



	if ((fmt == NULL) || (strlen(fmt) > sizeof(work_string)))

		{

			strcpy(work_string, "(String missing or too large)");

		}

	else

		{

			va_list ap;



			va_start(ap,fmt);



			vsprintf(work_string, fmt, ap);



			va_end  (ap);

		}



	#if 0  // log to file

	{

		FILE       *log;

		log = fopen("DEBUG.LOG","a+t");



		if (log != NULL)

			{

				fprintf(log,"%s\n",work_string);

				fclose(log);

			}

	}

	#endif



	OutputDebugString(work_string);

}





//------------------------------------------------

#ifdef DEBUG

static jeBoolean DIBDisplay_IsValid(const DIBDisplay *D)

{

	if (D == NULL)

		return JE_FALSE;



//	if (D->DIBInfo.pbmi == NULL)

//		return JE_FALSE;

	if (D->DIBInfo.backBuffer == NULL)

		return JE_FALSE;

//	if (D->DIBInfo.lpDIBBuffer == NULL)

//		return JE_FALSE;



	return JE_TRUE;

}

#endif







//------------------------------------------------

static jeBoolean DIBDisplay_FindDesktopRGBMasks(	G3DView*		hdc,

								int32	desktop_bpp,

								uint32  *desktop_R_bitmask,

								uint32	*desktop_G_bitmask,

								uint32	*desktop_B_bitmask)

{

	assert( hdc != NULL );

	assert( desktop_R_bitmask != NULL );

	assert( desktop_G_bitmask != NULL );

	assert( desktop_B_bitmask != NULL );

	

	BScreen thisScreen;

	color_space screenSpace = thisScreen.ColorSpace();

	

	int32 depth;

	if(screenSpace == B_RGB15 || screenSpace == B_RGB15_BIG)

	depth = 15;

	else

	depth = 16;

	

	*desktop_R_bitmask   = (depth == 16) ? 0x1f <<11 : 0x1f <<10;

    *desktop_G_bitmask = (depth == 16) ? 0x3f << 5 : 0x1f << 5;

    *desktop_B_bitmask = 0x1f;



/*	if(desktop_bpp > 8)

		{

			COLORREF		color,save;

			HBITMAP			TempBmp;

			BITMAPINFO		TempInfo;

			OSVERSIONINFO	WinVer;



			memset(&WinVer, 0, sizeof(OSVERSIONINFO));

			WinVer.dwOSVersionInfoSize	=sizeof(OSVERSIONINFO);



			GetVersionEx(&WinVer);

			if(WinVer.dwPlatformId == VER_PLATFORM_WIN32_NT)

				{

					TempBmp	=CreateCompatibleBitmap(hdc, 8, 8);

					if(TempBmp)

						{

							memset(&TempInfo, 0, sizeof(BITMAPINFO));

							TempInfo.bmiHeader.biSize		=sizeof(BITMAPINFO);

							TempInfo.bmiHeader.biBitCount	=(uint16)desktop_bpp;

							TempInfo.bmiHeader.biCompression	=BI_BITFIELDS;

							if(GetDIBits(hdc, TempBmp, 0, 0, NULL, &TempInfo, DIB_RGB_COLORS))

								{

									*desktop_R_bitmask	=*((uint32 *)&TempInfo.bmiColors[0]);

									*desktop_G_bitmask	=*((uint32 *)&TempInfo.bmiColors[1]);

									*desktop_B_bitmask	=*((uint32 *)&TempInfo.bmiColors[2]);

									DIBDisplay_DebugPrintf("(%x-%x-%x)\n", desktop_R_bitmask, desktop_G_bitmask, desktop_B_bitmask);

								}

							else

								{

									geErrorLog_AddString(GE_ERR_WINDOWS_API_FAILURE,"DIBDisplay_FindDesktopRGBMasks:  Unable to get bit format from desktop compatible bitmap",NULL);

									return JE_FALSE;

								}

							DeleteObject(TempBmp);

						}

					else

						{

							geErrorLog_AddString(GE_ERR_WINDOWS_API_FAILURE,"DIBDisplay_FindDesktopRGBMasks:  Unable to create desktop compatible bitmap",NULL);

							return JE_FALSE;

						}

				}

			else

				{

					HDC hdc2;

					

					hdc2 = CreateDC("Display", NULL, NULL,NULL);

					if (hdc2==NULL)

						{

							geErrorLog_AddString(GE_ERR_WINDOWS_API_FAILURE,"DIBDisplay_FindDesktopRGBMasks: Unable to create compatible hdc",NULL);

							return JE_FALSE;

						}

						

					save = GetPixel(hdc2,0,0);

					if ( save == CLR_INVALID )

						{

							geErrorLog_AddString(GE_ERR_WINDOWS_API_FAILURE,"DIBDisplay_FindDesktopRGBMasks:  Unable to get pixel from desktop",NULL);

							DeleteDC(hdc2);

							return JE_FALSE;

						}

							



					if (SetPixel(hdc2,0,0,RGB(0x08,0x08,0x08))==-1)

						{

							geErrorLog_AddString(GE_ERR_WINDOWS_API_FAILURE,"DIBDisplay_FindDesktopRGBMasks: Unable to select bitmap",NULL);

							DeleteDC(hdc2);

							return JE_FALSE;

						}

					

					color = GetPixel(hdc2,0,0) & 0xffffff;

					if ( color == CLR_INVALID )

						{

							geErrorLog_AddString(GE_ERR_WINDOWS_API_FAILURE,"DIBDisplay_FindDesktopRGBMasks:  Unable to get test pixel from desktop",NULL);

							DeleteDC(hdc2);

							return JE_FALSE;

						}



					SetPixel(hdc,0,0,save);		// ignore an error here.

					DeleteDC(hdc2);



					DIBDisplay_DebugPrintf("DIBDisplay: Desktop pixel format = 0x%X ",color);

					



					switch (color)

						{

							case 0x000000:						// 0x000000 = 5-5-5

								DIBDisplay_DebugPrintf("(5-5-5)\n");



								*desktop_R_bitmask = 0x007c00;

								*desktop_G_bitmask = 0x0003e0;

								*desktop_B_bitmask = 0x00001f;

								break;



							case 0x000800:						// 0x000800 = 5-6-5

    							DIBDisplay_DebugPrintf("(5-6-5)\n");



								*desktop_R_bitmask = 0x00f800;

								*desktop_G_bitmask = 0x0007e0;

								*desktop_B_bitmask = 0x00001f;

								break;

				

							case 0x080808:						// 0x080808 = 8-8-8

								DIBDisplay_DebugPrintf("(8-8-8)\n");



								*desktop_R_bitmask = 0xff0000;

								*desktop_G_bitmask = 0x00ff00;

								*desktop_B_bitmask = 0x0000ff;

								break;



							default:

								DIBDisplay_DebugPrintf("(Unsupported, using 5-6-5)\n");



								if ((desktop_bpp == 15) || (desktop_bpp == 16))

								   {

									   *desktop_R_bitmask = 0x00f800;

									   *desktop_G_bitmask = 0x0007e0;

									   *desktop_B_bitmask = 0x00001f;

									}

								else

									{

										geErrorLog_AddString(GE_ERR_DISPLAY_RESOURCE,"DIBDisplay_FindDesktopRGBMasks:  Unsupported desktop format.",NULL);

										return JE_FALSE;

									}

								break;

							}



				}

		}*/

		

	return JE_TRUE;

}









static void DIBDisplay_DestroyDIB(DIBDisplay *D,DIBDisplay_DIBInfo *DI)

{

/*

	assert(DI != NULL);

	

	delete DI->backBuffer;

	

	DI->backBuffer = NULL;

*/

D->hWnd->DestroyBitmap();

DI->backBuffer = NULL;	

}



static jeBoolean DIBDisplay_CreateDIB(DIBDisplay *D,

									  int32 Display_Width,

									  int32 Display_height,

									  color_space *space,

									  DIBDisplay_DIBInfo * DI)

{

	assert(DI != NULL);

	float width = Display_Width - 1.0f;

	float height = Display_height - 1.0f;

	

	BRect bounds(0.0,0.0,width,height);

//	bounds = {0,0,0,0};

	

//	printf("(int CreateDIB)Width %f, Height %f\n",width, height);

	

//	bounds.top = 0.0f;

//	bounds.left = 0.0f;

//	bounds.right = (int)Display_Width - 1;

//	bounds.bottom = Display_height - 1;

	

//	printf("Bounds.right %f, Bounds.bottom %f\n",bounds.right, bounds.bottom);

	D->hWnd->CreateBitmap(Display_Width,Display_height,*space);

	

	DI->backBuffer = D->hWnd->GetBackBuffer();//new BBitmap(bounds,*space,false,true);

	if(!DI->backBuffer)

	{

	printf("Out of memory for the back-buffer!\n");

	return JE_FALSE;

	}



//	printf("Backbuffer of size %fX%f successfully created\n",width,height);

	

return JE_TRUE;

}									 



jeBoolean DIBDisplay_Blit(DIBDisplay *D)

{

	// Is this any better?

	if( D->hWnd->Window()->LockWithTimeout(10000) != B_OK)

	{

		printf("Unable to lock window\n");

		return JE_TRUE;	

	}



#if 0	

	if( D->hWnd->Window()->IsHidden() )

	{

		printf("hidden\n");

		return JE_TRUE;

	}

		

	if(!D->DIBInfo.backBuffer)

	{

		printf("No backbuffer..\n");

		return JE_TRUE;

	}

#endif

		

	//D->hWnd->Invalidate();

	//D->hWnd->Window()->Unlock();

	D->hWnd->DrawBitmap(D->DIBInfo.backBuffer,BPoint(0,0));

	//D->hWnd->Sync();

	// Ensure we release the window

	D->hWnd->Window()->Unlock();





	return JE_TRUE;

}



//------------------------------------------------

jeBoolean DIBDisplay_Wipe	(	DIBDisplay   *D,

								uint32        color)

{

#ifdef DEBUG

	assert( DIBDisplay_IsValid(D) != JE_FALSE );

#endif

	if (!D->Locked)

		{

			//gegeErrorLog_AddString(Add(,"Display must be locked to clear",NULL);

			return JE_FALSE;

		}



//	if(color == 0)z

	memset(D->DIBInfo.backBuffer->Bits(), color, D->DIBInfo.backBuffer->BitsLength());

//	else

//	{

//		int i;

//		int16 *Ptr = (int16 *)D->DIBInfo.backBuffer->Bits();

//		int16 C    = (int16)color;

//		for (i=(D->Size_X * D->Size_Y); i>0; i--)

//			{

//				*(Ptr++) = C;

//			}

//	}



	

/*	if (color==0)

		memset(D->hWnd->Bits(), color, D->Size_Y * D->Pitch);

	else

		{

			int i;

			int16 *Ptr = (int16 *)D->hWnd->Bits();

			int16 C    = (int16)color;

			for (i=(D->Size_X * D->Size_Y); i>0; i--)

				{

					*(Ptr++) = C;

				}

		}*/

						

	return JE_TRUE;

}







jeBoolean DIBDisplay_UpdateWindow( DIBDisplay *D )

{

	BRect	window_rect;

	G3DView*	hWindow;



	assert( D->Locked == JE_FALSE );

	assert( D->hWnd != NULL );

	

	hWindow = D->hWnd;



	window_rect = D->hWnd->Bounds();

	

	assert ( window_rect.left  == 0 );

	assert ( window_rect.top == 0 );

	

	D->Size_X = (int)((window_rect.right  - window_rect.left)+3)&~3;

	D->Size_Y = (window_rect.bottom - window_rect.top);



	if ( D->Size_X <=0 )

		{

			jeErrorLog_AddString(JE_ERR_BAD_PARAMETER,"DIBDisplay_UpdateWindow: bad window client width=",jeErrorLog_IntToString(D->Size_X));

			return JE_FALSE;

		}

	if ( D->Size_Y <=0 )

		{

			jeErrorLog_AddString(JE_ERR_BAD_PARAMETER,"DIBDisplay_UpdateWindow: bad window client height=",jeErrorLog_IntToString(D->Size_Y));

			return JE_FALSE;

		}

	D->hWnd					= hWindow;



	DIBDisplay_DestroyDIB(D,&(D->DIBInfo));

//	D->DIBInfo.pbmi					= NULL;

//	D->DIBInfo.hDIB					= NULL;

//	D->DIBInfo.lpDIBBuffer			= NULL;



	D->Locked				= JE_FALSE;   

	D->Pitch				= (D->BitsPerPixel / 8 ) * D->Size_X;



	BScreen mainScreen;

	color_space Space = mainScreen.ColorSpace();

	

	printf("(BEFORE CREATEDIB CALL)Width %i, Height %i\n",D->Size_X, D->Size_Y);

		

	if (DIBDisplay_CreateDIB(D, D->Size_X, D->Size_Y,

								&Space,

								&(D->DIBInfo)) ==JE_FALSE)

		{

			jeErrorLog_Add(JE_ERR_SUBSYSTEM_FAILURE,"DIBDisplay_UpdateWindow: failed to create Dib section");

			DIBDisplay_DestroyDIB(D,&(D->DIBInfo));

			return JE_FALSE;

		}

	

	return JE_TRUE;

}	

	





//------------------------------------------------

DIBDisplay *DIBDisplay_Create	(	G3DView* hWindow,

									int Width,

									int Height,

									int   display_bpp,

									uint32  Flags )

{

	DIBDisplay *D;

	BRect	window_rect;

//	BOOL	result;

	Flags;	Width;  Height; // avoid unused formal parameter warnings



	assert( display_bpp    > 0);



	D= (DIBDisplay *)malloc(sizeof(*D));

	if (D==NULL)

		{

			// errlogAdd("failed to allocate DIBDisplay object");

			return NULL;

		}





	window_rect = hWindow->Bounds();// = GetClientRect(hWindow, &window_rect);



	D->Size_X = (window_rect.right  - window_rect.left);

	D->Size_Y = (window_rect.bottom - window_rect.top);



	

	if ( D->Size_X <=0 )

		{

			//errlogAdd("bad window client width");

			return NULL;

		}

	if ( D->Size_Y <=0 )

		{

			//errlogAdd("bad widndow client height");

			return NULL;

		}



	D->Locked				= JE_FALSE;   

	D->BitsPerPixel			= display_bpp;

	D->Pitch				= (D->BitsPerPixel / 8 ) * D->Size_X;

	D->hWnd					= hWindow;



//	D->DIBInfo.pbmi					= NULL;

//	D->DIBInfo.hDIB					= NULL;

//	D->DIBInfo.lpDIBBuffer			= NULL;

	

	BScreen mainScreen;

	color_space Space = mainScreen.ColorSpace();

	

	printf("(BEFORE CREATEDIB CALL)Width %i, Height %i\n",D->Size_X, D->Size_Y);



	if (DIBDisplay_CreateDIB(D,D->Size_X, D->Size_Y, &Space,&(D->DIBInfo)) ==JE_FALSE)

		{

			// errlogAdd("failed to create Dib section")

			printf("Failed to CreateDIB!\n");

			DIBDisplay_Destroy(&D);

			return NULL;

		}

	

	return D;

}





//------------------------------------------------

void DIBDisplay_Destroy(DIBDisplay **pD)

{

	DIBDisplay *D;



	assert( *pD != NULL );

	assert( pD  != NULL );



	D = *pD;



	#if DEBUG

	assert( DIBDisplay_IsValid(D) != JE_FALSE );

	#endif

		

	DIBDisplay_DestroyDIB(D,&(D->DIBInfo));

	free( D );



	*pD = NULL;

}







//------------------------------------------------

jeBoolean DIBDisplay_Lock      (DIBDisplay *D,

								uint8       **ptr,

								int32       *pitch)

{

#if DEBUG

	assert( DIBDisplay_IsValid(D) != JE_FALSE );

	assert( ptr    != NULL );

	assert( pitch  != NULL );

	assert( D->Locked == JE_FALSE );

#endif

	*ptr    =  (uint8*)D->DIBInfo.backBuffer->Bits();//D->hWnd->Bits();// + D->hWnd->_bytes_per_row * 3;

	*pitch = D->DIBInfo.backBuffer->BytesPerRow();//640;//640*2;//D->DIBInfo.backBuffer->BytesPerRow();//D->DIBInfo.backBuffer->BytesPerRow();//*pitch = D->Size_X * 2; //D->DIBInfo.backBuffer->BytesPerRow();

	D->Locked = JE_TRUE;

	return JE_TRUE;

}





jeBoolean DIBDisplay_Unlock        (DIBDisplay *D)

{

//	assert( DIBDisplay_IsValid(D) != JE_FALSE );

	assert( D->Locked == JE_TRUE );

	D->Locked = JE_FALSE;

	

	return JE_TRUE;

}





//------------------------------------------------

jeBoolean DIBDisplay_GetPixelFormat(	const DIBDisplay *D,

										//int32 *pixel_pitch, 

										int32 *bytes_per_pixel,

										int32 *R_shift,

										uint32 *R_mask,

										int32 *R_width,

										int32 *G_shift,

										uint32 *G_mask,

										int32 *G_width,

										int32 *B_shift,

										uint32 *B_mask,

										int32 *B_width)

{

	int32 red_shift=0;

	uint32 red_mask;

	int32 red_width=0;

	int32 grn_shift=0;

	uint32 grn_mask;

	int32 grn_width=0;

	int32 blu_shift=0;

	uint32 blu_mask;

	int32 blu_width=0;

	int32 i;



//	assert( DIBDisplay_IsValid(D) != JE_FALSE );



	//assert( pixel_pitch     != NULL );

	assert( bytes_per_pixel != NULL );

	assert( R_shift         != NULL );

	assert( R_mask          != NULL );

	assert( R_width         != NULL );

	assert( G_shift         != NULL );

	assert( G_mask          != NULL );

	assert( G_width         != NULL );

	assert( B_shift         != NULL );

	assert( B_mask          != NULL );

	assert( B_width         != NULL );

	





	//*pixel_pitch     = (D->BitsPerPixel / 8);

	*bytes_per_pixel = (D->BitsPerPixel / 8);

		

	BScreen mainScreen;

	color_space mainSpace = mainScreen.ColorSpace();

	if(mainSpace == B_RGB15 || mainSpace == B_RGB15_BIG)

	{

	red_mask   = 0x1f << 10;

    grn_mask = 0x1f << 5;

    blu_mask  = 0x1f;   

    }

    else

    {          

    red_mask   = 0x1f << 11;

    grn_mask = 0x3f << 5;

    blu_mask  = 0x1f;   

    }  

//	red_mask = D->DIBInfo.DIB_R_bitmask;

//	grn_mask = D->DIBInfo.DIB_G_bitmask;

//	blu_mask = D->DIBInfo.DIB_B_bitmask;

	

	//

	// Derive shift, width values from masks

	//



	for (i=31; i >= 0; i--)

		{

			if (red_mask & (1 << i))

				{

					red_shift = i;

				}



			if (grn_mask & (1 << i))

				{

					grn_shift = i;

				}



			if (blu_mask & (1 << i))

				{

					blu_shift = i;

				}

		}



	for (i=0; i <= 31; i++)

		{

			if (red_mask & (1 << i))

				{

					red_width = i - red_shift + 1;

				}



			if (grn_mask & (1 << i))

				{

					grn_width = i - grn_shift + 1;

				}



			if (blu_mask & (1 << i))

				{

					blu_width = i - blu_shift + 1;

				}

		}

	//

	// Pass all requested values back to the caller

	//



	*R_shift = red_shift;

	*G_shift = grn_shift;

	*B_shift = blu_shift;



	*R_mask  = red_mask;

	*G_mask  = grn_mask;

	*B_mask  = blu_mask;



	*R_width = red_width;

	*G_width = grn_width;

	*B_width = blu_width;

		

	return JE_TRUE;

}









