////////////////////////////////////////////////////////////////////////////////
//    Scorched3D (c) 2000-2011, 2025, 2026
//
//    This file is part of Scorched3D.
//
//    Scorched3D 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 2 of the License, or
//    (at your option) any later version.
//
//    Scorched3D 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, write to the Free Software Foundation, Inc.,
//    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
////////////////////////////////////////////////////////////////////////////////

#ifndef __INCLUDE_GameState_hpp_INCLUDE__
#define __INCLUDE_GameState_hpp_INCLUDE__

#include <string>
#include <map>
#include <list>
#include <engine/MainLoopI.hpp>
#include <common/Clock.hpp>

class GameStateI;
class GameStateStimulusI;
class GameState;

#define GAMESTATE_PERF_COUNTER_START( x, y ) \
	{ \
		static int __counter__ = x.getPerfCounter( y ); \
		x.startPerfCount( __counter__ ); \
	}
#define GAMESTATE_PERF_COUNTER_END( x, y ) \
	{ \
		static int __counter__ = x.getPerfCounter( y ); \
		x.endPerfCount( __counter__ ); \
	}

class GameStatePerfCounter
{
public:
	GameStatePerfCounter( const char* name );
	~GameStatePerfCounter();

	void start();
	void end();

	// clang-format off
	// uncrustify off
	const char*  getName() { return name_.c_str(); }
	unsigned int getTotal();
	bool         getUsed() { return used_; }
	// uncrustify on
	// clang-format on

protected:
	std::string  name_;
	unsigned int start_;
	unsigned int total_;
	bool         used_;
};

class MainLoop;

class GameState : public MainLoopI
{
public:
	enum MouseButton
	{
		MouseButtonLeft              = 0x1,
		MouseButtonMiddle            = 0x2,
		MouseButtonRight             = 0x4,
		MouseButtonLeftDoubleClick   = 0x8,
		MouseButtonMiddleDoubleClick = 0x16,
		MouseButtonRightDoubleClick  = 0x32
	};

	GameState( MainLoop* mainLoop, const char* name );
	virtual ~GameState();

	// Called by the simulator
	virtual void simulate( float simTime );
	virtual void draw();

	void clear();

	// Called by SDL subsystem
	void mouseDown( MouseButton button, int x, int y );
	void mouseUp( MouseButton button, int x, int y );
	void mouseMove( int x, int y );
	void mouseWheel( short z );

	// User fns to change + set state
	void setState( const unsigned int state );
	void stimulate( const unsigned int stimulus );
	void setFakeMiddleButton( bool fake );

	// clang-format off
	// uncrustify off
	unsigned int getState()            { return currentState_; }
	int          getMouseX()           { return currentMouseX_; }
	int          getMouseY()           { return currentMouseY_; }
	bool&        getStateLogging()     { return stateLogging_; }
	bool&        getStateTimeLogging() { return stateTimeLogging_; }
	// uncrustify on
	// clang-format on

	int  getPerfCounter( const char* name );
	void startPerfCount( int counter );
	void endPerfCount( int counter );

	// User fns to add classes to state management
	void addStateStimulus( const unsigned int state, const unsigned int stim, const unsigned int nexts );
	void addStateStimulus( const unsigned int state, GameStateStimulusI* check, const unsigned int nexts );
	void addStateEntry( const unsigned int state, GameStateI* entry );
	void addStateLoop( const unsigned int state, GameStateI* entry, GameStateI* subEntry );
	void addStateKeyEntry( const unsigned int state, GameStateI* subEntry );
	void addStateMouseDownEntry( const unsigned int state, const unsigned int buttons, GameStateI* subEntry );
	void addStateMouseUpEntry( const unsigned int state, const unsigned int buttons, GameStateI* subEntry );
	void addStateMouseDragEntry( const unsigned int state, const unsigned int buttons, GameStateI* subEntry );
	void addStateMouseWheelEntry( const unsigned int state, GameStateI* subEntry );

protected:
	typedef std::list< GameStateI* >                       StateIList;
	typedef std::pair< GameStateStimulusI*, unsigned int > SimulusIPair;
	typedef std::list< SimulusIPair >                      StiulusIList;

	struct TimerInfo
	{
		GameStateI*  gameStateI;
		unsigned int drawTime;
		unsigned int simulateTime;
	};

	struct GameStateSubEntry
	{
		GameStateI* current;
		StateIList  subLoopList;
	};

	struct GameStateEntry
	{
		// Classes called for every loop
		std::list< GameStateSubEntry > loopList;

		// Possible stimuli in this state
		std::map< unsigned int, unsigned int > stimList;
		// Stimili checked every loop
		StiulusIList                           condStimList;

		// Classes called on key events
		StateIList subKeyList;
		// Classes called on mouse down L
		StateIList subMouseDownLeftList;
		// Classes called on mouse down M
		StateIList subMouseDownMiddleList;
		// Classes called on mouse down R
		StateIList subMouseDownRightList;
		// Classes called on mouse up L
		StateIList subMouseUpLeftList;
		// Classes called on mouse up M
		StateIList subMouseUpMiddleList;
		// Classes called on mouse up R
		StateIList subMouseUpRightList;
		// Classes called on mouse drag L
		StateIList subMouseDragLeftList;
		// Classes called on mouse drag M
		StateIList subMouseDragMiddleList;
		// Classes called on mouse drag R
		StateIList subMouseDragRightList;
		// Classes called on mouse down W
		StateIList subMouseWheelList;
		// Classes called when entering state
		StateIList enterStateList;
	};

	MainLoop*                                mainLoop_;
	std::string                              name_;
	std::map< unsigned int, GameStateEntry > stateList_;
	unsigned int                             stateCount_;
	unsigned int                             currentState_;
	GameStateI*                              currentStateI_;
	GameStateEntry*                          currentEntry_;
	bool                                     fakeMiddleButton_;
	bool                                     stateLogging_;
	bool                                     stateTimeLogging_;
	float                                    frameTime_;
	int                                      frameCount_;
	Clock                                    timerClock_;
	Clock                                    overallTimerClock_;
	Clock                                    doubleClickClock_;
	TimerInfo                                timers_[50];

	// Dragging stuff
	// Up or down for each button (bit field)
	unsigned int currentMouseState_;
	int          mouseLDragX_, mouseLDragY_;
	int          mouseMDragX_, mouseMDragY_;
	int          mouseRDragX_, mouseRDragY_;
	int          mouseDoubleX_, mouseDoubleY_;
	int          currentMouseX_, currentMouseY_;

	void mouseMoveCall(
		const unsigned int state,
		MouseButton        button,
		StateIList&        currentList,
		int                mx,
		int                my,
		int                dx,
		int                dy
	);
	void                          mouseUpDown( MouseButton button, bool down, int x, int y );
	GameState::GameStateEntry*    getEntry( const unsigned int state );
	GameState::GameStateSubEntry* getSubEntry( const unsigned int state, GameStateI* entry );
	void                          clearTimers( bool printTimers = false );
};

#endif  // __INCLUDE_GameState_hpp_INCLUDE__
