#include "Gos.pch"
#include "App.h"
#include "stdio.h"

void  (*GosApp::Terminate)() = App::Terminate;
DWORD (*GosApp::GetStatus)() = App::GetStatus;		// (DWORD)  Bit 0 is true when the app is active, Bit 1 is true when the app has keyboard focus
bool  (*GosApp::RunMainLoop)( void(__stdcall *)() ) = App::RunMainLoop;
bool  (*GosApp::InternalRunLogic)( void(__stdcall *)() ) = App::InternalRunLogic;
void  (*GosApp::Body)() = App::Body;
bool  (*GosApp::MsgLoop)() = App::MsgLoop;
GosApp*(*GosApp::CurrentApp)() = App::CurrentApp;

void(__stdcall *DoScriptUpdate)(int)=0;

extern void StartGosViewServerThreads();
extern void InitLibraries();
extern void DestroyLibraries();



extern DWORD gPerfPolyCount;	// NOTE THIS IS NOT PERMANENT! - just a temporary way to view this one perf value



GosApp *App::pGosApp = NULL;
int		App::s_frameNo=0;
_int64	App::s_lastTime=0;
int		App::s_lastFrameNo=0;
int		App::s_lastPolyCount=0;



GosApp::GosApp()
{
	App::RegisterApp( this );
}

void GosApp::PreferredDisplay( int &Windowed, unsigned int &Width, unsigned int &Height, D3DFORMAT &Format )
{
	return;
}


GosApp *App::CurrentApp()
{
	return pGosApp;
}

App::RegisterApp( GosApp *pNewApp )
{
	if( pGosApp )
	{
		STOP(("Error: App %s is registered, can't register App %s", pGosApp->Name(), pNewApp->Name() ));
	}
	pGosApp = pNewApp;
}

LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
	switch( msg )
	{
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_MOUSEMOVE:
		GosUser::SetMouse( wParam, (WORD)(lParam), (WORD)(lParam >> 16) );
		break;
	}
	
	return DefWindowProc( hWnd, msg, wParam, lParam );
}

int APIENTRY WinMain
(
    HINSTANCE   hInstance,
    HINSTANCE   hPrevInstance,
    LPSTR       lpNonUnicodeCmdLine,
    int         nCmdShow
)
//--------------------------------------
{
    // have to get the Command line like this for unicode reasons
    LPTSTR  lpCmdLine;
    lpCmdLine = GetCommandLine();
	InitLibraries();

	// SPEW_ENABLE(_T("Heaps"));
	SPEW_ENABLE(_T("Error"));
	SPEW_ENABLE(_T("LeakDetail"));
//	SPEW_ENABLE(_T("FrameRate"));


	if( !GosApp::CurrentApp() )
	{
		STOP(("No application has been registered"));
	}

	WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
		GetModuleHandle(NULL), NULL, NULL, NULL, NULL, GosApp::CurrentApp()->Name(), NULL };

	RegisterClassEx( &wc );

	DWORD dwWindowStyle = WS_POPUP|WS_CAPTION|WS_SYSMENU|WS_THICKFRAME|
                          WS_MINIMIZEBOX|WS_VISIBLE;

	int windowed = true;
	unsigned int width=320;
	unsigned int height=240;
	D3DFORMAT format = D3DFMT_UNKNOWN;

	StartGosViewServerThreads();

	GosApp::CurrentApp()->PreferredDisplay( windowed, width, height, format );

	HWND hWnd = CreateWindow( GosApp::CurrentApp()->Name(), GosApp::CurrentApp()->Name(),
		dwWindowStyle /* WS_OVERLAPPEDWINDOW */, 0, 0, width, height,
		GetDesktopWindow(), NULL, hInstance /* wc.hInstance */, NULL );

	GosUser::Initialize( hWnd, hInstance);
	GosUtil::Initialize();
	Gos3D::Initialize( hWnd );
	GosText::Initialize();

	App::s_lastTime = GosUtil::Time_uS();

	if( GosApp::CurrentApp()->Initialize( GetCommandLine() ) )
	{
		ShowWindow( hWnd, SW_SHOWDEFAULT );
		UpdateWindow( hWnd );

		GosApp::Body();

	}

	GosApp::CurrentApp()->Shutdown();

	GosText::Shutdown();
	Gos3D::Shutdown();
	GosUser::Shutdown();
	GosHeap::Shutdown();
	DestroyLibraries();

	UnregisterClass( GosApp::CurrentApp()->Name(), wc.hInstance );	

	return 0;
}

void App::Body()				// Default body which then calls MsgLoop
{

	// This function is only here so it can be overridden by the app
	// .. which can control high-level program flow
	GosApp::MsgLoop();
}

bool App::MsgLoop()			// Handles window messages
{
	LOG_API("App::MsgLoop");

	MSG msg;
	ZeroMemory( &msg, sizeof(msg) );

	while( msg.message != WM_QUIT )
	{
		if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
		{
			TranslateMessage( &msg );
			DispatchMessage( &msg );
		}
		else
		{
			GosEventLog::LogStartPoint();
			_int64 newTime = GosUtil::Time_uS();
			s_frameNo++;
			if( newTime - s_lastTime > 1000000 )	// 1 second
			{
				float FrameRate = ((float)(s_frameNo-s_lastFrameNo))*1000000.f/(newTime-s_lastTime);
				float PolyRate = ((float)(gPerfPolyCount - s_lastPolyCount))*1000000.f/(newTime-s_lastTime);
				SPEW(_T("FrameRate"),(_T("FrameRate = %d  (%d mS/frame) PolyRate = %d Poly/S\n"),
					(int)FrameRate,(int)(1000.f/FrameRate),(int)PolyRate));
				s_lastFrameNo = s_frameNo;
				s_lastTime = newTime;
				s_lastPolyCount = gPerfPolyCount;
			}


			GosUser::Poll();

			if( !App::pGosApp->Step() )
				return false;

			Gos3D::Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, 0x0000ff, 1.0f, 0L );

			App::pGosApp->Render();

			Gos3D::Done();
		}
	}
	return true;
}


void App::Terminate()
{
	_asm int 3;
}

DWORD App::GetStatus()
{
	_asm int 3;
	return 0;
}

bool App::RunMainLoop( void(__stdcall *DoGameLogic)() )
{
	_asm int 3;
	return false;
}

bool App::InternalRunLogic( void(__stdcall *DoGameLogic)() )
{
	_asm int 3;
	return false;
}

