/****

Windows functionality that is definitely
not project specific.

****/

// To link code to main C functions 

extern "C" {

#include "3dc.h"
#include "inline.h"

// For modifications necessary to make Alt-Tabbing
// behaviour (WM_ACTIVATEAPP) work full screen.
// This is necessary to support full screen
// ActiveMovie play.

#define SupportAltTab Yes

// Globals

static HANDLE RasterThread;

// Externs

extern BOOL bActive;

// These function are here solely to provide a clean
// interface layer, since Win32 include files are fully
// available in both C and C++.
// All functions linking to standard windows code are
// in win_func.cpp or win_proj.cpp, and all DirectX 
// interface functions
// should be in dd_func.cpp (in the Win95 directory)
// or d3_func.cpp, dp_func.cpp, ds_func.cpp etc.
// Project specific platfrom functionality for Win95
// should be in project/win95, in files called 
// dd_proj.cpp etc.


// GetTickCount is the standard windows return
// millisecond time function, which isn't actually
// accurate to a millisecond.  In order to get FRI
// to work properly with GetTickCount at high frame 
// rates, you will have to switch KalmanTimer to Yes
// at the start of io.c to turn on a filtering algorithm
// in the frame counter handler.  
// Alternately, we can use the mm function 
// timeGetTime to get the time accurate to a millisecond.
// There is still enough variation in this to make
// the kalman filter probably worthwhile, however.

long GetWindowsTickCount(void)

{
    #if 0
	return GetTickCount();
	#else
	return timeGetTime();
	#endif
}

// This function is set up using a PeekMessage check,
// with a return on a failure of GetMessage, on the
// grounds that it might be more stable than just
// GetMessage.  But then again, maybe not.  
// PM_NOREMOVE means do not take this message out of
// the queue.  The while loop is designed to ensure
// that all messages are sent through to the Windows
// Procedure are associated with a maximum of one frame's
// delay in the main engine cycle, ensuring that e.g.
// keydown messages do not build up in the queue.

// if necessary, one could extern this flag
// to determine if a task-switch has occurred which might
// have trashed a static display, to decide whether to
// redraw the screen. After doing so, one should reset
// the flag

BOOL g_bMustRedrawScreen = FALSE;

void CheckForWindowsMessages(void)
{
	MSG         msg;
	extern signed int MouseWheelStatus;
	
	MouseWheelStatus = 0;

	// Initialisation for the current embarassingly primitive mouse 
	// handler...

	do
	{
		while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
		{
			if (!GetMessage(&msg, NULL, 0, 0))
				return;

			TranslateMessage(&msg);
			DispatchMessage(&msg);

			#if (!SupportAltTab)
			// Panic
			if (!bActive)
			{
				// Dubious hack...
				#if 0
				ExitSystem();
				#else
				ReleaseDirect3D();
				exit(0x00);
				#endif
			}
			#endif
		}
		
		// JH 13/2/98 - if the app is not active we should not return from the message lopp
		// until the app is re-activated
		
		if (!bActive)
		{
			ResetFrameCounter();
			Sleep(0);
			g_bMustRedrawScreen = TRUE;
		}
	}
		while (!bActive);
}




// Experimental functions to handle a separate
// thread to run rasterisation on hardware at low
// priority.

// Note that the RenderD3DScene function does not need
// to call ExitThread explictly - the return at the
// end of the function will do this for this, giving
// thread exit code equal to the return value from
// the function.

/*
  Note that this assumes DrawPerFrame mode!!!
  necessary for some hardware accelerators anyway
  (deferred texturing problem!!!)
*/

BOOL SpawnRasterThread()

{
	DWORD RasterThreadId;
	// Stack size of new thread in bytes.
	// For the moment, we will set it to
	// 128K, the normal size for the engine
	// process.
	// Note that this is in bytes.
	// Note that stack size should grow as 
	// necessary.  We hope.
	DWORD StackSize = 128 * 1024;


    // Create the thread
    RasterThread = CreateThread(
	   NULL,     // no security
	   StackSize,        // default stack size
	   (LPTHREAD_START_ROUTINE) RenderD3DScene,
	   0,        // no argument for function
	   0,        // default creation flags
	   &RasterThreadId); // get thread ID

    if (RasterThread == NULL)
	  {
       #if debug
	   ReleaseDirect3D();
	   exit(0xabab);
	   #else
	   return FALSE;
       #endif
	  }

    #if 1
	// Set the priority on the thread to
	// below normal, since we want this thread
	// to be unimportant --- it is only monitoring
	// the hardware rasteriser.  Hopefully.
	// Note that this priority value maybe should
	// be THREAD_PRIORITY_LOWEST or THREAD_PRIORITY_IDLE,
	// or maybe we shouldn't call this function at all.
	// Also, we must have a THREAD_SET_INFORMATION
	// access right associated with the thread for this
	// to work.  Hopefully, this should be the default
	// when using CreateThread.
	SetThreadPriority(RasterThread, 
	   THREAD_PRIORITY_NORMAL);
	#endif

	return TRUE;
}

BOOL WaitForRasterThread()

{
    BOOL RetVal;
	DWORD ThreadStatus;
	int i;

    // Note that if this is to work the 
    // rasterisation thread must have a
	// THREAD_QUERY_INFORMATION access right,
	// but we believe CreateThread should supply
	// this as a default.

    // Note!!! At some stage we may want to put a
	// delay loop in the statement below, in the
	// time honoured Saturn fashion, depending on how
	// much impact calling GetExitCodeThread has on the
	// rest of the system - hopefully not much...

    do
	  {
       RetVal = GetExitCodeThread(RasterThread,
	                    &ThreadStatus);
	  }
	while ((RetVal == TRUE) && 
	      (ThreadStatus == STILL_ACTIVE));

    // Failed to get a status report on the thread
	if (RetVal == FALSE)
	  {
	   #if debug
	   ReleaseDirect3D();
	   exit(0xabbb);
	   #else
	   return FALSE;
	   #endif
	  }

	return TRUE;
}


/*
  Pick up processor types,
  either from assembler test (note
  I have asm to do this, but it must 
  be converted from as / Motorola format
  to masm / Intel), or (more likely) from
  a text file left by the launcher, which
  can use GetProcessorType from the 
  mssetup api
*/

#ifdef __WATCOMC__

unsigned int GetCPUId(void);
#pragma aux GetCPUId = "mov eax,1" "cpuid" value [edx] modify [eax ebx ecx];

#elif defined(_MSC_VER)

static unsigned int GetCPUId(void)
{
	unsigned int retval;
	_asm
	{
		mov eax,1
		_emit 0x0f   ; CPUID (00001111 10100010) - This is a Pentium
		             ; specific instruction which gets information on the
		_emit 0xa2   ; processor. A Pentium family processor should set
		             ; bits 11-8 of eax to 5.
		mov retval,edx
	}
	return retval;
}

#else

#error "Unknown compiler"

#endif


PROCESSORTYPES ReadProcessorType(void)
{
	SYSTEM_INFO SystemInfo;
	int ProcessorType;
	PROCESSORTYPES RetVal;

    GetSystemInfo(&SystemInfo);

    ProcessorType = SystemInfo.dwProcessorType;

    switch (ProcessorType)
	  {
	   case PROCESSOR_INTEL_386:
		 RetVal = PType_OffBottomOfScale;
		 break;

	   case PROCESSOR_INTEL_486:
		 RetVal = PType_486;
		 break;

	   case PROCESSOR_INTEL_PENTIUM:
		 if (GetCPUId() & 0x00800000)
		 	RetVal = PType_PentiumMMX;
		 else
		 	RetVal = PType_Pentium;
		 break;

       #if 0
	   case PROCESSOR_INTEL_SOMETHING:
		 RetVal = PType_Klamath;
		 break;
	   #endif

	   default:
	     RetVal = PType_OffTopOfScale;
		 break;
	  }

	return RetVal;
}



// End of extern C declaration 

};




