#include "munga.h"
#pragma hdrstop

#include "appmgr.h"

HWND ghWnd = 0;

ApplicationManager* ApplicationManager::CurrentAppManager = NULL;

ApplicationManager::ApplicationManager(HINSTANCE hInstance, HWND hWnd, Scalar frame_rate) : Node(ApplicationManagerClassID), runningApplications(this)
{
	frameRate = frame_rate;
	frameDuration = 1.0f/frameRate;
	mHInstance = hInstance;
	ghWnd = hWnd;
	ApplicationManager::CurrentAppManager = this;
	Check_Fpu();
}

ApplicationManager::~ApplicationManager()
{
}

void ApplicationManager::StartApplication(Application *new_app)
{
	Check(this);
	Check(new_app);

	runningApplications.Add(new_app);
	application = new_app;
	new_app->Initialize();
}

//
//#############################################################################
// RunMissions
//#############################################################################
//
void ApplicationManager::RunMissions()
{
	Check(this);
   int backgroundTasksRun = 0;
   int foregroundTasksRun = 0;
   Time beginFrameTimestamp, lastFrameTimestamp;
	
	MSG msg;
	SChainIteratorOf<Application*> current_application(runningApplications);
	SChainOf<Application*> endedApplications(NULL);
	SChainIteratorOf<Application*> endingApplication(endedApplications);

Start_Of_Frame:
   backgroundTasksRun = 0;
   foregroundTasksRun = 0;
	beginFrameTimestamp = Now();

	current_application.First();

	lastFrameTimestamp = Now();

	Time end_of_frame = Now();
	end_of_frame += frameDuration;
	#if defined(LAB_ONLY)
		int bad_count = 0;
	#endif

	//
	//----------------------------------
	// Run all relevant foreground loops
	//----------------------------------
	//

   Time startForeground = Now();
	while ((application = current_application.ReadAndNext()) != NULL)
	{
		Check(application);
      foregroundTasksRun++;
		if (!application->ExecuteForeground(end_of_frame, frameDuration))
		{
			if (!application->Shutdown(current_application.GetSize()))
			{
				endedApplications.Add(application);

				if (current_application.GetCurrent() == NULL)
				{
					current_application.Last();
				} else
				{
					current_application.Previous();
				}
				current_application.Remove();
			}
		}
	}
   Time endForeground = Now();

	//
	//-----------------------------------------------------------------------
	// Check the Windows message queue for messages to be processed
	//-----------------------------------------------------------------------
	//
	//InvalidateRect(ghWnd, NULL, false);
	if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
	{
		if (msg.message == WM_QUIT)
      {
		  		endingApplication.First();

		while (application = endingApplication.ReadAndNext())
		{
			application->Terminate();
			delete application;
		}

			return;
      }
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	//
	//-----------------------------------------------------------------------
	// Run all relevant background loops until it is time for the next frame.
	// If no applications remain, exit the whole loop
	//-----------------------------------------------------------------------
	//
	current_application.First();
	application = current_application.GetCurrent();
	if (!application)
	{
		endingApplication.First();

		while (application = endingApplication.ReadAndNext())
		{
			application->Terminate();
			delete application;
		}
		return;
	}

	do
	{
		while (application->GetNetworkManager()->RoutePacket())
			;

		current_application.Next();
		application = current_application.GetCurrent();
	} while (application);

	current_application.First();
	application = current_application.GetCurrent();
	if (!application)
	{
		endingApplication.First();

		while (application = endingApplication.ReadAndNext())
		{
			application->Terminate();
			delete application;
		}
		return;
	}

	//
	//------------------------------------------------------------------
	// Give each application a chance to do at least one background task
	//------------------------------------------------------------------
	//
   Time startBackground = Now();
Background_Loop:
	do
	{
		Check(application);
		application->ExecuteBackgroundTask();
      backgroundTasksRun++;

		//
		// Move to the next application
		//
		current_application.Next();
		application = current_application.GetCurrent();
	} while (application);

	//
	//---------------------------------------------------------------------
	// If time remains before the next frame is due, do another pass on the
	// background tasks
	//---------------------------------------------------------------------
	//
	Time t2 = Now();
   Time lateTime = Now();
   lateTime += 15L;

   current_application.First();
   application = current_application.GetCurrent();

	if (t2 < end_of_frame)
	{

		#if defined(LAB_ONLY)
			if (end_of_frame - t2 > 0.1f)
			{
				DEBUG_STREAM << t2 << ',' << end_of_frame << endl << std::flush;
				if (++bad_count == 1000)
				{
					Fail("End-of-frame cannot be correctly calculated!");
				}
			}
		#endif
		goto Background_Loop;
	}
   Time endBackground = Now();

	//char str[256];
	//Scalar lastFrameLength = Now() - beginFrameTimestamp;
	//sprintf(str, "RPL4 - %.2f FPS", 1.0f / lastFrameLength);
	//SetWindowTextA(ghWnd, str);

	goto Start_Of_Frame;
}
