//===========================================================================//
// File:	mechweap.hpp                                                        //
// Project: BattleTech           Brick: Entity Manager                        //
// Contents: MechWeapon -- abstract base for every mounted Mech weapon        //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
//  --/--/95  JM   Initial coding.                                           //
//---------------------------------------------------------------------------//
// Copyright (C) 1995, Virtual World Entertainment, Inc. All Rights reserved //
//                      PROPRIETARY AND CONFIDENTIAL                         //
//===========================================================================//
//
// RECONSTRUCTED from the shipped binary (Ghidra pseudo-C, module cluster
// @004b95ec-@004b9d10, file=bt/mechweap.cpp; pure-virtual stub @004ba45c,
// file=bt/mechweap.hpp).  Cross-referenced HEAVILY against the SURVIVING
// derived-class sources PPC.HPP/PPC.CPP and GAUSS.HPP/GAUSS.CPP (which derive
// from Emitter : MechWeapon) and MISLANCH.HPP (MissileLauncher : ProjectileWeapon
// : MechWeapon), plus the MUNGA SUBSYSTM.HPP base interface and the RP
// WEAPSYS.h analog.  See mechweap.cpp for per-method @ADDR evidence.
//
// Inheritance chain established from the decomp:
//      Simulation -> Subsystem -> PoweredSubsystem (powersub.cpp, ctor @004b0f74)
//                 -> MechWeapon (this file, vtable @00511d2c)
//                 -> Emitter / ProjectileWeapon -> PPC / GaussRifle / MissileLauncher
//
// The MechWeapon ctor (@004b99a8) installs vtable PTR_FUN_00511d2c and chains
// to the PoweredSubsystem ctor @004b0f74, confirming the base.  PPC and Gauss
// FireWeapon() chain to Emitter::FireWeapon(); MechWeapon::FireWeapon() is the
// pure-virtual default that asserts "MechWeapon abstract class should..." (@004b95ec)
// -- the exact analog of RP GenericGun::Fire()'s "Should not be here!" stub.
//
// Field offsets in comments are byte offsets into the shipped object
// (e.g. "@0x34c" == this word index 0xd3).  Names taken from PPC.CPP
// (targetWithinRange, damageData) and the resource-parser tag strings
// (WeaponRange, DamageAmount, HeatCostToFire, RechargeRate, DamageType,
// PipPosition/PipColor/PipExtendedRange, ExplosionModelFile); the remainder
// are inferred from usage and flagged best-effort where uncertain.
//

#if !defined(MECHWEAP_HPP)
#	define MECHWEAP_HPP

#if !defined(POWERSUB_HPP)
#	include <powersub.hpp>
#endif

#if !defined(DAMAGE_HPP)
#	include <damage.hpp>
#endif

#if !defined(ALARM_HPP)
#	include <alarm.hpp>
#endif

#include <color.hpp>		// RGBColor
#include <linmtrx.hpp>		// LinearMatrix
#include <vector3d.hpp>		// Vector3D
#include <point3d.hpp>		// Point3D

//#####################    Forward Class Declarations  #######################
class Mech;
class Entity;
class ResourceFile;
class NotationFile;
class CockpitHud;

//###########################################################################
//####################   MechWeapon Model Resource   ########################
//###########################################################################
//
// Extends the PoweredSubsystem resource record.  The MechWeapon-specific
// fields begin at +0x190 in the parsed record (see CreateStreamedSubsystem
// @004b9d10).  Total record size is 0x1BC (444 bytes) -- the parser stamps
// subsystemModelSize = 0x1BC and classID = MechWeaponClassID (0xBCD).
//
	struct MechWeapon__SubsystemResource:
		public PoweredSubsystem::SubsystemResource
	{
		Scalar		rechargeRate;			// +0x190  "RechargeRate"
		Scalar		weaponRange;			// +0x194  "WeaponRange"
		ResourceDescription::ResourceID
					explosionResourceID;	// +0x198  "ExplosionModelFile"
		Scalar		damageAmount;			// +0x19C  "DamageAmount" (-> damageData)
		Enumeration	damageType;				// +0x1A0  "DamageType"  (Collision..Energy)
		Scalar		heatCostToFire;			// +0x1A4  "HeatCostToFire"
		int			pipPosition;			// +0x1A8  "PipPosition"
		RGBColor	pipColor;				// +0x1AC  "PipColor"  (3 floats, init -1)
		int			pipExtendedRange;		// +0x1B8  "PipExtendedRange"
	};											// sizeof == 0x1BC

//###########################################################################
//#########################      MechWeapon      ############################
//###########################################################################
//
//	Abstract base for any subsystem that can be aimed at a target and fired,
//	dealing damage and (optionally) spawning an explosion model and a HUD pip.
//	(vtable @00511d2c, ctor @004b99a8, dtor @004b9b9c.)
//
	class MechWeapon:
		public PoweredSubsystem
	{
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Shared Data Support
	//
	public:
		static Derivation ClassDerivations;
		static Receiver::MessageHandlerSet MessageHandlers;
		static AttributeIndexSet AttributeIndex;
		static SharedData DefaultData;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Attribute Support  (gauge data-binding wave)
	//
	// The energy/ballistic weapon engineering clusters read OutputVoltage (the
	// charge/ready level -- animates as the weapon recharges after firing) and
	// PercentDone (the recharge dial), both onto rechargeLevel (1.0 == ready).
	// GetAttributeIndex() chains to PoweredSubsystem's so InputVoltage + the
	// HeatSink temps also resolve; Emitter/PPC/ProjectileWeapon/MissileLauncher
	// point their DefaultData at MechWeapon::GetAttributeIndex() to inherit it
	// (they carried an EMPTY default-constructed index -> resolved NOTHING).
	//
	public:
		enum {
			OutputVoltageAttributeID = PoweredSubsystem::NextAttributeID,
			PercentDoneAttributeID,
			NextAttributeID
		};
	private:
		static const IndexEntry AttributePointers[];
	public:
		static AttributeIndexSet& GetAttributeIndex();

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Update record (replication) -- extends the base Simulation record with
	// the weapon-specific alarm/idle state appended by WriteUpdateRecord.
	//
	public:
		struct MechWeapon__UpdateRecord:
			public PoweredSubsystem::UpdateRecord
		{
			Enumeration	alarmState;		// weaponAlarm state
			int			weaponIdle;		// 1 == idle (alarm level 0)
		};
		typedef MechWeapon__UpdateRecord UpdateRecord;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Firing -- the central abstract operation
	//
	//	Pure-virtual in spirit: the base body @004b95ec just asserts
	//	"MechWeapon abstract class should [not be fired]".  Emitter (PPC, Gauss)
	//	and ProjectileWeapon (MissileLauncher) provide the real implementations;
	//	PPC::FireWeapon / GaussRifle::FireWeapon chain up to Emitter::FireWeapon.
	//
	protected:
		virtual void
			FireWeapon();								// @004b95ec (abstract stub)

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Subsystem virtual overrides (slots on vtable @00511d2c)
	//
	//	Slot indices verified by diffing against the Subsystem vtable tail of
	//	@0050e170 and the PoweredSubsystem vtable @0050f9d8:
	//		slot  6  TakeDamage(Damage&)        (Subsystem base @0041bd34)
	//		slot  7  WriteUpdateRecord(...)     (Subsystem base @0041c500)
	//		slot  9  ReadUpdateRecord(...)      (PoweredSubsystem base @004b0efc)
	//		slot 10  ResetToInitialState()      (PoweredSubsystem base @004b0e6c)
	//		slot 13  PrintState()               (PoweredSubsystem base @004b1224)
	//
	public:
		void
			TakeDamage(Damage &damage);					// slot  6, @004b964c
		void
			ResetToInitialState();						// slot 10, @004b96ec
		void
			PrintState();								// slot 13, @004b9d00

	protected:
		void
			WriteUpdateRecord(							// slot  7, @004b9690
				UpdateRecord	*message,
				int				update_model
			);
		void
			ReadUpdateRecord(UpdateRecord *message);	// slot  9, @004b96d4

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Test Class Support
	//
	public:
		static Logical
			TestClass(Mech&);
		Logical
			TestInstance() const;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Targeting / aiming helpers (used by FireWeapon and the per-frame update)
	//
	protected:
		// E3: read the owning Mech's CURRENT target slot (mech+0x388, the Entity* that
		// mech4.cpp's targeting step writes) directly -- the old `hasTarget` member was
		// never set (the Mech per-frame refresh isn't reconstructed) so the fire gate was
		// permanently false.  GetOwningSimulation is non-const, hence the cast.
		Logical
			HasActiveTarget() const
			{
				char *o = (char *)owner;	// inherited MechSubsystem::owner (the Mech)
				return (o != 0 && *(void **)(o + 0x388) != 0) ? True : False;
			}

		// @004b9bdc -- recompute rangeToTarget and the heat-degraded
		// effectiveRange, then set targetWithinRange.  No target => range -1.
		// Returns the resulting targetWithinRange flag (consumed by Emitter /
		// ProjectileWeapon simulation).
		Logical
			UpdateTargeting();

		// @004b9608 -- rising-edge detector on fireImpulse (best-effort name);
		// returns True the frame the weapon's discharge signal goes positive.
		Logical
			CheckFireEdge();		// TODO: confirm semantic (recharge/discharge edge)

		// @004b9cbc -- copy the owning Mech's current target position.
		void
			GetTargetPosition(Point3D &position);

		// @004b9cdc -- direction from a supplied point to the weapon mount.
		void
			GetVectorToWeapon(const Point3D &from, Vector3D &direction);

		// @004b9948 -- resolve the weapon's muzzle/site world position from the
		// owning Mech's segment table.
		void
			GetMuzzlePoint(Point3D &point);

	public:
		//
		// Beam-render accessors (the visible-beam layer draws each weapon's beam
		// from its own live state: real muzzle + the authored per-weapon colour).
		//
		void			MuzzlePoint(Point3D &p)		{ GetMuzzlePoint(p); }
		const RGBColor	&PipColor() const			{ return pipColor; }

		//
		// Reticle/HUD accessors (the pip registration wiring @part_014.c:5390:
		// per weapon the 1996 build reads TargetWithinRange / WeaponRange /
		// PipPosition / PipColor / PipExtendedRange / SimulationState).
		//
		Scalar			WeaponRange() const			{ return weaponRange; }
		int				PipPosition() const			{ return pipPosition; }
		int				PipExtendedRange() const	{ return pipExtendedRange; }
		void			*WithinRangePtr()			{ return &targetWithinRange; }
		void			*WeaponAlarmPtr()			{ return &weaponAlarm; }
		void			*SimulationStatePtr()		{ return &simulationState; }	// attr 1 ("SimulationState" -- damage state; 1 = Destroyed)
		Scalar			*RechargeLevelPtr()			{ return &rechargeLevel; }		// the pip ready source (1.0 == loaded/ready)

	protected:

		// @004b9864 -- build an aim orientation (atan2 of a direction vector).
		void
			ComputeAimOrientation(LinearMatrix &orientation, const Vector3D &direction);

		// @004b9728 -- register the targeting "pip" / fire marker with the
		// owning Mech's cockpit HUD manager.
		void
			DrawWeaponPip(const LinearMatrix &transform);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Construction and Destruction
	//
	public:
		typedef MechWeapon__SubsystemResource SubsystemResource;

		MechWeapon(
			Mech				*owner,
			int					subsystem_ID,
			SubsystemResource	*subsystem_resource,
			SharedData			&shared_data = DefaultData
		);
		~MechWeapon();

		static int
			CreateStreamedSubsystem(					// @004b9d10
				ResourceFile				*resource_file,
				NotationFile				*model_file,
				const char					*model_name,
				const char					*subsystem_name,
				SubsystemResource			*subsystem_resource,
				NotationFile				*subsystem_file,
				const ResourceDirectories	*directories,
				int							passes = 1
			);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Local data for the weapon base class.
	// Offsets are byte offsets into the shipped object (PoweredSubsystem
	// occupies everything below +0x31c).
	//
	protected:
		// --- recharge / firing state ---
		Scalar	fireImpulse;			// @0x31C  reset 0   (edge-detected; best-effort name)
		Scalar	rechargeLevel;			// @0x320  reset 1.0 (1.0 == fully charged/ready)
		Scalar	rangeToTarget;			// @0x324  distance to current target (-1 if none)
		Scalar	effectiveRange;			// @0x328  (1 - heatLoad) * weaponRange  (heat-degraded)
		Scalar	weaponRange;			// @0x32C  resource WeaponRange (configured max)
		int		pipState;				// @0x330  reset 0  (TODO: name -- pip/active state)
		Logical	usesExternalModel;		// @0x334  resource model-name compare flag (best-effort)
		int		pipPosition;			// @0x338  resource PipPosition
		RGBColor	pipColor;			// @0x33C  resource PipColor (3 floats, init -1,-1,-1)
		int		pipExtendedRange;		// @0x348  resource PipExtendedRange
		Logical	targetWithinRange;		// @0x34C  PPC.CPP: "if (targetWithinRange)"

		// --- damage / display alarm ---
		GaugeAlarm54	weaponAlarm;	// @0x350  0x54 AlarmIndicator (ctor FUN_0041b9ec); level @0x364 -> ends 0x3A4

		Scalar	previousFireImpulse;	// @0x3A4  reset 0  (fire-edge history, see @004b9608)
		Damage	damageData;				// @0x3A8  damageType @0x3A8, damageAmount @0x3AC
										//         PPC.CPP: Damage energy_damage(damageData)

		Scalar	heatCostToFire;			// @0x3D8  resource HeatCostToFire
		Scalar	rechargeRate;			// @0x3DC  resource RechargeRate
		Logical	useConfiguredPip;		// @0x3E0  = (usesExternalModel == 0)  (best-effort)
		ResourceDescription::ResourceID
				explosionResourceID;	// @0x3E4  resource ExplosionModelFile
		Scalar	recoil;					// @0x3E8  reset 0  (Emitter::firingArmed aliases this slot)
		int		segmentPageIndex;		// @0x3EC  resource segmentIndex (+0x28) -- LAST field, binary ends 0x3F0

		// The binary MechWeapon ends at 0x3F0 (segmentPageIndex is the last ctor write,
		// @004b99a8 param_1[0xfb]).  The former tail fields segmentReference/pipSegment/
		// hasTarget/targetPoint/muzzlePoint were PHANTOM (no binary storage) and, worse,
		// collided with the Emitter subclass's own fields at 0x3F0+; removed:
		//   * segmentReference -> the muzzle segment is the inherited base slot this+0xdc
		//     (GetMuzzlePoint resolves it live via the mech4 BTResolveWeaponMuzzle bridge)
		//   * pipSegment       -> DrawWeaponPip gates on useConfiguredPip
		//   * hasTarget        -> dead (HasActiveTarget reads owner+0x388)
		//   * targetPoint      -> GetTargetPosition reads owner+0x37c
		//   * muzzlePoint      -> resolved live by GetMuzzlePoint

		friend struct MechWeaponLayoutCheck;
	};

	struct MechWeaponLayoutCheck {
		static_assert(offsetof(MechWeapon, weaponAlarm)         == 0x350, "MechWeapon::weaponAlarm @0x350 (0x54 alarm)");
		static_assert(offsetof(MechWeapon, previousFireImpulse) == 0x3A4, "MechWeapon::previousFireImpulse @0x3A4 (alarm ends here)");
		static_assert(offsetof(MechWeapon, recoil)              == 0x3E8, "MechWeapon::recoil @0x3E8");
		static_assert(offsetof(MechWeapon, segmentPageIndex)    == 0x3EC, "MechWeapon::segmentPageIndex @0x3EC (last field)");
		static_assert(sizeof(MechWeapon)                        == 0x3F0, "sizeof(MechWeapon) 0x3F0");
	};

#endif
