/*--------------------------------------------------------------------------
 * File: dx32.c
 * Written by: Alexander Boczar, 1997-04-01
 * Description: A DirectX driver for 32bit truecolor resolutions
 *
 * Updates:
 * -- Date -- | ----- Name ----- |-- Did what....
 * 1997-04-01 | Alexander Boczar | Žndrade s† att ifall lPitch = Width, s† kopierar jag i ett svep ist„llet f”r rad f”r rad,
 *
 * Todo:
 *
 * Kolla vad h†rdvaran klarar ist„llet f”r k”ra rakt p† och faila..
 -------------------------------------------------------------------------------*/

#include <nt/windows.h>
#include <nt/ddraw.h>
#include <float.h>
#include "truecol/gfxdrv.h"
#include "truecol/externs.h"

extern GFXDRV dx32DRV;

#define MAXKEYS 256

static int windowactive = 0;
static int keys = 0;
static char keystack[MAXKEYS];

static int mousetimer;

static int mousexpos,mouseypos;
static int mousexchange,mouseychange;
static int gmousexchange,gmouseychange;

static HINSTANCE process;
static LPDIRECTDRAW dd;
static LPDIRECTDRAWSURFACE showscreen;
//static LPDIRECTDRAWSURFACE workscreen;

static HRESULT (WINAPI *_DirectDrawCreate)( GUID FAR *lpGUID, LPDIRECTDRAW FAR *lplpDD, IUnknown FAR *pUnkOuter );
static HRESULT (WINAPI *_DirectDrawEnumerateA)( LPDDENUMCALLBACKA lpCallback, LPVOID lpContext );

memset32( void *dst, unsigned int c, size_t length);
#pragma aux memset32 "*" =\
	"rep stosd"\
	parm [edi] [eax] [ecx]\
	modify[edi ecx];

memsetf32( void *dst, float c, size_t length);
#pragma aux memsetf32 "*" =\
	"rep stosd"\
	parm [edi] [eax] [ecx]\
	modify[edi ecx];

memcpy32( void *dst, const void *src, size_t length);
#pragma aux memcpy32 "*" =\
	"rep movsd"\
	parm [edi] [esi] [ecx]\
	modify[edi esi ecx];

static void checkmsg()
{
	MSG msg;

	while ( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
}

static long FAR PASCAL WindowProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
	PAINTSTRUCT ps;
	POINT mousepos;

	switch( message )
	{
		case WM_ACTIVATEAPP:
//			printf("WM_ACTIVEAPP\n");
			windowactive = wParam;
			break;

		case WM_TIMER:
			if (wParam == mousetimer)
			{
				GetCursorPos( &mousepos);
				mousexchange = mousepos.x - dx32DRV.xorigo;
				mouseychange = mousepos.y - dx32DRV.yorigo;
				gmousexchange += mousexchange;
				gmouseychange += mouseychange;
				mousexpos += mousexchange;
				mouseypos += mouseychange;
				if ( mousexpos > (dx32DRV.width - 1))
					mousexpos = dx32DRV.width - 1;
				else if ( mousexpos < 0)
					mousexpos = 0;
				if ( mouseypos > (dx32DRV.height - 1))
					mouseypos = dx32DRV.height - 1;
				else if ( mouseypos < 0)
					mouseypos = 0;

				SetCursorPos( dx32DRV.xorigo, dx32DRV.yorigo);
			}
			else
			{
				printf("Unknown timer caught!\n");
			}

		case WM_CREATE:
//			printf("WM_CREATE\n");
			break;

		case WM_SETCURSOR:
//			printf("WM_SETCURSOR\n");
			SetCursor(NULL);
			return TRUE;

		case WM_KEYDOWN:
			printf("WM_KEYDOWN (%d,'%c')\n", wParam, wParam);
			if ( keys < MAXKEYS)
			{
				keystack[keys] = wParam;
				keys++;
			}
			break;

		case WM_PAINT:
//			printf("WM_PAINT\n");
			BeginPaint( hWnd, &ps );
			if (dx32DRV.vscreen != NULL) copy();
			EndPaint( hWnd, &ps );
			break;

		case WM_DESTROY:
//			printf("WM_DESTROY\n");
			PostQuitMessage( 0 );
			break;
	}
	return DefWindowProc(hWnd, message, wParam, lParam);
}

static int init( int width, int height, int config)
{
	HINSTANCE dxDLL;
	WNDCLASS wc;
	HWND window;
	DDSURFACEDESC ddsd;
	DDSCAPS ddscaps;
	int i;

	dx32DRV.width = width;
	dx32DRV.xorigo = width >> 1;
	dx32DRV.minx = 0;
	dx32DRV.maxx = width - 1;

	dx32DRV.height = height;
	dx32DRV.yorigo = height >> 1;
	dx32DRV.miny = 0;
	dx32DRV.maxy = height - 1;

	dx32DRV.config = config;

	if ( (dxDLL = LoadLibrary("ddraw.dll")) == NULL)
		return( 0);

	if ( (_DirectDrawCreate = GetProcAddress( dxDLL, "DirectDrawCreate")) == NULL)
		return( 0);
	if ( (_DirectDrawEnumerateA = GetProcAddress( dxDLL, "DirectDrawEnumerateA")) == NULL)
		return( 0);

	process = GetModuleHandle( NULL);

	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = WindowProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = process;
	wc.hIcon = LoadIcon( NULL, IDI_APPLICATION );
	wc.hCursor = LoadCursor( NULL, IDC_ARROW );
	wc.hbrBackground = NULL;
	wc.lpszMenuName = "Visual 3D";
	wc.lpszClassName = "Visual 3D";
	RegisterClass( &wc);

	window = CreateWindowEx( WS_EX_TOPMOST, "Visual 3D", "Visual 3D", WS_POPUP, 0, 0, GetSystemMetrics( SM_CXSCREEN ), GetSystemMetrics( SM_CYSCREEN ), NULL, NULL, process, NULL );
	if ( window == NULL)
		return( 0);

	ShowWindow( window, SW_SHOWDEFAULT );
	UpdateWindow( window );

	if ( _DirectDrawCreate( NULL, &dd, NULL) != DD_OK)
		return( 0);

	if( dd->lpVtbl->SetCooperativeLevel( dd, window, DDSCL_ALLOWREBOOT | DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN ) != DD_OK )
		return( 0);

	if( dd->lpVtbl->SetDisplayMode( dd, dx32DRV.width, dx32DRV.height, 32 ) != DD_OK )
		return( 0);
/*
	ddsd.dwSize = sizeof( ddsd );
	ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
	ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_FLIP;
	ddsd.dwBackBufferCount = 1;
	if ( dd->lpVtbl->CreateSurface( dd, &ddsd, &showscreen, NULL ) != DD_OK )
		return( 0);
*/
	ddsd.dwSize = sizeof( ddsd );
	ddsd.dwFlags = DDSD_CAPS;
	ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
	if ( dd->lpVtbl->CreateSurface( dd, &ddsd, &showscreen, NULL ) != DD_OK )
		return( 0);

/*
	ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
	if ( showscreen->lpVtbl->GetAttachedSurface(showscreen, &ddscaps, &workscreen) != DD_OK )
		return( 0);
*/

	dx32DRV.vscreen = (RGB *)malloc( dx32DRV.width * dx32DRV.height * sizeof( RGB));
	dx32DRV.filltab = (FILLLINE *)malloc( dx32DRV.height * sizeof( FILLLINE));
	dx32DRV.ytab = (int *)malloc( dx32DRV.height * sizeof( int));
	if ( dx32DRV.config & GFXCFG_ZBUFFER)
		dx32DRV.zbuffer = (float *)malloc( dx32DRV.width * dx32DRV.height * sizeof( float));

	for (i=0; i<dx32DRV.height; i++)
		dx32DRV.ytab[i] = i * dx32DRV.width;

	clear();

	mousetimer = SetTimer( window, 1, 20, NULL); //Start a mouse timer at 50hz

	return( 1);
}

static void exit()
{
	KillTimer( NULL, mousetimer);
	showscreen->lpVtbl->Release( showscreen);
	dd->lpVtbl->Release( dd);
}

static void vsync()
{
	checkmsg();
	if ( windowactive)
		dd->lpVtbl->WaitForVerticalBlank( dd, DDWAITVB_BLOCKBEGIN, NULL);
}

static void copy()
{
	DDSURFACEDESC ddsd;
	HRESULT ddrval;
	RGB *src,*dest;
	int y;

	checkmsg();

	if ( windowactive)
	{
		ddsd.dwSize = sizeof( ddsd );
		ddrval = showscreen->lpVtbl->Lock( showscreen, NULL, &ddsd,  DDLOCK_WAIT, NULL);
		if ( ddrval == DDERR_SURFACELOST )
		{
			showscreen->lpVtbl->Restore( showscreen);
			ddrval = showscreen->lpVtbl->Lock( showscreen, NULL, &ddsd,  DDLOCK_WAIT, NULL);
		}
		if ( ddrval == DD_OK )
		{
			if ( ddsd.lPitch != dx32DRV.width)
			{
				dest = ddsd.lpSurface;
				src = dx32DRV.vscreen;
				for (y=0; y<dx32DRV.height; y++)
				{
					memcpy32( dest, src, dx32DRV.width);
					src += dx32DRV.width;
					dest += ddsd.lPitch >> 2;
				}
			}
			else
				memcpy32( ddsd.lpSurface, dx32DRV.vscreen, (dx32DRV.width * dx32DRV.height));

			showscreen->lpVtbl->Unlock( showscreen, NULL);

//		showscreen->lpVtbl->Flip( showscreen, NULL, DDFLIP_WAIT);
		}
		else
			printf("Failed while attempting ->lock() [%08x]\n", ddrval);
	}

}

static void clear()
{
	memset32( dx32DRV.vscreen, 0, (dx32DRV.width * dx32DRV.height) );
	if ( dx32DRV.config & GFXCFG_ZBUFFER)
		memsetf32( dx32DRV.zbuffer, -FLT_MAX, dx32DRV.width * dx32DRV.height);

}

static void move()
{
	copy();
	clear();
}

static void copyregion( int x, int y, int width, int height, int ofsx, int ofsy)
{
}

static void clearregion( int x, int y, int width, int height)
{
}

static void moveregion( int x, int y, int width, int height, int ofsx, int ofsy)
{
}

static void plot( float x, float y, float z, RGB c)
{
	if ( dx32DRV.config & GFXCFG_ZBUFFER)
		truecol_zplot( &dx32DRV, x, y, z, c );
	else
		truecol_plot( &dx32DRV, x, y, z, c );
}

static void line( float x1, float y1, float z1, float x2, float y2, float z2, RGB c )
{
	if ( dx32DRV.config & GFXCFG_ZBUFFER)
		truecol_zline( &dx32DRV, x1, y1, z1, x2, y2, z2, c );
	else
		truecol_line( &dx32DRV, x1, y1, z1, x2, y2, z2, c );
}

static void iblit( int x, int y, float z, char *image, RGB *palette, int width, int height)
{
	if ( dx32DRV.config & GFXCFG_ZBUFFER)
		truecol_ziblit( &dx32DRV, x, y, z, image, palette, width, height);
	else
		truecol_iblit( &dx32DRV, x, y, z, image, palette, width, height);
}

static void tblit( int x, int y, float z, RGB *image, int width, int height)
{
	if ( dx32DRV.config & GFXCFG_ZBUFFER)
		truecol_ztblit( &dx32DRV, x, y, z, image, width, height);
	else
		truecol_tblit( &dx32DRV, x, y, z, image, width, height);
}


static void fpoly( float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, RGB c )
{
	if ( dx32DRV.config & GFXCFG_ZBUFFER)
		truecol_zfpoly( &dx32DRV, x1, y1, z1, x2, y2, z2, x3, y3, z3, c );
	else
		truecol_fpoly( &dx32DRV, x1, y1, z1, x2, y2, z2, x3, y3, z3, c );
}

static void gpoly( float x1, float y1, float z1, RGB c1, float x2, float y2, float z2, RGB c2, float x3, float y3, float z3, RGB c3 )
{
	if ( dx32DRV.config & GFXCFG_ZBUFFER)
		truecol_zgpoly( &dx32DRV, x1, y1, z1, c1, x2, y2, z2, c2, x3, y3, z3, c3 );
	else
		truecol_gpoly( &dx32DRV, x1, y1, z1, c1, x2, y2, z2, c2, x3, y3, z3, c3 );
}

static void tpoly( float x1, float y1, float z1, float tx1, float ty1, float x2, float y2, float z2, float tx2, float ty2, float x3, float y3, float z3, float tx3, float ty3, TEXTURE *t)
{
	if ( dx32DRV.config & GFXCFG_ZBUFFER)
		truecol_ztpoly( &dx32DRV, x1, y1, z1, tx1, ty1, x2, y2, z2, tx2, ty2, x3, y3, z3, tx3, ty3, t );
	else
		truecol_tpoly( &dx32DRV, x1, y1, z1, tx1, ty1, x2, y2, z2, tx2, ty2, x3, y3, z3, tx3, ty3, t );
}

static int kbhit()
{
	return( keys);
}

static char getkey()
{
	int i;
	char key;

	if ( keys > 0)
	{
		key = keystack[0];
		keys--;
		for (i=0; i<keys; i++)
			keystack[i] = keystack[i+1];
		return( key);
	}
	else
	{
		while ( keys == 0)
			checkmsg();

		key = keystack[0];
		keys--;
		for (i=0; i<keys; i++)
			keystack[i] = keystack[i+1];
		return( key);
	}
}

static int mousex()
{
	return( mousexpos);
}

static int mousey()
{
	return( mouseypos);
}

static int mousexdiff()
{
	int x;
	x = gmousexchange;
	gmousexchange = 0;
	return( x);
}

static int mouseydiff()
{
	int y;
	y = gmouseychange;
	gmouseychange = 0;
	return( y);
}

GFXDRV dx32DRV =
{
	"Windows 95/NT DirectDraw(tm) Truecolor Driver",\

	&init,\
	&exit,\

	&vsync,\

	&copy,\
	&clear,\
	&move,\

	&copyregion,\
	&clearregion,\
	&moveregion,\

	&plot,\
	&line,\
	&iblit,\
	&tblit,\
	&fpoly,\
	&gpoly,\
	&tpoly,\

	&mousex, &mousey,\
	&mousexdiff, &mouseydiff,\

	&kbhit, getkey,\

	320,200,\
	160,100,\
	0,319,0,199,\
	1,1,\

	0,/*config*/\

	NULL,/*vscreen*/\
	NULL,/*zbuffer*/\
	NULL,/*filltab*/\

	0,0,0,0,/*mxs,mxe,mys,mye*/\

	NULL,/*ytab*/\
};











