/*
Copyright (c) 1998 Richard Lawrence

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.
*/

#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <io.h>
#include <fcntl.h>
#include "atari.h"
#include "colours.h"
#include "platform.h"
#include "winatari.h"
#include "graphics.h"
#include "resource.h"
#include "winatari.h"
#include "registry.h"
#include "log.h"
#include "zlib.h"
#include "atari.h"
#include "rt-config.h"

extern void Coldstart( void );
extern void Input_Reset( void );
extern void CleanupInput(void);
extern void RestoreSIO( void );
extern void SetSIOEsc( void );
extern void Screen_Paused( UBYTE *screen );
extern UBYTE memory[];
extern unsigned	long	ulModesAvail;
extern void	CheckDDrawModes( void );
extern LPBITMAPINFO	lpbmi;
extern ULONG *atari_screen;
extern UBYTE	screenbuff[];
extern ULONG ulAtariState;
extern ULONG ulMiscStates;
extern void Clear_Sound( BOOL bPermanent );
extern void Restart_Sound( void );
extern void SetSafeDisplay( );
extern HWND	MainhWnd, hWnd;
extern int	enable_sio_patch;
extern void Clear_Temp_Files( void );
extern int	main (int argc, char **argv);
extern int	default_system;
extern int	default_tv_mode;
extern ULONG ulScreenMode;
extern UBYTE atari_basic[];
extern char atari_basic_filename[];
extern char atari_xlxe_filename[];
extern int	enable_sio_patch;
extern int wsync_halt;			/* WSYNC_HALT */
extern int test_val;
extern unsigned long CheckFile( char *fn, int *result );

PALETTEENTRY	pe[256];
char	szSystemText[64];


ULONG	ulAtari_HW_Nexttime;
ULONG	ulDeltaT = 0;
#ifdef USE_PERF_COUNTER
LARGE_INTEGER	lnTimeFreq;
#endif
long nSleepThreshold = 0;

BOOL	bCheckZlib = TRUE;
HANDLE	hZlib = NULL;
FARPROC	pgzread = NULL, pgzopen = NULL, pgzclose = NULL, pgzwrite = NULL, pgzerror = NULL;

int	Atari_Exit( int );
void DescribeAtariSystem( void );

int enable_rom_patches = 1;


#ifdef USE_THREAD
DWORD WINAPI StartAtariThread( LPVOID pParam )
{
	char	*argv[2] = {"Atari800Win.exe", ""};
	/*main( 3, argv );*/
	Atari_ReInit();
	return 0;
}
#endif

/* Simple routine to return TRUE if the given filename is any one of the various compressed
   types. Currently the only test for this is by extension - also the only way you can actually
   read a compressed file in, so that seems valid enough. */
BOOL IsCompressedFile( char *filename )
{
	int i;
	char szTempFile[ MAX_PATH ];

	for( i = 0; i < strlen( filename ); i++ )
		szTempFile[i] = toupper( filename[i] );
	szTempFile[i] = 0;

	if( strstr( szTempFile, ".GZ" ) || strstr( szTempFile, ".ATZ") || strstr( szTempFile, ".XFZ" )
		|| strstr( szTempFile, ".DCM") )
		return TRUE;

	return FALSE;
}

int ReadDisabledROMs( void )
{
	int	romfile;

	romfile = _open( atari_basic_filename, O_BINARY | O_RDONLY, 0777 );
	if( romfile == -1 )
	{
		Aprint( "Could not open %s for reading", atari_basic_filename );
		return FALSE;
	}

	if( _read( romfile, atari_basic, 8192 ) < 8192 )
	{
		Aprint( "Could not read all of atari basic from %s", atari_basic_filename );
		return FALSE;
	}
	_close( romfile );

	romfile = _open( atari_xlxe_filename, O_BINARY | O_RDONLY, 0777 );
	if( romfile == -1 )
	{
		Aprint( "Could not open %s for reading", atari_xlxe_filename );
		return FALSE;
	}

	if( _read( romfile, atari_xlxe_filename, 16384 ) < 16384 )
	{
		Aprint( "Could not read entire atari ROM from %s", atari_xlxe_filename );
		return FALSE;
	}
	_close( romfile );
	return TRUE;
}

int prepend_tmpfile_path( char *buffer )
{
	int	offset = 0;

	offset += GetTempPath( MAX_PATH, buffer) - 1;
	if( offset == -1 || buffer[offset] != '\\' )
		offset++;

	return offset;
}

int zlib_capable( void )
{
	if( !hZlib )
	{
		Aprint( "Cannot gzip/ungzip without gzip.dll loaded properly" );
		return -1;
	}
	return 1;
}

void Start_Atari_Timer( void )
{
#ifdef USE_PERF_COUNTER
	LARGE_INTEGER	lnCurrentTime;

	QueryPerformanceCounter( &lnCurrentTime );
	ulAtari_HW_Nexttime = lnCurrentTime.LowPart + ulDeltaT;
#else
	ulAtari_HW_Nexttime = timeGetTime() + ulDeltaT;
#endif
}

void Atari_Initialise (int *argc, char *argv[])
{
	int i;

	memset( szSystemText, 0, 64 );
	if( bCheckZlib )
	{
		bCheckZlib = FALSE;
		if( !hZlib )
		{
			hZlib = LoadLibrary( "ZLIB.DLL" );
			if( !hZlib )
				MessageBox( MainhWnd, "Could not load ZLIB.DLL. No decompression of gzip files will be possible.", "Atari800Win", MB_OK );
			else
			{
				pgzread = GetProcAddress( hZlib, "gzread" );
				pgzopen = GetProcAddress( hZlib, "gzopen" );
				pgzclose = GetProcAddress( hZlib, "gzclose" );
				pgzwrite = GetProcAddress( hZlib, "gzwrite" );
				pgzerror = GetProcAddress( hZlib, "gzerror" );
				if( !pgzread || !pgzopen || !pgzclose || !pgzwrite || !pgzerror)
				{
					FreeLibrary( hZlib );
					hZlib = NULL;
					MessageBox( MainhWnd, "Could not obtain necessary functions from ZLIB.DLL. No decompression of gzip files will be possible.", "Atari800Win", MB_OK );
				}
			}
		}
	}

#ifdef USE_PERF_COUNTER
	memset( &lnTimeFreq, 0, sizeof( lnTimeFreq ) );
	memset( &ulAtari_HW_Nexttime, 0, sizeof( ulAtari_HW_Nexttime ) );
#endif

	if (tv_mode == TV_PAL)
	{
#ifdef USE_PERF_COUNTER
		QueryPerformanceFrequency( &lnTimeFreq );
		ulDeltaT = lnTimeFreq.LowPart / 50;
		nSleepThreshold = lnTimeFreq.LowPart / 1000L;
		nSleepThreshold *= (SLEEP_TIME_IN_MS + 1);
#else
		ulDeltaT = 20;
		nSleepThreshold = (SLEEP_TIME_IN_MS + 1);
#endif
	}
	else
	{
#ifdef USE_PERF_COUNTER
		QueryPerformanceFrequency( &lnTimeFreq );
		ulDeltaT = lnTimeFreq.LowPart / 60;
		nSleepThreshold = lnTimeFreq.LowPart / 1000L;
		nSleepThreshold *= (SLEEP_TIME_IN_MS + 1);
#else
		ulDeltaT = 17;
		nSleepThreshold = (SLEEP_TIME_IN_MS + 1);
#endif
	}

	if( !ulModesAvail )
		CheckDDrawModes();

	if( !lpbmi )
	{
		/* Only initialize this the first time it's allocated, since we'll come through here
		   on changing hardware types and it will already be set up correctly at that point */
		lpbmi = (LPBITMAPINFO)calloc( 1, (sizeof( BITMAPINFOHEADER ) + sizeof( RGBQUAD ) * 256) );

		if( !lpbmi  )
			Atari_Exit( TRUE );

		lpbmi->bmiHeader.biSize = sizeof( BITMAPINFOHEADER );
		lpbmi->bmiHeader.biWidth = ATARI_VIS_WIDTH;
		lpbmi->bmiHeader.biHeight = -ATARI_HEIGHT;	/* Negative because we are a top-down bitmap */
		lpbmi->bmiHeader.biPlanes = 1;
		lpbmi->bmiHeader.biBitCount = 8;				/* Each byte stands for a color value */
		lpbmi->bmiHeader.biCompression = BI_RGB;		/* Uncompressed format */

		for (i=0;i<256;i++)
		{
			int	rgb = colortable[i];
			
			lpbmi->bmiColors[i].rgbRed = pe[i].peRed = (rgb & 0x00ff0000) >> 16;
			lpbmi->bmiColors[i].rgbGreen = pe[i].peGreen = (rgb & 0x0000ff00) >> 8;
			lpbmi->bmiColors[i].rgbBlue = pe[i].peBlue = rgb & 0x000000ff;
			lpbmi->bmiColors[i].rgbReserved = pe[i].peFlags = 0;
		}	
	}

	Input_Reset();
}

void Toggle_Pause( void )
{
	if( ulAtariState & ATARI_RUNNING )
	{
		if( !(ulAtariState & ATARI_PAUSED ))
		{
			ulAtariState |= ATARI_PAUSED;
			Clear_Sound( FALSE );
			if( MainhWnd )
				SetTimer( MainhWnd, TIMER_READ_JOYSTICK, 100, NULL); 
			Screen_Paused( (UBYTE *)atari_screen );
 		}
		else
		{
			ulAtariState &= ~ATARI_PAUSED;
			Restart_Sound();
		}
		DescribeAtariSystem();
	}
}

void Toggle_FullSpeed( void )
{
	if( ulAtariState & ATARI_RUNNING )
	{
		if( ulMiscStates & ATARI_FULL_SPEED )
		{
			ulMiscStates &= ~ATARI_FULL_SPEED;
			Restart_Sound();
		}
		else
		{
			ulMiscStates |= ATARI_FULL_SPEED;
			Clear_Sound( FALSE );
		}
		DescribeAtariSystem();
	}
}

void Toggle_SIO_Patch( void )
{
	if( enable_sio_patch )
	{
		enable_sio_patch = 0;
		RestoreSIO();
	}
	else
	{
		enable_sio_patch = 1;
		SetSIOEsc();
	}

	DescribeAtariSystem();
}

void Atari_ReInit( void )
{
	ulAtariState = ATARI_UNINITIALIZED | ATARI_PAUSED;
	Remove_ROM( );
	Clear_Temp_Files( );
	main( 1, NULL );
	hWnd = NULL;
}

int Atari_Exit (int panic)
{
	if( panic && (ulAtariState & ATARI_RUNNING) )
		ulAtariState |= ATARI_CRASHED;
	
	if( ulAtariState == ATARI_CRASHED )
	{
		wsync_halt = 1;	/* turn off CPU */
		return 0;
	}

	if( panic && (ulAtariState & ATARI_RUNNING) )
	{
		char	message[LOADSTRING_STRING_SIZE];
		int		result;

		LoadString( NULL, IDS_WARNING_ATARI_PANIC, message, LOADSTRING_STRING_SIZE );
		result = MessageBox( MainhWnd, message, "Atari800Win", MB_ICONSTOP|MB_OK );

		ulAtariState = ATARI_CRASHED;
		InvalidateRect( MainhWnd, NULL, FALSE );
		wsync_halt = 1;	/* turn off CPU */
		test_val = 32767;
	}
	else
	{
		/*Reset everything DirectDraw*/
		SetSafeDisplay( );

		/*Reset everything DirectSound*/
		Clear_Sound( TRUE );

		/*Reset everything DirectInput*/
		CleanupInput( );

		ulAtariState = ATARI_UNINITIALIZED | ATARI_CLOSING;

		Remove_ROM( );

		if( lpbmi )
			free( lpbmi );
		lpbmi = NULL;

		Clear_Temp_Files( );

		if( hZlib )
		{
			FreeLibrary( hZlib );
        			pgzopen = pgzclose = pgzread = pgzwrite = pgzerror = NULL;
		}
	}
	return 0;
}

void DescribeAtariSystem( void )
{
	if( !MainhWnd )
		return;

	if( ulScreenMode & DDRAW_FULL )
		strcpy( szSystemText, "  ");
	else
		strcpy( szSystemText, "Atari800Win: " );

	if( ulAtariState & ATARI_LOAD_FAILED )
	{
		strcat( szSystemText, "Initialization failure" );
		SetWindowText( MainhWnd, szSystemText );
		return;
	}

	if( ulAtariState & ATARI_RUNNING )
	{
		if( ulAtariState & (ATARI_PAUSED | ATARI_NOFOCUS))
		{
			if( ulAtariState & ATARI_NOFOCUS && !(ulAtariState & ATARI_PAUSED) )
				strcat( szSystemText, "Stopped" );
			if( ulAtariState & ATARI_PAUSED )
				strcat( szSystemText, "Paused (F9 to continue)" );
		}
		else
		{
			switch( default_system )
			{
			case	1:
				strcat( szSystemText, "800 OS-A " );
				break;
				
			case	2:
				strcat( szSystemText, "800 OS-B " );
				break;
				
			case	3:
				strcat( szSystemText, "800XL " );
				break;
				
			case	4:
				strcat( szSystemText, "130XE " );
				break;
				
			case	5:
				strcat( szSystemText, "320XE " );
				break;
				
			case	6:
				strcat( szSystemText, "5200 " );
				break;
				
			default:
				strcat( szSystemText, "Unknown system! " );
				break;
			}
			
			switch( default_tv_mode )
			{
			case	1:
				if( ulMiscStates & ATARI_FULL_SPEED )
					strcat( szSystemText, "PAL FULL speed" );
				else
					strcat( szSystemText, "PAL 1.77Mhz" );
				break;
				
			case	2:
				if( ulMiscStates & ATARI_FULL_SPEED )
					strcat( szSystemText, "NTSC FULL speed" );
				else
					strcat( szSystemText, "NTSC 1.79Mhz" );
				break;
				
			default:
				strcat( szSystemText, "Unknown TV type!" );
				break;
			}

			if( enable_sio_patch )
				strcat( szSystemText, " SIO" );
		}
	}
	else
		strcat( szSystemText, "not running" );

	if( !(ulScreenMode & DDRAW_FULL ) )
		SetWindowText( MainhWnd, szSystemText );
}

BOOL TestAndSetPath( char *rompath, char *filename, unsigned long ulExpectedCRC )
{
	char	szFullPath[ MAX_PATH ];
	unsigned long ulCRC;
	int		result;

	ulCRC = CheckFile( filename, &result );
	if( ulCRC != 0 && ulCRC == ulExpectedCRC )
	{
		GetCurrentDirectory( MAX_PATH, rompath );
		strcat( rompath, "\\" );
		strcat( rompath, filename );
		return TRUE;
	}
	else
	{
		strcpy( szFullPath, "ROMS\\" );
		strcat( szFullPath, filename );

		ulCRC = CheckFile( szFullPath, &result );
		if( ulCRC != 0 && ulCRC == ulExpectedCRC )
		{
			GetCurrentDirectory( MAX_PATH, rompath );
			strcat( rompath, "\\ROMS\\" );
			strcat( rompath, filename );
			return TRUE;
		}
	}
	return FALSE;
}

/* Routine tries to locate the system ROMs in either the current directory or
   a subdirectory "ROMS" under the current directory. Returns true if at least
   one ROM file was found */

BOOL TestRomPaths( void )
{
	BOOL	bFoundRom = FALSE;

	if( TestAndSetPath( atari_osa_filename, "atariosa.rom", 0L ) )
		bFoundRom = TRUE;
	if( TestAndSetPath( atari_osb_filename, "atariosb.rom", 3252116993L ) )
		bFoundRom = TRUE;
	if( TestAndSetPath( atari_xlxe_filename, "atarixl.rom", 3764596111L ) )
		bFoundRom = TRUE;
	if( TestAndSetPath( atari_xlxe_filename, "atarixe.rom", 3764596111L ) )
		bFoundRom = TRUE;
	if( TestAndSetPath( atari_5200_filename, "atari5200.rom", 3182898204L ) )
		bFoundRom = TRUE;
	if( TestAndSetPath( atari_5200_filename, "5200.rom", 3182898204L ) )
		bFoundRom = TRUE;
	if( TestAndSetPath( atari_5200_filename, "atari52.rom", 3182898204L ) )
		bFoundRom = TRUE;
	if( TestAndSetPath( atari_basic_filename, "ataribas.rom", 2190982779L ) )
		bFoundRom = TRUE;
	if( TestAndSetPath( atari_basic_filename, "basic.rom", 2190982779L ) )
		bFoundRom = TRUE;

	return bFoundRom;
}