/*
**	Command & Conquer Renegade(tm)
**	Copyright 2025 Electronic Arts Inc.
**
**	This program is free software: you can redistribute it and/or modify
**	it under the terms of the GNU General Public License as published by
**	the Free Software Foundation, either version 3 of the License, or
**	(at your option) any later version.
**
**	This program is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**	GNU General Public License for more details.
**
**	You should have received a copy of the GNU General Public License
**	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/***********************************************************************************************
 ***              C O N F I D E N T I A L  ---  W E S T W O O D  S T U D I O S               ***
 ***********************************************************************************************
 *                                                                                             *
 *                 Project Name : combat																		  *
 *                                                                                             *
 *                     $Archive:: /Commando/Code/Combat/statemachine.h         $*
 *                                                                                             *
 *                       Author:: Patrick Smith                                                *
 *                                                                                             *
 *                     $Modtime:: 11/07/01 5:27p                                              $*
 *                                                                                             *
 *                    $Revision:: 4                                                           $*
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 * Functions:                                                                                  *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

#if defined(_MSC_VER)
#pragma once
#endif

#ifndef __STATEMACHINE_H
#define __STATEMACHINE_H


#include "simplevec.h"
#include "chunkio.h"


//////////////////////////////////////////////////////////////////////
//	Macros
//////////////////////////////////////////////////////////////////////
//#define ADD_STATE_TO_MACHINE(machine, state)		\
		//machine.Add_State (On_##state_Think, On_##state_Request_End, On_##state_Begin, On_##state_End);

/*#define ADD_STATE_TO_MACHINE(machine, state, is_think, is_request_end, is_begin, is_end)		\
		machine.Add_State (															\
			is_think ? On_##state##_Think : NULL,									\
			is_request_end ? On_##state##_Request_End : NULL,					\
			is_begin ? On_##state##_Begin : NULL,									\
			is_end ? On_##state##_End : NULL);	*/

#define ADD_STATE_TO_MACHINE(machine, state)		\
		machine.Add_State (								\
			On_##state##_Think,							\
			On_##state##_Request_End,					\
			On_##state##_Begin,							\
			On_##state##_End);


		//machine.Add_State (On_##state_Think, On_##state_Request_End, On_##state_Begin, On_##state_End);

/*#define BEGIN_STATE_MAP(machine)						\
		void	Install_##machine_States (void) {	\
			

#define ADD_STATE_HANDLER(machine, state)		\
			machine.Add_State (state);				\
			machine.
			

#define END_STATE_MAP()					\
		}*/

//#define ADD_STATE_TO_MACHINE(machine, state)		\
		//machine.Add_State (On_##state_Think, On_##state_Request_End, On_##state_Begin, On_##state_End);


/*#define DECLARE_STATE_HANDLER(state)						\
		void On_##state##_Begin (void);						\
		void On_##state##_End (void);							\
		void On_##state##_Think (void);						\
		bool On_##state##_Request_End (int new_state);*/

#define SM_BEGIN			;
#define SM_END				;
#define SM_THINK			;
#define SM_REQ_END		;
#define SM_NO_BEGIN		{}
#define SM_NO_END			{}
#define SM_NO_THINK		{}
#define SM_NO_REQ_END	{ return true; }

#define DECLARE_STATE(state, begin, end, think, req_end)			\
		void On_##state##_Begin (void) begin					\
		void On_##state##_End (void) end							\
		void On_##state##_Think (void) think					\
		bool On_##state##_Request_End (int state) req_end

#define DECLARE_STATE_HANDLER_BEGIN(state)			\
		void On_##state##_Begin (void)

#define DECLARE_STATE_HANDLER_END(state)				\
		void On_##state##_End (void)

#define DECLARE_STATE_HANDLER_THINK(state)			\
		void On_##state##_Think (void)

#define DECLARE_STATE_HANDLER_REQUEST_END(state)	\
		bool On_##state##_Request_End (int state)

#define STATE_IMPL_BEGIN(state)	\
		On_##state##_Begin

#define STATE_IMPL_END(state)	\
		On_##state##_End

#define STATE_IMPL_THINK(state)	\
		On_##state##_Think

#define STATE_IMPL_REQUEST_END(state)	\
		On_##state##_Request_End



/*#define DECLARE_STATE_HANDLER_BEGIN(state)		\
		void On_##state_Begin (void);

#define DECLARE_STATE_HANDLER_END(state)			\
		void On_##state_End (void);

#define DECLARE_STATE_HANDLER_THINK(state)		\
		void On_##state_Think (void);

#define DECLARE_STATE_HANDLER_REQUEST_END(state)		\
		void On_##state_Request_End (void);

*/

//////////////////////////////////////////////////////////////////////
//
//	StateClass
//
//////////////////////////////////////////////////////////////////////
template <class T>
class StateClass
{
public:

	///////////////////////////////////////////////////////////////////
	//	Public constructors/destructors
	///////////////////////////////////////////////////////////////////
	StateClass<T> (void)	:
		Think (NULL),
		RequestEnd (NULL),
		Begin (NULL),
		End (NULL)				{}

	///////////////////////////////////////////////////////////////////
	//	Public typedefs
	///////////////////////////////////////////////////////////////////
	typedef void (T::*THINK_PTR)			(void);
	typedef bool (T::*REQUEST_END_PTR)	(int new_state);
	typedef void (T::*BEGIN_PTR)			(void);
	typedef void (T::*END_PTR)				(void);

	///////////////////////////////////////////////////////////////////
	//	Public member data
	///////////////////////////////////////////////////////////////////
	THINK_PTR			Think;
	REQUEST_END_PTR	RequestEnd;
	BEGIN_PTR			Begin;
	END_PTR				End;
};


//////////////////////////////////////////////////////////////////////
//
//	StateMachineClass
//
//////////////////////////////////////////////////////////////////////
template <class T>
class StateMachineClass
{
public:

	///////////////////////////////////////////////////////////////////
	//	Public typedefs
	///////////////////////////////////////////////////////////////////
	typedef StateClass<T>	STATE_OBJ;

	///////////////////////////////////////////////////////////////////
	//	Save/load constants
	///////////////////////////////////////////////////////////////////
	enum
	{
		CHUNKID_VARIABLES		= 0x11070946,

		VARID_CURR_STATE		= 0,
		VARID_IS_HALTED,
	};

	///////////////////////////////////////////////////////////////////
	//	Public constructors/destructors
	///////////////////////////////////////////////////////////////////
	StateMachineClass (T *object)	:
		CurrState (-1),
		Object (object),
		IsHalted (false)						{}
	
	virtual ~StateMachineClass (void)	{}

	///////////////////////////////////////////////////////////////////
	//	Public methods
	///////////////////////////////////////////////////////////////////
	
	///////////////////////////////////////////////////////////////////
	//	Think
	///////////////////////////////////////////////////////////////////
	void	Think (void)
	{
		if (IsHalted) {
			return ;
		}

		//
		//	Call the think member function of the container
		//
		if (Is_Valid_State (CurrState)) {
			if (StateTable[CurrState].Think != NULL) {
				(Object->*(StateTable[CurrState].Think)) ();
			}
		}

		return ;
	}

	///////////////////////////////////////////////////////////////////
	//	Add_State
	///////////////////////////////////////////////////////////////////
	void	Add_State (StateClass<T> &state)
	{
		StateTable.Add (state);
		return ;
	}

	///////////////////////////////////////////////////////////////////
	//	Add_State
	///////////////////////////////////////////////////////////////////
	void	Add_State
	(
		STATE_OBJ::THINK_PTR think_ptr,
		STATE_OBJ::REQUEST_END_PTR request_ptr,
		STATE_OBJ::BEGIN_PTR begin_ptr,
		STATE_OBJ::END_PTR end_ptr
	)
	{
		StateClass<T> state;
		state.Think			= think_ptr;
		state.RequestEnd	= request_ptr;
		state.Begin			= begin_ptr;
		state.End			= end_ptr;

		Add_State (state);
		return ;
	}

	///////////////////////////////////////////////////////////////////
	//	Halt_State
	///////////////////////////////////////////////////////////////////
	void	Halt_State (void)
	{
		if (IsHalted) {
			return ;
		}

		//
		//	Notify the current state that it's "ending"
		//
		if (Is_Valid_State (CurrState)) {
			
			if (StateTable[CurrState].End != NULL) {
				(Object->*(StateTable[CurrState].End)) ();
			}
		}
		
		IsHalted = true;
		return ;
	}

	///////////////////////////////////////////////////////////////////
	//	Resume_State
	///////////////////////////////////////////////////////////////////
	void	Resume_State (void)
	{
		if (IsHalted == false) {
			return ;
		}

		//
		//	Notify the current state that it's starting again
		//
		if (Is_Valid_State (CurrState)) {			
			if (StateTable[CurrState].Begin != NULL) {
				(Object->*(StateTable[CurrState].Begin)) ();
			}
		}
		
		IsHalted = false;
		return ;
	}

	///////////////////////////////////////////////////////////////////
	//	Get_State
	///////////////////////////////////////////////////////////////////
	int	Get_State (void)
	{
		return CurrState;
	}

	///////////////////////////////////////////////////////////////////
	//	Set_State
	///////////////////////////////////////////////////////////////////
	void	Set_State (int state_index, bool force = false)
	{
		if (state_index == CurrState && force == false) {
			return ;
		}

		//
		//	First, check to see if we can switch states
		//
		if (IsHalted == false && Is_Valid_State (CurrState)) {
			if (force == false && StateTable[CurrState].RequestEnd != NULL) {
				if ((Object->*(StateTable[CurrState].RequestEnd)) (state_index) == false) {
					return ;
				}
			}

			//
			//	Now, notify the old state that it's ending
			//
			if (StateTable[CurrState].End != NULL) {
				(Object->*(StateTable[CurrState].End)) ();
			}
		}

		if (Is_Valid_State (state_index)) {
						
			//
			//	Switch to the new state
			//
			CurrState = state_index;
			if (IsHalted == false && StateTable[CurrState].Begin != NULL) {
				(Object->*(StateTable[CurrState].Begin)) ();
			}
		} else {
			CurrState = -1;
		}

		return ;
	}

	///////////////////////////////////////////////////////////////////
	//	Save
	///////////////////////////////////////////////////////////////////
	void Save (ChunkSaveClass &csave)
	{
		csave.Begin_Chunk (CHUNKID_VARIABLES);
			WRITE_MICRO_CHUNK (csave, VARID_CURR_STATE,	CurrState);
			WRITE_MICRO_CHUNK (csave, VARID_IS_HALTED,	IsHalted);
		csave.End_Chunk ();		
		return ;
	}

	///////////////////////////////////////////////////////////////////
	//	Load
	///////////////////////////////////////////////////////////////////
	void Load (ChunkLoadClass &cload)
	{
		while (cload.Open_Chunk ())
		{
			switch (cload.Cur_Chunk_ID ())
			{
				case CHUNKID_VARIABLES:
					Load_Variables (cload);
					break;
		  
				default:
					WWDEBUG_SAY (("Unrecognized StateMachineClass chunkID\n"));
					break;

			}
			cload.Close_Chunk ();
		}

		return ;
	}

	///////////////////////////////////////////////////////////////////
	//	Load_Variables
	///////////////////////////////////////////////////////////////////
	void Load_Variables (ChunkLoadClass &cload)
	{
		while (cload.Open_Micro_Chunk ()) {
			switch (cload.Cur_Micro_Chunk_ID ())
			{
				READ_MICRO_CHUNK (cload, VARID_CURR_STATE,	CurrState);
				READ_MICRO_CHUNK (cload, VARID_IS_HALTED,		IsHalted);
				
				default:
					WWDEBUG_SAY (("Unrecognized StateMachineClass variable chunkID\n"));
					break;
			}
			cload.Close_Micro_Chunk ();
		}

		return ;
	}

protected:
	
	///////////////////////////////////////////////////////////////////
	//	Protected methods
	///////////////////////////////////////////////////////////////////

	///////////////////////////////////////////////////////////////////
	//	Is_Valid_State
	///////////////////////////////////////////////////////////////////
	bool	Is_Valid_State (int index)
	{
		return (index >= 0 && index < StateTable.Count ());
	}

	///////////////////////////////////////////////////////////////////
	//	Protected member data
	///////////////////////////////////////////////////////////////////
	SimpleDynVecClass<STATE_OBJ>	StateTable;
	T *									Object;
	int									CurrState;
	bool									IsHalted;
};


#endif //__STATEMACHINE_H
