//===========================================================================//
// File:	btl4mssn.cpp                                                        //
// Project: BattleTech           Brick:                                       //
// Contents: Implementation for the L4 (game-level) mission   				  //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 04/07/95  ECH  Initial coding.                                            //
// 09/12/95  JM	Derived from L4Mission												  //
//---------------------------------------------------------------------------//
// Copyright (C) 1995, Virtual World Entertainment, Inc.                     //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//
//
// RECONSTRUCTED from the shipped binary BTL4OPT.EXE.  The only non-trivial
// method, SetPlayerData, is recovered in full from the Ghidra pseudo-C at
// @004d2c30 (the lone function the linker attributed to bt_l4/btl4mssn.cpp
// via its embedded assert path "d:\tesla_bt\bt_l4\BTL4MSSN.CPP").  The
// constructor / destructor were recovered from the adjacent code cluster
// (@004d2be0 ctor, @004d2c04 deleting-dtor) and from the BTL4Mission vtable
// @0051e870 = { dtor@004d2c04, SetPlayerData@004d2c30 }.  TestInstance is a
// debug-only consistency check; it is absent from the OPT-build vtable
// (inlined to nothing) and is reconstructed best-effort to mirror the
// surviving Red-Planet analog RPL4Mission::TestInstance.
//
// Ground-truth interfaces consulted (all survived in source):
//      BTL4MSSN.HPP   - this class (BTL4Mission : public BTMission)
//      BTMSSN.HPP/CPP - the BTMission base (member layout + SetPlayerData stub)
//      SCNROLE.HPP    - ScenarioRole (per-role damage modifiers)
// Structural analog (Red Planet): RP_L4/RPL4MSSN.cpp / .h.
//
// Member-offset map (this == BTMission*, observed byte offsets):
//      +0x20  colorName        char*   "color"        (Mission base)
//      +0x24  badgeName        char*   "badge"        (Mission base)
//      +0x28  dropZoneName     char*   "dropzone"     (Mission base)
//      +0x2c  gameModel        char*   "vehicle"      (Mission base)
//      +0x3c  dropZoneHost     Logical "loadzones"    (Mission base)
//      +0x4c  playerBitmapIndex int    "bitmapindex"  (Mission base)
//      +0x50  scenarioRoles    locked dictionary <CString -> ScenarioRole*>
//      +0x68  (name-bitmap table; GetLargeNameBitmap)
//      +0xe4  experienceLevel  Enumeration            (BTMission)
//      +0xe8  roleName         CString  "role"        (BTMission)
//      +0xec  teamName         CString  (see note)    (BTMission)
//      +0xf0  advancedDamageOn Logical  "advancedDamage" (BTMission)
//      +0xf4  missionTemperature Scalar "temperature" (BTMission, set in ctor)
//      +0xf8  patchName        CString  "patch"       (BTMission)
//
// Engine-helper name mapping (FUN_ -> intended symbol):
//      FUN_0049b54c  BTMission::BTMission(notation_file, resources)   [base ctor]
//      FUN_0049b648  BTMission::~BTMission (deleting thunk)
//      FUN_0049b6bc  BTMission::SetPlayerData (Fail stub - "should never reach")
//      DAT_004efc94  the global Application*
//      FUN_00404088  NotationFile::GetEntry(node, key, const char**)  -> Logical
//      FUN_004040d8  NotationFile::GetEntry(node, key, int*/Logical*) -> Logical
//      FUN_00404118  NotationFile::GetEntry(node, key, Scalar*)       -> Logical
//      FUN_00402300  strdup (+ Register_Pointer)
//      FUN_00402a98  CString::operator=(const char*)
//      FUN_004023f4 / FUN_00402460  CString constructors
//      FUN_004024d8  CString release (ref-count)
//      FUN_00402298  operator new (Make_Pointer)
//      FUN_00429a9c  ScenarioRole::ScenarioRole(role_name, model_file)
//      FUN_004323c7 / FUN_00432405  dictionary Locker acquire / release
//      FUN_004d4b58  strcmp
//      FUN_004dbb24  DEBUG_STREAM << (stream insert)
//      FUN_004d9c38  std::flush / endl on DEBUG_STREAM
//      FUN_004036a0  PostQuitMessage / Exit(AbortExitCodeID)
//      FUN_0040385c  Verify()/Fail(msg, file, line)
//      DAT_00524e20 / DAT_00524e68  the two debug streams (info / error)
//

#include <bt.hpp>
#pragma hdrstop

#if !defined(BTL4MSSN_HPP)
#	include <btl4mssn.hpp>
#endif
#if !defined(BTL4APP_HPP)
#	include <btl4app.hpp>
#endif
#if !defined(L4GREND_HPP)
#	include <l4grend.hpp>
#endif
#if !defined(L4HOST_HPP)
#	include <l4host.hpp>
#endif
#if !defined(HOSTMGR_HPP)
#	include <hostmgr.hpp>
#endif
#if !defined(NOTATION_HPP)
#	include <notation.hpp>
#endif
#if !defined(SCNROLE_HPP)
#	include <scnrole.hpp>
#endif

//##########################################################################
//###########################    BTL4Mission    ############################
//##########################################################################

//
//#############################################################################
// @004d2be0 -- chains to BTMission(notation_file, resources) (FUN_0049b54c),
// then stamps the BTL4Mission vtable (&0051e870).  Adds no data members of
// its own (the surviving header has an empty class body); the base ctor reads
// "mission"/"temperature" and seeds experienceLevel / advancedDamageOn.
//#############################################################################
//
BTL4Mission::BTL4Mission(
	NotationFile *notation_file,
	ResourceFile *resources
):
	BTMission(notation_file, resources)
{
	Check_Fpu();
}

//
//#############################################################################
// @004d2c04 (deleting-destructor thunk; chains to ~BTMission @0049b648).
// The author-level body is the trivial debug check, exactly as in the
// Red-Planet analog ~RPL4Mission.
//#############################################################################
//
BTL4Mission::~BTL4Mission()
{
	Check(this);
	Check_Fpu();
}

//
//#############################################################################
// Best-effort: not present in the OPT-build vtable @0051e870 (only the dtor
// and SetPlayerData slots survive), so the body was inlined away.  Mirrors
// RPL4Mission::TestInstance -- defer to the base and report healthy.
//#############################################################################
//
Logical
	BTL4Mission::TestInstance() const
{
	BTMission::TestInstance();
	return True;
}

//
//#############################################################################
// @004d2c30 -- the override of the BTMission::SetPlayerData "should never
// reach" stub (BTMSSN.CPP / FUN_0049b6bc).  Pulls this host's player page
// out of the notation file and populates the mission's player description:
// model, dropzone, color, badge, patch, experience level, advanced-damage
// flag, scenario role (+ its damage modifiers), and the plasma-display badge
// bitmap.  Occupies vtable slot 1 (it is virtual via BTMission, though the
// surviving headers do not repeat the `virtual` keyword).
//#############################################################################
//
void
	BTL4Mission::SetPlayerData(NotationFile *notation_file)
{
	Check(this);

	//
	//--------------------------------------------------------------
	// Figure out which page we should be using to create the player
	//--------------------------------------------------------------
	//
	Check(application);
	HostManager *host_manager = application->GetHostManager();
	Check(host_manager);
	L4Host *host = (L4Host*)host_manager->GetLocalHost();
	Check(host);
	CString
		player_node = host->GetSymbolicName();

	//
	//----------------------------------------------------------
	// Copy the game model and dropzone strings into the mission
	//----------------------------------------------------------
	//
	gameModel = NULL;					// @+0x2c
	const char *player_model = NULL;
	notation_file->GetEntry(player_node, "vehicle", &player_model);
	if (!player_model)
	{
		DEBUG_STREAM << "Error: vehicle not specified!\n" << std::flush;
		PostQuitMessage(AbortExitCodeID);
	}
	Check_Pointer(player_model);
	gameModel = strdup(player_model);
	Register_Pointer(gameModel);

	dropZoneName = NULL;				// @+0x28
	const char *drop_zone = NULL;
	notation_file->GetEntry(player_node, "dropzone", &drop_zone);
	if (!drop_zone)
	{
		DEBUG_STREAM << "Error: dropzone not specified!\n" << std::flush;
		PostQuitMessage(AbortExitCodeID);
	}
	Check_Pointer(drop_zone);
	dropZoneName = strdup(drop_zone);
	Register_Pointer(dropZoneName);

	dropZoneHost = False;				// @+0x3c
	notation_file->GetEntry(player_node, "loadzones", &dropZoneHost);

	//
	//----------------------------------------------------------
	// Color
	//----------------------------------------------------------
	//
	colorName = NULL;					// @+0x20
	const char *color_name = NULL;
	notation_file->GetEntry(player_node, "color", &color_name);
	if (!color_name)
	{
		DEBUG_STREAM << "Error: color not specified!\n" << std::flush;
		PostQuitMessage(AbortExitCodeID);
	}
	Check_Pointer(color_name);
	colorName = strdup(color_name);
	Register_Pointer(colorName);

	//
	//----------------------------------------------------------
	// Patch (CString member, @+0xf8)
	//----------------------------------------------------------
	//
	const char *patch_name = NULL;
	notation_file->GetEntry(player_node, "patch", &patch_name);
	if (!patch_name)
	{
		DEBUG_STREAM << "Error: patch not specified!\n" << std::flush;
		PostQuitMessage(AbortExitCodeID);
	}
	Check_Pointer(patch_name);
	patchName = patch_name;

	//
	//----------------------------------------------------------
	// Badge (legacy char* member, @+0x24)
	//----------------------------------------------------------
	//
	badgeName = NULL;					// @+0x24
	const char *badge_name = NULL;
	notation_file->GetEntry(player_node, "badge", &badge_name);
	if (!badge_name)
	{
		DEBUG_STREAM << "Error: badge not specified!\n" << std::flush;
		PostQuitMessage(AbortExitCodeID);
	}
	Check_Pointer(badge_name);
	badgeName = strdup(badge_name);
	Register_Pointer(badgeName);

	//
	//----------------------------------------------------------
	// teamName (CString, @+0xec).  NOTE: the shipped binary reads the
	// "badge" key here as well (a second, distinct "badge" string
	// literal is present in .rdata) and defaults to "DEFAULT" when the
	// page omits it.  Faithful to BTL4OPT.EXE @004d2c30.
	//----------------------------------------------------------
	//
	const char *team_name = NULL;
	if (!notation_file->GetEntry(player_node, "badge", &team_name))
	{
		teamName = "DEFAULT";
	}
	else
	{
		Check_Pointer(team_name);
		teamName = team_name;
	}

	//
	//----------------------------------------------------------
	// Experience level (@experienceLevel +0xe4).  Absence is tolerated
	// only for the "camera" page; "Unspecified" leaves the default.
	//----------------------------------------------------------
	//
	const char *experience = "Unspecified";
	if (!notation_file->GetEntry(player_node, "experience", &experience))
	{
		if (strcmp(gameModel, "camera") != 0)
		{
			DEBUG_STREAM << player_node << " page missing expereince!" << std::endl;
			Fail("Exiting", "d:\\tesla_bt\\bt_l4\\BTL4MSSN.CPP", 240);
		}
	}

	if (strcmp("Unspecified", experience) != 0)
	{
		if (strcmp(experience, "novice") == 0)
		{
			experienceLevel = BTMission::NoviceMode;
		}
		else if (strcmp(experience, "standard") == 0)
		{
			experienceLevel = BTMission::StandardMode;
		}
		else if (strcmp(experience, "veteran") == 0)
		{
			experienceLevel = BTMission::VeteranMode;
		}
		else if (strcmp(experience, "expert") == 0)
		{
			experienceLevel = BTMission::ExpertMode;
		}
		else
		{
			DEBUG_STREAM << player_node
				<< " has an unknown experience field of "
				<< experience;
			Fail("Exiting", "d:\\tesla_bt\\bt_l4\\BTL4MSSN.CPP", 264);
		}
	}

	//
	//----------------------------------------------------------
	// Advanced damage flag (@advancedDamageOn +0xf0)
	//----------------------------------------------------------
	//
	Logical advanced_damage = False;
	if (notation_file->GetEntry(player_node, "advancedDamage", &advanced_damage))
	{
		advancedDamageOn = advanced_damage ? True : False;
	}

	//
	//----------------------------------------------------------
	// Scenario role (@roleName +0xe8) -- every player page must name a
	// role, and the role page must name a model.  The ScenarioRole is
	// looked up (and created on first use) in the mission's role
	// dictionary (this+0x50), then its per-page score modifiers applied.
	//----------------------------------------------------------
	//
	const char *role_node = NULL;
	if (!notation_file->GetEntry(player_node, "role", &role_node))
	{
		DEBUG_STREAM << "Each Player page must have a role field !" << std::endl;
		Fail("Exiting", "d:\\tesla_bt\\bt_l4\\BTL4MSSN.CPP", 451);
	}
	else
	{
		Check_Pointer(role_node);
		roleName = role_node;

		const char *role_model = NULL;
		if (!notation_file->GetEntry(role_node, "model", &role_model))
		{
			DEBUG_STREAM << role_node << " page or model field missing !" << std::endl;
			Fail("Exiting", "d:\\tesla_bt\\bt_l4\\BTL4MSSN.CPP", 445);
		}
		else
		{
			//
			// Find-or-create the ScenarioRole keyed by roleName.  The DOS
			// binary kept the per-mission role dictionary inline (this+0x50)
			// and called dictionary.Find()/.Add() directly; the WinTesla
			// Mission base exposes that dictionary (scenarioRoleChain) through
			// GetScenarioRole()/AddScenarioRole() -- the latter already guards
			// against duplicate role names, so the inner Find() is folded in.
			//
			ScenarioRole *role = GetScenarioRole(roleName);
			if (role == NULL)
			{
				role = new ScenarioRole(roleName, CString(role_model));
				Register_Object(role);
				AddScenarioRole(role);
			}

			//
			// Optional per-page score modifiers.
			//
			Scalar value;
			if (notation_file->GetEntry(role_node, "dmgInflctdMdfr", &value))
			{
				role->SetDamageInflictedModifier(value);	// ScenarioRole +0x0c
			}
			if (notation_file->GetEntry(role_node, "dmgRcvdMdfr", &value))
			{
				role->SetDamageReceivedModifier(value);		// ScenarioRole +0x10
			}
			if (notation_file->GetEntry(role_node, "dmgBias", &value))
			{
				role->SetDamageBias(value);					// ScenarioRole +0x18
			}
			if (notation_file->GetEntry(role_node, "friendlyFire", &value))
			{
				role->SetFriendlyFirePenalty(value);		// ScenarioRole +0x14
			}
			int return_from_death;
			if (notation_file->GetEntry(role_node, "return", &return_from_death))
			{
				role->SetReturnFromDeath(return_from_death);	// ScenarioRole +0x28
			}
			if (notation_file->GetEntry(role_node, "killBonus", &value))
			{
				role->SetKillBonus(value);					// ScenarioRole +0x1c
			}
		}
	}

	//
	//~~~~~~~~~~~~~~~~~~~~~~
	// Get playerBitmapIndex (@+0x4c) and, if present, blit the badge
	// bitmap onto the external (plasma) display.
	//~~~~~~~~~~~~~~~~~~~~~~
	//
	if (!notation_file->GetEntry(player_node, "bitmapindex", &playerBitmapIndex))
	{
		DEBUG_STREAM << "No Bitmap for player" << player_node;
		DEBUG_STREAM << std::flush;
	}
	else
	{
		BitMap *bitmap = GetLargeNameBitmap(playerBitmapIndex);
		if (bitmap != NULL)
		{
			Check(application);
			L4GaugeRenderer
				*gauge_renderer = ((BTL4Application*)application)->
					GetGaugeRenderer();
			if (gauge_renderer != NULL)
			{
				Check(gauge_renderer);
				GraphicsDisplay *graphics_display =
					gauge_renderer->GetExternalDisplay();

				if (graphics_display != NULL)
				{
					graphics_display->DrawBitMapOpaque(
						1,									// foreground color
						0,									// background color
						0xFF,								// bitmask
						GraphicsDisplay::Replace,	// operation
						0,									// rotation
						0, 0,								// position
						bitmap,
						0,									// sx1
						0,									// sy1
						bitmap->Data.Size.x - 1,	// sx2
						bitmap->Data.Size.y - 1		// sy2
					);
					graphics_display->Update(True);
					graphics_display->WaitForUpdate();
				}
			}
		}
	}

	Check_Fpu();
}
