//===========================================================================//
// File:	heatfamily_reslice.hpp                                              //
// Project: BattleTech           Brick: Entity Manager                        //
// Contents: Re-slice of the heat/power family class bodies that fell OUTSIDE //
//           the first heat decomp window (gap 0x4ae4d8 - 0x4afbe0).          //
//---------------------------------------------------------------------------//
// Copyright (C) 1995, Virtual World Entertainment, Inc. All Rights reserved //
//                      PROPRIETARY AND CONFIDENTIAL                         //
//===========================================================================//
//
// RECONSTRUCTED from the shipped binary (heatfamily_gap.c).  This header is a
// companion to heat.hpp: it COMPLETES the Condenser class that heat.hpp/.cpp
// left best-effort, and adds the three other heat/power-family classes whose
// bodies live in the same gap:
//
//   class            primary vtable   ctor       classID   base
//   ---------------  --------------   --------   -------   -----------------
//   Condenser        0050ed88         @4ae568    0x0BBD    HeatSink
//   <HeatSink agg>   0050ed4c/ed50    @4ae8d0    0x0BBE    HeatSink   (name unconfirmed)
//   HeatWatcher      0050ed10         @4aeb40    0x0BBF    HeatableSubsystem
//   Reservoir        0050ecd4/ecd8    @4af408    0x0BC0    HeatSink
//
// classID order (resource record +0x20) dovetails with the powersub family:
//   ...0x0BBD Condenser, 0x0BBE <agg>, 0x0BBF HeatWatcher, 0x0BC0 Reservoir,
//      0x0BC1 Generator, 0x0BC2 PoweredSubsystem, ... 0x0BD5 PowerWatcher.
//
// HeatWatcher is the missing intermediate base of powersub's PowerWatcher
// (ctor @4b18a4 chains to the HeatWatcher base ctor @4aeb40).  CLASSMAP.md
// previously listed PowerWatcher directly under Subsystem; the real chain is
//   PowerWatcher : HeatWatcher : HeatableSubsystem : Subsystem.
//
// Field offsets are byte offsets into the shipped object (word index * 4).
// Hex float constants resolved from the CODE-section literal pool:
//   _DAT_004ae530 = 1.0f      _DAT_004ae708 = -1.0f    _DAT_004ae89c = 300.0f
//   _DAT_004ae8a0 = 3.0f      _DAT_004ae8a4 = 1.0f     _DAT_004ae8a8 = 0.0f
//   _DAT_004ae8ac = 1.0e-4f   _DAT_004aee28 = -1.0f    _DAT_004aeedc = 0.0f
//   _DAT_004af3a0 = 1.0e-4f   _DAT_004af3a4 = 0.0f     _DAT_004af3a8 = 0.0f
//   _DAT_004af404 = 0.0f      _DAT_004af694 = -1.0f
//   _DAT_004ae974 ~= 1.738 (double 0x3FFBCC.., thermalMass-per-heat-sink scale)
//   _DAT_004af518 ~= 0.x   (double, coolant-capacity-per-master scale; low word)
//

#if !defined(HEATFAMILY_RESLICE_HPP)
#	define HEATFAMILY_RESLICE_HPP

#if !defined(HEAT_HPP)
#	include <heat.hpp>		// HeatableSubsystem, HeatSink, Condenser base decls
#endif

//###########################################################################
//###############   Watcher-branch-LOCAL layout types   #####################
//###########################################################################
//
// BASE-CHAIN RE-BASE: the binary's HeatWatcher/PowerWatcher use a 0xC-byte
// connection (watchedLink) and a 0x54-byte gauge alarm (heatAlarm/watchdogAlarm
// -- a full GaugeAlarm), NOT the 4-byte SubsystemConnection / 8-byte HeatAlarm
// used on the HeatSink/PoweredSubsystem heat-leaf branch.  These Watcher-LOCAL
// types close the 156-byte base deficit so a Watcher-branch leaf's own fields
// begin at 0x1D8 (== Torso::currentTwist, the gyro cross-link target), WITHOUT
// resizing the shared heat.hpp types the working heat leaves depend on.  All
// access is through the API (Resolve/GetLevel), never a raw internal offset, so
// the interior padding layout is immaterial -- only the total sizeof must match.
//
	struct WatchedConnection				// binary watchedLink: 0xC bytes
	{
		Subsystem	*linked;
		char		_pad[0x0C - sizeof(Subsystem*)];
		WatchedConnection(int = 0)			{ linked = 0; }
		void		Add(Subsystem *s)		{ linked = s; }
		Subsystem*	Resolve() const			{ return linked; }
		void		Clear()					{ linked = 0; }
	};
	static_assert(sizeof(WatchedConnection) == 0x0C, "Watcher connection must be 0xC to close the deficit");

	struct WatcherGaugeAlarm				// binary watcher alarm: 0x54 bytes (full GaugeAlarm)
	{
		int		levelCount;
		int		level;
		char	_pad[0x54 - 2 * sizeof(int)];
		WatcherGaugeAlarm(int levels = 0)	{ levelCount = levels; level = 0; }
		void	Initialize(int levels)		{ levelCount = levels; level = 0; }
		void	SetLevel(int n)				{ level = n; }
		int		GetLevel() const			{ return level; }
		int		Level() const				{ return level; }
	};
	static_assert(sizeof(WatcherGaugeAlarm) == 0x54, "Watcher alarm must be 0x54 to close the deficit");

//###########################################################################
//#############################   Condenser   ###############################
//###########################################################################
//
//	COMPLETION of heat.hpp's Condenser.  It is NOT a passive subsystem (as
//	heat.hpp guessed) -- it derives from HeatSink, models a refrigeration
//	output that fights the master heat-sink's stored heat, carries a 3-way
//	valve, and reports a condenser number parsed from its instance name.
//
//	vtable @0050ed88   ctor @4ae568   dtor @4ae5fc   classID CondenserClassID
//
//	NOTE: the Condenser class + its resource record (Condenser__SubsystemResource)
//	are now declared in heat.hpp (the completed HeatSink-derived form); this file
//	only supplies the recovered method bodies for it.

	// --- Condenser instance layout (HeatSink + the following) ---
	//   Scalar	refrigerationOutput;	// @0x160 (word 0x58) recomputed each frame
	//   int		valveState;				// @0x1D0 (word 0x74) init 1 (MoveValve 0..2)
	//   int		condenserNumber;		// @0x1D4 (word 0x75) last digit of name
	//   Scalar	refrigerationFactor;	// @0x1D8 (word 0x76) from resource +0xFC
	//   Scalar	(unused/flow @0x15C set 0)// @0x15C (word 0x57)
	//   AlarmIndicator	condenserAlarm;	// @0x1DC (word 0x77) 3 levels
	//
	//   Methods recovered:
	//     @4ae4d8  RefrigerationSimulation(Scalar)  -- vtable slot 9
	//     @4ae568  Condenser(owner,id,res,shared)   -- ctor
	//     @4ae5fc  ~Condenser(byte flags)           -- vtable slot 0
	//     @4ae63c  TestInstance()                   -- IsDerivedFrom 0x50e4fc
	//     @4ae658  CreateStreamedSubsystem(...)     -- parses RefrigerationFactor
	//     @4afbe0  MoveValve(message) [best-effort] -- cycles valveState 0->1->2->0,
	//                                                   drives owner HUD gauges

//###########################################################################
//###########################   HeatWatcher   ###############################
//###########################################################################
//
//	Abstract monitor that watches ANOTHER subsystem's temperature and raises
//	a 3-level degradation/failure alarm.  Base class of powersub's
//	PowerWatcher (the "PowerWatcher base ctor @4aeb40" called out in the task).
//
//	vtable @0050ed10   ctor @4aeb40   dtor @4aebe8   classID 0x0BBF
//
	// RESOURCE-LAYOUT FIX (RESOURCE_AUDIT.md root cause A): HeatWatcher is a light
	// MechSubsystem, NOT a full HeatableSubsystem -- verified from the binary: ctor
	// @4aeb40 -> FUN_004ac644 (MechSubsystem base ctor), parser @4aec54 -> FUN_004ac9ec
	// (MechSubsystem parse) + only Degradation/Failure/Watched, record ENDS 0xF0.  The
	// resource MUST inherit MechSubsystem__SubsystemResource (ends 0xE4); inheriting
	// HeatableSubsystem::SubsystemResource (0xFC) slid these 3 fields to 0xFC/0x100/0x104.
	struct HeatWatcher__SubsystemResource:
		public MechSubsystem__SubsystemResource
	{
		int		watchedSubsystem;		// +0xE4  "WatchedSubsystem" (index, +2 bias)
		Scalar	degradationTemperature;	// +0xE8  "DegradationTemperature" (sentinel -1.0f)
		Scalar	failureTemperature;		// +0xEC  "FailureTemperature"     (sentinel -1.0f)
	};									// ends 0xF0

	// Compile-time guard: the record is loaded verbatim at binary offsets, so these MUST hold.
	static_assert(offsetof(HeatWatcher__SubsystemResource, watchedSubsystem)       == 0xE4, "HeatWatcher watchedSubsystem must be at 0xE4");
	static_assert(offsetof(HeatWatcher__SubsystemResource, degradationTemperature) == 0xE8, "HeatWatcher degradationTemperature must be at 0xE8");
	static_assert(offsetof(HeatWatcher__SubsystemResource, failureTemperature)     == 0xEC, "HeatWatcher failureTemperature must be at 0xEC");
	static_assert(sizeof(HeatWatcher__SubsystemResource)                           == 0xF0, "HeatWatcher record must end at 0xF0");

	class HeatWatcher:
		public MechSubsystem		// was HeatableSubsystem (approximation); real base is
									// MechSubsystem (ctor @4aeb40 -> FUN_004ac644).  Re-basing
									// also un-shadows degradation/failureTemperature.
	{
	public:
		static Derivation	*GetClassDerivations();	// name "HeatWatcher"
		static SharedData	DefaultData;

		typedef HeatWatcher__SubsystemResource SubsystemResource;

		typedef void
			(HeatWatcher::*Performance)(Scalar time_slice);
		void
			SetPerformance(Performance performance)
			{
				Check(this);
				activePerformance = (Simulation::Performance)performance;
			}

		static Logical	TestClass(Mech&);
		Logical			TestInstance() const;		// @4aec38
		void			ResetToInitialState(Logical powered);		// @4aea9c (slot 10)

		HeatWatcher(
			Mech				*owner,
			int					subsystem_ID,
			SubsystemResource	*subsystem_resource,
			SharedData			&shared_data = DefaultData
		);											// @4aeb40
		~HeatWatcher();							// @4aebe8

		// @4aeac4 -- the registered Performance: resolve the watched subsystem,
		// read its currentTemperature (+0x114) and drive the alarm.
		void			WatchSimulation(Scalar time_slice);

		static int		CreateStreamedSubsystem(	// @4aec54 (468 bytes)
			NotationFile				*model_file,
			const char					*model_name,
			const char					*subsystem_name,
			SubsystemResource			*subsystem_resource,
			NotationFile				*subsystem_file,
			const ResourceDirectories	*directories,
			int							passes = 1
		);

	public:
		WatchedConnection	watchedLink;		// @0x114 (0xC) resolved subsystem  -> 0x120
		Scalar			degradationTemperature;	// @0x120 (word 0x48) resource +0xE8
		Scalar			failureTemperature;		// @0x124 (word 0x49) resource +0xEC
		int				watchedSubsystem;		// @0x128 (word 0x4a) resource +0xE4
		WatcherGaugeAlarm	heatAlarm;			// @0x12C (0x54) 3 levels  -> ends 0x180
	};	// HeatWatcher own region 0x114..0x180 (0x6C); PowerWatcher own follows at 0x180.

	// BASE-CHAIN RE-BASE locks: prove the Watcher base ends where the binary needs.
	// (engine Subsystem 0xE0 + MechSubsystem 0x34 = 0x114 == HeatWatcher start.)
	static_assert(offsetof(HeatWatcher, watchedLink) == 0x114, "HeatWatcher watchedLink must be at 0x114");
	static_assert(offsetof(HeatWatcher, heatAlarm)   == 0x12C, "HeatWatcher heatAlarm must be at 0x12C (0x54-byte alarm)");
	static_assert(sizeof(HeatWatcher)                == 0x180, "HeatWatcher must end at 0x180 (PowerWatcher own follows)");

//###########################################################################
//############################   Reservoir   ################################
//###########################################################################
//
//	Coolant reservoir: a HeatSink that stores a coolant charge and "squirts"
//	it into the linked sinks on demand (the slot-14 DrawCoolant SOURCE).
//
//	vtable @0050ecd4/ecd8   ctor @4af408   classID 0x0BC0
//
	struct Reservoir__SubsystemResource:
		public HeatableSubsystem::SubsystemResource
	{
		Scalar	coolantCapacity;		// +0xFC   "CoolantCapacity"   (sentinel -1.0f)
		Scalar	coolantSquirtMass;		// +0x100  "CoolantSquirtMass" (sentinel -1.0f)
	};

	class Reservoir:
		public HeatSink
	{
	public:
		static Derivation	*GetClassDerivations();	// name "Reservoir"
		static SharedData	DefaultData;

		typedef Reservoir__SubsystemResource SubsystemResource;

		typedef void
			(Reservoir::*Performance)(Scalar time_slice);
		void
			SetPerformance(Performance performance)
			{
				Check(this);
				activePerformance = (Simulation::Performance)performance;
			}

		static Logical	TestClass(Mech&);
		Logical			TestInstance() const;	// @4af564 -> IsDerivedFrom 0x50e650

		Reservoir(
			Mech				*owner,
			int					subsystem_ID,
			SubsystemResource	*subsystem_resource,
			SharedData			&shared_data = DefaultData
		);											// @4af408

		// @4aef78 -- registered Performance (this[7..9] = &CoolantInjection via 0050e6b4)
		void			CoolantSimulation(Scalar time_slice);
		// @4aefa4 (1019 bytes) -- distribute coolant ("squirt") across the
		// linked heat-sink network, clamped per-sink and per-frame.
		void			InjectCoolant(Scalar time_slice);

		// @4af3b0 -- slot-14 DrawCoolant override: hand out up to coolantLevel.
		Scalar			DrawCoolant(Scalar requested);

		Logical			HandleMessage(int message);	// @4aee70 ("InjectCoolant" msg)
		void			PrintState();				// @4aeef8 (ReservoirState)
		void			GetState(/*...*/);			// @4aef40 (slot 7 state query)

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

	public:
		// HeatSink base ... then Reservoir adds:
		HeatAlarm		reservoirAlarm;			// @0x1D0 (word 0x74) 2 levels
		Scalar			coolantCapacity;		// @0x128 overlays HeatSink thermalCapacity
		Scalar			coolantSquirtMass;		// @0x224 (word 0x89) resource +0x100
		int				injectActive;			// @0x228 (word 0x8a) init 0
		Scalar			squirtEfficiency;		// @0x22C (word 0x8b) init 0.5f
		Scalar			injectAccumulator;		// @0x228 inject elapsed-time accumulator
		// coolantLevel @0x12C (word 0x4b) init = capacity.
	};

//###########################################################################
//###############   HeatSink aggregate class @0050ed4c/ed50   ###############
//###########################################################################
//
//	BEST-EFFORT / NAME UNCONFIRMED.  A HeatSink subclass (classID 0x0BBE) that
//	parses "HeatSinkCount" + "AmbientTemperature" and conducts the stored heat
//	toward an ambient setpoint while drawing coolant.  Appears to model the
//	mech's aggregate / master heat-sink bank.  The only standalone name string
//	adjacent to its field strings is "HeatSink" (pooled with the resource
//	field name), so the Derivation name cannot be pinned down from the dump.
//
//	vtable @0050ed4c/ed50   ctor @4ae8d0   classID 0x0BBE   base HeatSink
//
//	struct ...__SubsystemResource : HeatableSubsystem::SubsystemResource
//	{ int heatSinkCount; /* +0xFC */ };
//
//	Recovered members / methods:
//	  @0x1D0 (word 0x74) heatSinkCount      (from resource +0xFC; scales mass)
//	  @0x1D4 (word 0x75) ambientTemperature (init 300.0f)
//	  @0x1D8 (word 0x76) helper sub-object  (FUN_004afa1a, vtable 0050ecc4)
//	  @4ae724  Simulation (slot 9) -> base HeatSink slot9 (@4ad748)
//	  @4ae73c (351 bytes) Performance: conduct toward ambient + DrawCoolant
//	  @4ae8d0  ctor      @4ae9c0 TestInstance (IsDerivedFrom 0x50e590)
//	  @4ae9dc  CreateStreamedSubsystem (parses HeatSinkCount)

//###########################################################################
//#############   Engine collection-iterator helpers (NOT heat)   ###########
//###########################################################################
//
//	The thunks @4af9cf..@4afb96 are tiny ctor/dtor wrappers for the engine's
//	generic collection/iterator library (vtables 0050eccc, 0050ecc4, 0050ecbc,
//	0050ec6c, 0050ec1c), used as locals inside Reservoir::InjectCoolant
//	(@4aefa4).  They are out of scope for the heat family and are documented
//	in heatfamily_reslice.cpp only for completeness.

#endif
