// win_main.h

#include "../qcommon/qcommon.h"
#include "../client/client.h"
#include "win_local.h"
#include "resource.h"

#ifndef _GAMECUBE
#include <errno.h>
#include <float.h>
#include <fcntl.h>
#include <stdio.h>
#include <direct.h>
#include <io.h>
#include <conio.h>
#endif



//#define SPANK_MONKEYS	//----(SA)	commented out for running net developer release builds
int	sys_monkeySpank;


/*
==================
Sys_MonkeyShouldBeSpanked
==================
*/
int Sys_MonkeyShouldBeSpanked( void ) {
	return sys_monkeySpank;
}




/*
==================
Sys_FunctionCmp
==================
*/
int Sys_FunctionCmp(void *f1, void *f2) {

	int i, j, l;
	byte func_end[32] = {0xC3, 0x90, 0x90, 0x00};
	byte *ptr, *ptr2;
	byte *f1_ptr, *f2_ptr;

	ptr = (byte *) f1;
	if (*(byte *)ptr == 0xE9) {
		//Com_Printf("f1 %p1 jmp %d\n", (int *) f1, *(int*)(ptr+1));
		f1_ptr = (byte*)(((byte*)f1) + (*(int *)(ptr+1)) + 5);
	}
	else {
		f1_ptr = ptr;
	}
	//Com_Printf("f1 ptr %p\n", f1_ptr);

	ptr = (byte *) f2;
	if (*(byte *)ptr == 0xE9) {
		//Com_Printf("f2 %p jmp %d\n", (int *) f2, *(int*)(ptr+1));
		f2_ptr = (byte*)(((byte*)f2) + (*(int *)(ptr+1)) + 5);
	}
	else {
		f2_ptr = ptr;
	}
	//Com_Printf("f2 ptr %p\n", f2_ptr);

#ifdef _DEBUG
	sprintf((char *)func_end, "%c%c%c%c%c%c%c", 0x5F, 0x5E, 0x5B, 0x8B, 0xE5, 0x5D, 0xC3);
#endif
	for (i = 0; i < 1024; i++) {
		for (j = 0; func_end[j]; j++) {
			if (f1_ptr[i+j] != func_end[j])
				break;
		}
		if (!func_end[j]) {
			break;
		}
	}
#ifdef _DEBUG
	l = i + 7;
#else
	l = i + 2;
#endif
	//Com_Printf("function length = %d\n", l);

	for (i = 0; i < l; i++) {
		// check for a potential function call
		if (*((byte *) &f1_ptr[i]) == 0xE8) {
			// get the function pointers in case this really is a function call
			ptr = (byte *) (((byte *) &f1_ptr[i]) + (*(int *) &f1_ptr[i+1])) + 5;
			ptr2 = (byte *) (((byte *) &f2_ptr[i]) + (*(int *) &f2_ptr[i+1])) + 5;
			// if it was a function call and both f1 and f2 call the same function
			if (ptr == ptr2) {
				i += 4;
				continue;
			}
		}
		if (f1_ptr[i] != f2_ptr[i])
			return qfalse;
	}
	return qtrue;
}

/*
==================
Sys_FunctionCheckSum
==================
*/
int Sys_FunctionCheckSum(void *f1) {

	int i, j, l;
	unsigned shermcrap;
	byte func_end[32] = {0xC3, 0x90, 0x90, 0x00};
	byte *ptr;
	byte *f1_ptr;

	ptr = (byte *) f1;
	if (*(byte *)ptr == 0xE9) {
		//Com_Printf("f1 %p1 jmp %d\n", (int *) f1, *(int*)(ptr+1));
		f1_ptr = (byte*)(((byte*)f1) + (*(int *)(ptr+1)) + 5);
	}
	else {
		f1_ptr = ptr;
	}
	//Com_Printf("f1 ptr %p\n", f1_ptr);

#ifdef _DEBUG
	sprintf((char *)func_end, "%c%c%c%c%c%c%c", 0x5F, 0x5E, 0x5B, 0x8B, 0xE5, 0x5D, 0xC3);
#endif
	for (i = 0; i < 1024; i++) {
		for (j = 0; func_end[j]; j++) {
			if (f1_ptr[i+j] != func_end[j])
				break;
		}
		if (!func_end[j]) {
			break;
		}
	}
#ifdef _DEBUG
	l = i + 7;
#else
	l = i + 2;
#endif
	//Com_Printf("function length = %d\n", l);
	shermcrap = Com_BlockChecksum( f1_ptr, l );
	return (int)shermcrap;
}


//NOTE TTimo: heavily NON PORTABLE, PLZ DON'T USE
//  https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=447
#if 0
//----(SA) added
/*
==============
Sys_ShellExecute

-	Windows only

	Performs an operation on a specified file. 

	See info on ShellExecute() for details
  
==============
*/
int Sys_ShellExecute(char *op, char *file, qboolean doexit, char *params, char *dir ) {
	unsigned int retval;
	char *se_op;
	
	// set default operation to "open"
	if(op)	se_op = op;
	else	se_op = "open";


	// probably need to protect this some in the future so people have
	// less chance of system invasion with this powerful interface
	// (okay, not so invasive, but could be annoying/rude)


	retval = (UINT)ShellExecute(NULL, se_op, file, params, dir, SW_NORMAL);	// only option forced by game is 'sw_normal'
	
	if( retval <= 32) {	// ERROR
		Com_DPrintf("Sys_ShellExecuteERROR: %d\n", retval);
		return retval;
	}

	if ( doexit ) {
		// (SA) this works better for exiting cleanly...
		Cbuf_ExecuteText( EXEC_APPEND, "quit" );
	}

	return 999;	// success
}
//----(SA) end
#endif

/*
==================
Sys_BeginProfiling
==================
*/
void Sys_BeginProfiling( void ) {
	// this is just used on the mac build
}





/*
==============
Sys_DefaultCDPath
==============
*/
char *Sys_DefaultCDPath( void ) {
	return "";
}

/*
==============
Sys_DefaultBasePath
==============
*/
char *Sys_DefaultBasePath( void ) {
	return Sys_Cwd();
}




/*
========================================================================

EVENT LOOP

========================================================================
*/


sysEvent_t	eventQue[MAX_QUED_EVENTS];
int			eventHead, eventTail;
byte		sys_packetReceived[MAX_MSGLEN];

/*
================
Sys_QueEvent

A time of 0 will get the current time
Ptr should either be null, or point to a block of data that can
be freed by the game later.
================
*/
void Sys_QueEvent( int time, sysEventType_t type, int value, int value2, int ptrLength, void *ptr ) {
	sysEvent_t	*ev;

	ev = &eventQue[ eventHead & MASK_QUED_EVENTS ];
	if ( eventHead - eventTail >= MAX_QUED_EVENTS ) {
		Com_Printf("Sys_QueEvent: overflow\n");
		// we are discarding an event, but don't leak memory
		if ( ev->evPtr ) {
			Z_Free( ev->evPtr );
		}
		eventTail++;
	}

	eventHead++;

	if ( time == 0 ) {
		time = Sys_Milliseconds();
	}

	ev->evTime = time;
	ev->evType = type;
	ev->evValue = value;
	ev->evValue2 = value2;
	ev->evPtrLength = ptrLength;
	ev->evPtr = ptr;
}


//================================================================


/*
=================
Sys_Net_Restart_f

Restart the network subsystem
=================
*/
void Sys_Net_Restart_f( void ) {
//	NET_Restart();
}



//=======================================================================


void Sys_InitStreamThread( void ) {
}

void Sys_ShutdownStreamThread( void ) {
}

void Sys_BeginStreamedFile( fileHandle_t f, int readAhead ) {
}

void Sys_EndStreamedFile( fileHandle_t f ) {
}

int Sys_StreamedRead( void *buffer, int size, int count, fileHandle_t f ) {
   return FS_Read( buffer, size * count, f );
}

void Sys_StreamSeek( fileHandle_t f, int offset, int origin ) {
   FS_Seek( f, offset, origin );
}


void *Sys_InitializeCriticalSection() {
	return (void*)-1;
}

void Sys_EnterCriticalSection(void *ptr) {
}

void Sys_LeaveCriticalSection(void *ptr) {
}

