/*
Copyright (C) 2006 Andrey Nazarov

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

/*
** win_ceimp.c
**
** This file contains ALL WinCE specific stuff having to do with the
** software refresh.
*/

#ifndef _WIN32_WCE
#  error You should not be trying to compile this file on this platform
#endif

#include "win_local.h"
#include "win_swimp.h"
#include <aygshell.h>
#include <gx.h>

typedef int (*GXOpenDisplay_t)( HWND, DWORD );
typedef int (*GXCloseDisplay_t)( void );
typedef void *(*GXBeginDraw_t)( void );
typedef int (*GXEndDraw_t)( void );
typedef int (*GXOpenInput_t)( void );
typedef int (*GXCloseInput_t)( void );
typedef struct GXDisplayProperties (*GXGetDisplayProperties_t)( void );

static GXOpenDisplay_t	qgxOpenDisplay;
static GXCloseDisplay_t qgxCloseDisplay;
static GXBeginDraw_t	qgxBeginDraw;
static GXEndDraw_t		qgxEndDraw;
static GXOpenInput_t	qgxOpenInput;
static GXCloseInput_t	qgxCloseInput;
static GXGetDisplayProperties_t	qgxGetDisplayProperties;

#define QGX_FORMAT_MASK	( kfDirect555 | kfDirect565 | kfDirect888 | kfDirect444 )

static uint16 qgx_palette[256];

swwstate_t sww_state;

static void QGX_Shutdown( void ) {
	Com_Printf( "Shuttding down QGX\n" );

	if( sww_state.displayGrabbed ) {
		qgxEndDraw();
		sww_state.displayGrabbed = qfalse;
	}
	if( sww_state.displayOpened ) {
		qgxCloseDisplay();
		sww_state.displayOpened = qfalse;
	}
	if( sww_state.hinstGX ) {
		FreeLibrary( sww_state.hinstGX );
		sww_state.hinstGX = NULL;
	}
	if( sww_state.buffer ) {
		com.Free( sww_state.buffer );
		sww_state.buffer = NULL;
	}
	GetFBAddress(
}

static void QGX_SetPalette( const byte *palette ) {
	int i;
	uint16 *dest;
	byte r, g, b;

	dest = qgx_palette;
	for( i = 0; i < 256; i++, palette += 4 ) {
		r = palette[0] >> 3;
		g = palette[1] >> 2;
		b = palette[2] >> 3;
		*dest++ = ( r << 11 ) | ( g << 5 ) | b;
	}
}

static void QGX_Blit( void ) {
	uint16 *dest, *last;
	byte *src;
#if 1
	dest = qgxBeginDraw();
	if( !dest ) {
		Com_Error( ERR_FATAL , "QGX_Blit: qgxBeginDraw failed" );
	}
	last = dest + sww_state.width * sww_state.height;
#else
	dest = sww_state.surfaceStart;
	last = sww_state.surfaceEnd;
#endif
	src = sww_state.buffer;
	do {
		*dest++ = qgx_palette[*src++];
	} while( dest != last );

	qgxEndDraw();
}

#define GPA( x, y, z )														\
	do {																	\
		x = ( y ## _t )GetProcAddress( sww_state.hinstGX, __TEXT( z ) );	\
		if( !x ) {															\
			Com_Printf( #y " not found in GX.DLL\n" );						\
			goto fail;														\
		}																	\
	} while( 0 )

static qboolean QGX_Init( int *pwidth, int *pheight, byte **buffer, int *rowbytes, const byte *palette ) {
	struct GXDisplayProperties props;

	Com_Printf( "Initializing QGX\n" );

	if ( !sww_state.hinstGX ) {
		Com_Printf( "...loading GX.DLL: ");
		if ( ( sww_state.hinstGX = LoadLibrary( __TEXT( "gx.dll" ) ) ) == NULL ) {
			Com_Printf( "failed\n" );
			goto fail;
		}
		Com_Printf( "ok\n" );
	}

	GPA( qgxOpenDisplay, GXOpenDisplay, "?GXOpenDisplay@@YAHPAUHWND__@@K@Z" );
	GPA( qgxCloseDisplay, GXCloseDisplay, "?GXCloseDisplay@@YAHXZ" );
	GPA( qgxBeginDraw, GXBeginDraw, "?GXBeginDraw@@YAPAXXZ" );
	GPA( qgxEndDraw, GXEndDraw, "?GXEndDraw@@YAHXZ" );
	GPA( qgxOpenInput, GXOpenInput, "?GXOpenInput@@YAHXZ" );
	GPA( qgxCloseInput, GXCloseInput, "?GXCloseInput@@YAHXZ" );
	GPA( qgxGetDisplayProperties, GXGetDisplayProperties, "?GXGetDisplayProperties@@YA?AUGXDisplayProperties@@XZ" );

	props = qgxGetDisplayProperties();
	Com_Printf( "Display properties:\n" );
	Com_Printf( "cxWidth %d\n", props.cxWidth );
	Com_Printf( "cyHeight %d\n", props.cyHeight );
	Com_Printf( "cbxPitch %d\n", props.cbxPitch );
	Com_Printf( "cbyPitch %d\n", props.cbyPitch );
	Com_Printf( "cBPP %d\n", props.cBPP );
	Com_Printf( "ffFormat %d\n", props.ffFormat );
	

	if( !( props.ffFormat & kfDirect ) ) {
		Com_Printf( "Palettized display in not supported\n" );
		goto fail;
	}

	switch( props.ffFormat & QGX_FORMAT_MASK ) {
	case kfDirect565:
		break;
	default:
		Com_Printf( "Display color depth %d not supported\n", props.ffFormat & QGX_FORMAT_MASK );
		goto fail;
	}

	//sww_state.hWnd = Win_CreateWindow( props.cxWidth, props.cyHeight, qtrue );
	
	Com_Printf( "...opening GX display: \n" );
	if( !qgxOpenDisplay( sww_state.hWnd, GX_FULLSCREEN ) ) {
		Com_Printf( "failed\n" );
		goto fail;
	}
	Com_Printf( "ok\n" );
	sww_state.displayOpened = qtrue;

	sww_state.width = props.cxWidth;
	sww_state.height = props.cyHeight;

#if 0
	Com_Printf( "...acquiring surface: \n" );
	sww_state.surfaceStart = qgxBeginDraw();
	if( !sww_state.surfaceStart ) {
		Com_Printf( "failed\n" );
		goto fail;
	}
	Com_Printf( "ok\n" );
	sww_state.surfaceEnd = sww_state.surfaceStart + sww_state.width * sww_state.height;

	sww_state.displayGrabbed = qtrue;
#endif

	sww_state.buffer = com.Malloc( sww_state.width * sww_state.height );

	*pwidth = sww_state.width;
	*pheight = sww_state.height;
	*rowbytes = sww_state.width;
	*buffer = sww_state.buffer; 

	QGX_SetPalette( palette );

	//Com_Error(ERR_FATAL,"foo");
	Com_Printf( "QGX initialization succeeded\n");

	return qtrue;

fail:
	QGX_Shutdown();
	return qfalse;
}

#undef GPA

/*
** SWimp_Init
**
** This routine is responsible for initializing the implementation
** specific stuff in a software rendering subsystem.
*/
static qboolean SWimp_Init( void ) {
	Win_Init();
	return qtrue;
}

/*
** SWimp_Shutdown
**
** System specific graphics subsystem shutdown routine.  Destroys
** DIBs or DDRAW surfaces as appropriate.
*/
static void SWimp_Shutdown( void ) {
	Com_Printf( "Shutting down SW imp...\n" );
	if( sww_state.flags & QVF_ACCELERATED ) {
		QGX_Shutdown();
	} else {
		DIB_Shutdown();
	}

	if( sww_state.hWnd ) {
		if( !( sww_state.flags & QVF_ACCELERATED ) ) {
			SHFullScreen( sww_state.hWnd, SHFS_SHOWTASKBAR | SHFS_SHOWSIPBUTTON | 
                  SHFS_SHOWSTARTICON );
		}
		Com_Printf( "...destroying window\n" );
		Win_DestroyWindow( sww_state.hWnd );
		sww_state.hWnd = NULL;
	}

	memset( &sww_state, 0, sizeof( sww_state ) );
}

static void SWimp_BeginFrame( float stereo_spearation ) {
}


/*
** SWimp_EndFrame
**
** This does an implementation specific copy from the backbuffer to the
** front buffer.
*/
static void SWimp_EndFrame( byte **buffer, int *rowbytes ) {
	if( sww_state.flags & QVF_ACCELERATED ) {
		QGX_Blit();
	} else {
		BitBlt( sww_state.hDC,
				0, 0,
				sww_state.width,
				sww_state.height,
				sww_state.hdcDIBSection,
				0, 0,
				SRCCOPY );
	}
}

/*
** SWimp_SetMode
*/
static vidSetModeResult_t SWimp_SetMode( int *pwidth, int *pheight, byte **buffer, int *rowbytes, const byte *palette, int mode, qboolean fullscreen ) {
	const char *win_fs[] = { "DIB", "GX" };
	vidSetModeResult_t retval;

	if( sww_state.hWnd ) {
		// free resources in use
		SWimp_Shutdown();
	}

	sww_state.width = 480;
	sww_state.height = 640-24;

	Com_Printf( "setting mode %d: %d %d %s\n", mode, sww_state.width, sww_state.height, win_fs[fullscreen] );

	*pwidth = sww_state.width;
	*pheight = sww_state.height;

	sww_state.hWnd = Win_CreateWindow( sww_state.width, sww_state.height, qtrue );
	sww_state.flags = QVF_FULLSCREEN_ENABLED;

	if( fullscreen ) {
		if( QGX_Init( pwidth, pheight, buffer, rowbytes, palette ) ) {
			retval = SETMODE_FULLSCREEN;
			sww_state.flags |= QVF_ACCELERATED;
		} else if( DIB_Init( buffer, rowbytes, palette ) ) {
			SHFullScreen( sww_state.hWnd, SHFS_HIDETASKBAR | SHFS_HIDESTARTICON );
			retval = SETMODE_FULLSCREEN;
		} else {
			retval = SETMODE_ERROR;
		}
	} else {
		if( DIB_Init( buffer, rowbytes, palette ) ) {
			SHFullScreen( sww_state.hWnd, SHFS_HIDETASKBAR | SHFS_HIDESTARTICON );
			retval = SETMODE_FULLSCREEN;
		} else {
			retval = SETMODE_ERROR;
		}

	}

	if( retval == SETMODE_ERROR ) {
		Win_DestroyWindow( sww_state.hWnd );
		sww_state.hWnd = NULL;
	} 

	return retval;
}

/*
** SWimp_UpdatePalette
**
** System specific palette setting routine.  A NULL palette means
** to use the existing palette.  The palette is expected to be in
** a padded 4-byte xRGB format.
*/
static void SWimp_UpdatePalette( const byte *palette ) {
	if( sww_state.flags & QVF_ACCELERATED ) {
		QGX_SetPalette( palette );
	} else {
		DIB_SetPalette( palette );
	}
}

static void SWimp_UpdateGamma( const byte *gammatable ) {
}

/*
============
SWimp_GetFlags
============
*/
static vidFlags_t SWimp_GetFlags( void ) {
	return sww_state.flags;
}

/*
@@@@@@@@@@@@
SWimp_FillAPI
@@@@@@@@@@@@
*/
void Video_FillSWAPI( vidSWAPI_t *api ) {
	api->Init = SWimp_Init;
	api->Shutdown = SWimp_Shutdown;
	api->UpdateGamma = SWimp_UpdateGamma;
	api->GetFlags = SWimp_GetFlags;
	api->SetMode = SWimp_SetMode;
	api->UpdatePalette = SWimp_UpdatePalette;
	api->BeginFrame = SWimp_BeginFrame;
	api->EndFrame = SWimp_EndFrame;
}
