////===========================================================================//
//// File:        rp4lbe4.cc                                                   //
//// Project: MUNGA           Brick: Red Planet LBE Application                //
//// Contents:                                                                 //
////---------------------------------------------------------------------------//
////   Date    Who  Modification                                               //
//// --------  ---  ---------------------------------------------------------- //
////                                                                           //
////---------------------------------------------------------------------------//
//// Copyright (C) 1994, Virtual World Entertainment, Inc.                     //
//// All Rights reserved worldwide                                             //
//// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
////===========================================================================//
//
#define WIN32_LEAN_AND_MEAN
#define	_WIN32_WINNT	0x0500
#define WINVER			0x0500

#include <tchar.h>
#include "rpl4.h"
#pragma hdrstop

#include "rpl4pb.h"
#include "rpl4fe.h"
#include "rpl4console.h"
#include "rpl4lobby.h"
#include "..\munga_l4\l4steamtransport.h"
#include "..\munga_l4\l4splr.h"
#include "rpl4ver.h"
#include "..\munga\resver.h"
#include "..\munga\resource.h"
// added for game status drawing support
#include "..\munga\player.h"
#include "..\munga\mission.h"
#include "..\munga_l4\l4ctrl.h"
// end add
#include <DbgHelp.h>
#include <strsafe.h>
#include <direct.h>
#include <shellapi.h>

#define SPOOL_SIZE 0x600000

//#define SCREEN_WIDTH	1024
//#define SCREEN_HEIGHT	768

Byte version[3] = {MAJOR_DATA_VERSION, RELEASE_VERSION, MINOR_DATA_VERSION};

char* names[] = {"InitializingState",
	"WaitingForEgg",
	"LoadingMission",
	"WaitingForLaunch",
	"LaunchingMission",
	"RunningMission",
	"EndingMission",
	"StoppingMission",
	"SuspendingMission",
	"ResumingMission",
	"AbortingMission",
	"CreatingMission",
	"ApplicationStateCount"};

//##########################################################################
//##################    RP4L4Application Globals    ########################
//##########################################################################

extern const char* const ProgName;
const char* const ProgName = "rpl4";
Application *rpl4App;
HWND hWnd;

//
// Unhandled-exception minidump: rpl4crash.dmp lands beside the exe so
// a crash on a test machine hands back a stack. dbghelp loads lazily -
// no link-time dependency.
//
static LONG WINAPI RPL4CrashDumpFilter(EXCEPTION_POINTERS *exception)
{
	HMODULE dbghelp = LoadLibraryA("dbghelp.dll");
	if (dbghelp != NULL)
	{
		typedef BOOL (WINAPI *MiniDumpWriteDumpFn)(
			HANDLE, DWORD, HANDLE, MINIDUMP_TYPE,
			PMINIDUMP_EXCEPTION_INFORMATION,
			PMINIDUMP_USER_STREAM_INFORMATION,
			PMINIDUMP_CALLBACK_INFORMATION);
		MiniDumpWriteDumpFn write_dump =
			(MiniDumpWriteDumpFn) GetProcAddress(dbghelp, "MiniDumpWriteDump");
		if (write_dump != NULL)
		{
			HANDLE file = CreateFileA("rpl4crash.dmp", GENERIC_WRITE, 0,
				NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
			if (file != INVALID_HANDLE_VALUE)
			{
				MINIDUMP_EXCEPTION_INFORMATION dump_info;
				dump_info.ThreadId = GetCurrentThreadId();
				dump_info.ExceptionPointers = exception;
				dump_info.ClientPointers = FALSE;
				write_dump(GetCurrentProcess(), GetCurrentProcessId(), file,
					MiniDumpNormal, &dump_info, NULL, NULL);
				CloseHandle(file);
			}
		}
	}
	return EXCEPTION_CONTINUE_SEARCH;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_CLOSE:
		DestroyWindow(hWnd);
	   return 0;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_SYSCOMMAND:
		// Alt is a flight control (reverse thrust) - releasing it must
		// not pop the window menu and steal keyboard focus
		if ((wParam & 0xFFF0) == SC_KEYMENU)
		{
			return 0;
		}
		break;
	}
	return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
	char filename[50];
	strcpy_s(filename, 50, "rpl4.log");

   std::ofstream logfile;
   logfile.open(filename);
   std::cout.rdbuf(logfile.rdbuf());

	SetUnhandledExceptionFilter(RPL4CrashDumpFilter);

   // load up our environment variables
   //controls
   if(getenv("L4CONTROLS") == NULL)
      putenv("L4CONTROLS=KEYBOARD");
   if(getenv("DPLARG") == NULL)
      putenv("DPLARG=1");
   if(getenv("L4DPLCFG") == NULL)
      putenv("L4DPLCFG=RPDPL.INI");
   if(getenv("MULTISAMPLE") == NULL)
      putenv("MULTISAMPLE=0");
   if(getenv("TARGETFPS") == NULL)
	   putenv("TARGETFPS=60");
   if(getenv("MAXPARTICLES") == NULL)
	   putenv("MAXPARTICLES=8192");
   FILE *file;
   char line[1024];
   if (fopen_s(&file, "environ.ini", "r") == 0)
   {
      while (!feof(file))
      {
         if (fgets(line, sizeof(line), file))
         {
            for (int i = strlen(line); i >= 0; i--)
               if (line[i] == '\n' || line[i] == '\r')
                  line[i] = 0;
            // the file is self-documenting: skip comments, blanks,
            // and anything that is not KEY=VALUE
            char *setting = line;
            while (*setting == ' ' || *setting == '\t')
               ++setting;
            if (*setting == '\0' || *setting == '#' || *setting == ';' ||
                strchr(setting, '=') == NULL)
               continue;
            putenv(setting);
         }
      }
      fclose(file);
   }

   DEBUG_STREAM << "Red Planet 4.12.2" << std::endl << std::flush;
   DEBUG_STREAM << "L4CONTROLS=" << getenv("L4CONTROLS") << std::endl << std::flush;

#ifdef RP412_STEAM
	//
	// RP412STEAM=1 (environ.ini or a Steam launch) swaps the wire to
	// ISteamNetworkingSockets. Failure logs the reason and the game
	// carries on over TCP - dev boxes without Steam are unaffected.
	//
	{
		const char *steam_switch = getenv("RP412STEAM");
		if (steam_switch != NULL && atoi(steam_switch) != 0)
		{
			SteamNetTransport_Install();
		}
	}
#endif

//   GetProcessAffinityMask(GetCurrentProcess(), 
   DWORD_PTR res = SetThreadAffinityMask(GetCurrentThread(), (DWORD_PTR)1);
   if (res == 0)
   {
      DWORD err = GetLastError();
	  DEBUG_STREAM << "Could not set thread affinity. Err Code = " << err << std::endl << std::flush;
   }

   //
   //----------------------------------------------------------------------
   // Parse command line for an egg notation file name
   //----------------------------------------------------------------------
   //
   int argc;
   LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc);
   Logical run_application = RPL4Application::ParseCommandLine(argc, argv);
   if (!run_application)
   {
      return 1;
   }
   else
   {
      Start_Registering();
   }

   //
   //-------------------------------------------------------------------------
   // Create our window. This is only temporary because this will move after
   // the graphics are moved over.
   //-------------------------------------------------------------------------
   //
   WNDCLASS wc;

   if (!hPrevInstance)
   {
      wc.style = 0; 
      wc.lpfnWndProc = (WNDPROC) WndProc;
      wc.cbClsExtra = 0; 
      wc.cbWndExtra = 0; 
      wc.hInstance = hInstance; 
      wc.hIcon = LoadIcon((HINSTANCE) NULL, IDI_APPLICATION); 
      wc.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW); 
      wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
      wc.lpszMenuName =  NULL; 
      wc.lpszClassName = L"MainWndClass"; 

      if (!RegisterClass(&wc)) 
         return FALSE; 
   }


   DWORD wsStyle = WS_OVERLAPPED | WS_SYSMENU;
   if (L4Application::GetFullscreen())
      wsStyle = WS_POPUP;
   hWnd = CreateWindowEx(0, L"MainWndClass", L"RPL4", WS_OVERLAPPEDWINDOW, 0, 0, L4Application::GetScreenWidth(), L4Application::GetScreenHeight(), (HWND)NULL, (HMENU)NULL, hInstance, (LPVOID)NULL);
   if (!hWnd)
      return FALSE;

	ShowWindow(hWnd, nShowCmd);
#if !_DEBUG
	// Arcade pods have no mouse - but desktop/windowed play needs the
	// cursor for the on-screen cockpit buttons, so hide it only when
	// running fullscreen.
	if (L4Application::GetFullscreen())
	{
		ShowCursor(FALSE);
	}
#endif

	//
	//-------------------------------------------------------------------------
	// Front-end mode: started without an egg, a network, or mission review.
	// The race loop below keeps everything in ONE process - setup screen,
	// mission, teardown, and back to the setup screen - with the local
	// console marshaling each race. (-egg / -net / -mr run a single pass,
	// exactly as before.)
	//-------------------------------------------------------------------------
	//
	Logical front_end_mode =
		L4Application::GetMissionReviewMode() == 0 &&
		L4Application::GetNetworkCommonFlatAddress() == 0 &&
		!L4Application::GetEggNotationFileName();	// no egg on the command line

  int last_launch_mode = FELaunchSingle;

  for (;;)
  {
	if (front_end_mode)
	{
		if (!IsWindow(hWnd))
		{
			break;		// the cockpit window is gone (closed mid-race)
		}

		// drain any WM_QUIT left over from the previous mission's
		// teardown so it cannot instantly close the setup screen
		MSG leftover;
		while (PeekMessage(&leftover, NULL, WM_QUIT, WM_QUIT, PM_REMOVE))
		{
		}

		// lobby races: the owner publishes the score sheet, members
		// pull it - the same results screen shows on every machine
		RPL4Lobby_PushRaceResults();
		RPL4Lobby_PullRaceResults();

		// last race's final scores first (no-op on first boot)
		if (!RPL4FrontEnd_ShowResults(hInstance, hWnd))
		{
			break;
		}

		static char frontend_egg[MAX_PATH];
		if (!RPL4FrontEnd_Run(hInstance, hWnd, frontend_egg, sizeof(frontend_egg)))
		{
			break;		// player closed the menu without launching
		}
		last_launch_mode = RPL4FrontEnd_LastLaunchMode();
		Exit_Code = 0;

		if (last_launch_mode == FELaunchMember)
		{
			//
			// Lobby member: enter the race as a network pod. The
			// owner's console connects over the wire, feeds the egg,
			// and marshals us - no local egg, no local console. If
			// the owner leaves mid-race the mission ends (a race
			// with no console never stops on its own).
			//
			L4Application::SetNetworkCommonFlatAddress(1501);
			gConsoleLossEndsMission = True;
		}
		else
		{
			gConsoleLossEndsMission = False;
			L4Application::SetEggNotationFileName(frontend_egg);

			// Front-end games are marshaled by the in-process console:
			// it ends the race when the selected time expires.
			// (Hand-fed -egg runs stay unmarshaled - the developer
			// shortcut.)
			//
			// RP412HOSTPODS turns the launch into a hosted network
			// race: this pod switches to network mode (it meshes like
			// any pod) and the console also marshals the listed member
			// pods over the wire. The Steam lobby primes the same path.
			const char *host_pods = getenv("RP412HOSTPODS");
			if (host_pods != NULL && host_pods[0] != '\0')
			{
				int host_port = 1501;
				const char *port_override = getenv("RP412HOSTPORT");
				if (port_override != NULL && atoi(port_override) > 0)
				{
					host_port = atoi(port_override);
				}
				L4Application::SetNetworkCommonFlatAddress(host_port);
				if (!RPL4LocalConsole_InstallNetworkRace(
						RPL4FrontEnd_LastMissionSeconds(), frontend_egg,
						host_pods, RPL4FrontEnd_LastPilotNames()))
				{
					DEBUG_STREAM << "Network race setup failed - back to the menu\n" << std::flush;
					L4Application::SetNetworkCommonFlatAddress(0);
					continue;
				}
			}
			else
			{
				L4Application::SetNetworkCommonFlatAddress(0);
				RPL4LocalConsole_Install(RPL4FrontEnd_LastMissionSeconds());
			}
		}
	}

   //
   //-------------------------------------------------------------------------
   // Open the resource file.  The brace will help correct the scoping problem
   // of destruction the resource file
   //-------------------------------------------------------------------------
   //
   {
      StreamableResourceFile resources("rpl4.res", version);
      Check(&resources);

      //
      //----------------------------------------------
      // Create and initialize the application manager
      //----------------------------------------------
      //
      ApplicationManager* app_manager;
      Application *new_app;
      int review_mode = RPL4Application::GetMissionReviewMode();
      if (review_mode > 0)
      {
         MissionReviewApplicationManager *spool_mgr = new MissionReviewApplicationManager(hInstance, hWnd, atoi(getenv("TARGETFPS")), (review_mode == 2) ? 1 : 2, SPOOL_SIZE);
         app_manager = spool_mgr;
         Register_Object(app_manager);

         //
         //-------------------------------------------------------------------
         // Create and initialize the playback application if there is a spool
         // file
         //-------------------------------------------------------------------
         //
#if 1
		 CString spoolFileName = L4Application::GetSpoolFileName();
		 if (!spoolFileName)
			 spoolFileName = "last.spl";

         SpoolFile *spool = spool_mgr->GetStoredSpoolFile(spoolFileName);
         if (spool)
         {
            new_app = new RPL4PlaybackApplication(hInstance, hWnd, &resources, spool);
         } else
		 {
			 new_app = new RPL4IdleApplication(hInstance, hWnd, &resources);
		 }

        Register_Object(new_app);
        app_manager->StartApplication(new_app);
#else
         new_app = new RPL4PlaybackApplication(hInstance, hWnd, &resources, NULL);
         Register_Object(new_app);
         app_manager->StartApplication(new_app);
#endif

         //
         //------------------------------------
         // Initialize the spooling application
         //------------------------------------
         //
         if (review_mode == 1)
         {
            new_app = new L4SpoolingApplication(hInstance, hWnd, &resources, RPL4);
            Register_Object(new_app);
            app_manager->StartApplication(new_app);
         }
      }
      else
      {
		  app_manager = new ApplicationManager(hInstance, hWnd, atoi(getenv("TARGETFPS")));
         Register_Object(app_manager);

         //
         //--------------------------------------
         // Create and initialize the application
         //--------------------------------------
         //
         Application *new_app = new RPL4Application(hInstance, hWnd, &resources);
         rpl4App = new_app;
         Register_Object(new_app);
         SetCursorPos(1680 / 2, 1050 / 2);
         app_manager->StartApplication(new_app);
      }

      //
      //--------------------------
      // Run missions till we done
      //--------------------------
      //
      app_manager->RunMissions();
      rpl4App = NULL;
      Unregister_Object(app_manager);
      delete app_manager;
   }

	//
	//-------------------------------------------------------------------------
	// Single-binary race loop: if the local console ended this mission
	// (or we raced as a lobby member under the owner's console), go
	// back to the setup screen / lobby room for the next one; anything
	// else (user quit, -egg / -net / -mr single pass) falls out.
	//-------------------------------------------------------------------------
	//
	if (!front_end_mode ||
		(last_launch_mode != FELaunchMember && !RPL4LocalConsole_MissionCompleted()))
	{
		break;
	}
  }

#if !_DEBUG
	// symmetric with the fullscreen-only hide at startup
	if (L4Application::GetFullscreen())
	{
		ShowCursor(TRUE);
	}
#endif

   Stop_Registering();

   return Exit_Code;
}
