// ------------------------------------------------------------------------
/*
	\author: Jaap Suter

	Copyright (C) Electronic Arts Canada Inc. 2002.  All rights reserved.	
*/
// ------------------------------------------------------------------------

/*! \file */

#ifndef REVEAL_TREE_H
#define REVEAL_TREE_H

#include "reveal/color.h"
#include "reveal/gui_colors.h"

#include "reveal/callback/callback_0.h"
#include "reveal/callback/callback_1.h"
#include "reveal/callback/callback_2.h"

#include "reveal/impl/menu.h"
#include "reveal/impl/mru_menu_forward.h"
#include "reveal/impl/range_control.h"
#include "reveal/impl/option_control.h"

#include "reveal/impl/gui_metrics.h"

#include "reveal/global_enable.h"

#include <stddef.h>

namespace Reveal {
	
	class Serialize;
	class InputState;
	class Renderer;

	namespace Impl
	{
		class Widget;

// ------------------------------------------------------------------------------
/*!
	\brief Menu-tree system based class;

	Because of MSVC 6's crappy template-member support, we have to include their definitions inside
	the class body. This obfuscates the class definition a lot, but oh well. Good luck with that eh :).
*/
// ------------------------------------------------------------------------------
class Tree
{	
public:

	/*!
		\brief Default constructor;

		Creates an empty menu system, with just a start and pause button (with no behaviour attached to them);
	*/
    Tree( const char* rootName );

	//! Empty virtual destructor;
	virtual ~Tree() {}

	//! When screen dimensions change, the MRUs have to be re-spaced. 
	void RepositionMrus( int separationWidth );

	//! Removes a control or menu from the menu-system. If \a name is NULL, it removes the menu with the specified path,
	//! otherwise it only removes the control with that name, in that path;
	void Remove( const char* path, const char* name );

	/*!
		\brief Adds a range control widget to the tweaker system;
		
        \param T		        Type of the range your controlling (int, float, unsigned char, etc. etc.);
		\param path		        Full path from the tweaker though all its submenus, up to the name of the control itself;
		\param name		        Name of the control;
		\param min		        Instance of T representing the minimum value (inclusive);
		\param max		        Instance of T representing the maximum value (inclusive);
		\param callbackToGame   Callback that takes a T and returns void. Used whenever the value is set;
        \param callbackFromGame Callback that takes void and returns T.   Used whenever the value is required. Can be ignored (default constructed, doing
                                nothing, meaning the tree won't ask the game for the value);;
	*/
	template < class T >
	void AddRangeControl( const char* path, const char* name, T min, T max, const Callback::Callback1< void, T >& callbackToGame, const Callback::Callback0< T > callbackFromGame, float stepSize = 1.0f )
	{
		#if defined REVEAL
			if ( IsRevealGloballyEnabled() )
			{
				Impl::Widget* control = new Impl::RangeControl< T >( name, *this, min, max, callbackToGame, callbackFromGame, stepSize );		
				mRoot.AddControl( path, control );
			}
		#endif // defined REVEAL
	}


	//! This is somewhat of a work-around to help template-parameter deduction for functions pointers,
	//! Basically, it makes life easier, and the implementation simply forwards to the generic-callback version;
    //! In an ideal world (read: EDG based compilers) this wouldn't be neccessary but for now we have to deal with it;
	template < class T >
	void AddRangeControl( const char* path, const char* name, T min, T max, void (*callbackToGame)( T ), T (*callbackFromGame)(), float stepSize = 1.0f )
	{
		if ( callbackFromGame != NULL )
			AddRangeControl( path, name, min, max, Callback::Callback1< void, T >( callbackToGame ), Callback::Callback0< T >( callbackFromGame ), stepSize );
		else
			AddRangeControl( path, name, min, max, Callback::Callback1< void, T >( callbackToGame ), Callback::Callback0< T >(), stepSize );
	}

    //! Another work-around
	template < class T >
	void AddRangeControl( const char* path, const char* name, T min, T max, void (*callbackToGame)( T ), const Callback::Callback0< T > callbackFromGame, float stepSize = 1.0f )
	{
		AddRangeControl( path, name, min, max, Callback::Callback1< void, T >( callbackToGame ), callbackFromGame, stepSize );
	}

    //! Another work-around
	template < class T >
	void AddRangeControl( const char* path, const char* name, T min, T max, const Callback::Callback1< void, T >& callbackToGame, T (*callbackFromGame)(), float stepSize = 1.0f )
	{
		if ( callbackFromGame != NULL )
			AddRangeControl( path, name, min, max, callbackToGame, Callback::Callback0< T >( callbackFromGame ), stepSize );
		else
			AddRangeControl( path, name, min, max, callbackToGame, Callback::Callback0< T >(), stepSize );
	}
	
    /*!
		\brief Adds an option control widget to the tweaker system

		Provides something like a list-box;;
		
		\param OptionType       Type of the options (can be anything, from enums, to floats, to fully fledged instances of complex data types or reference there to;
		\param path			    Full path from the tweaker though all its submenus, up to the control itself;
		\param name			    Name of the control;
		\param callbackToGame   Callback that takes an OptionType instance and returns void. Used whenever the value is set;
        \param callbackFromGame Callback that takes void and returns an OptionType instance.   Used whenever the value is required. Can
                                be ignored (default constructed, doing nothing, meaning the Tree won't ask the game for the value);
		\param begin		    Pointer to the start of a range of EnumItem instances. Can be ignored (default constructed) if you want an empty listbox (meaning you will add stuff later);
		\param end			    Past-the-end pointer to the range of EnumItem instances. Must be ignored if \begin is ignored as well;

		\return A pointer to the optioncontrol so you can add and remove options later (this has to be a pointer (instead of a reference) because
				the release implementation needs to be able to return something (NULL in this case);
			
		\warning	You do NOT own the pointer that is returned. Once the tweaker is destructed, the pointer becomes invalid too. Thus, do not use the
					pointer after you destructed the tweaker;

		\note The \a Reveal::Option type is a templatized structure containing the option value, and its string representation;
	*/
	template < class OptionType >
    Impl::OptionControl< OptionType >* AddOptionControl( const char* path, const char* name,
                                                         const Callback::Callback1< void, OptionType >& callbackToGame,
                                                         const Callback::Callback0< OptionType >& callbackFromGame,
														 const Option< OptionType >* begin = 0, const Option< OptionType >* end = 0 )
	{
		#if defined REVEAL
			if ( IsRevealGloballyEnabled() )
			{
				Impl::OptionControl< OptionType >* control = new Impl::OptionControl< OptionType >( name, *this, callbackToGame, callbackFromGame, begin, end );
				mRoot.AddControl( path, control );
				return control;
			}
			else
				return NULL;
		#else // defined REVEAL
				return NULL;
		#endif
	}

	//! This is a work-around to help template-parameter deduction for functions pointers,
    //! In an ideal world (read: EDG based compilers) this wouldn't be neccessary;
	//! For our purposes however, it makes life easier. Its implementation simply forwards to the generic-callback version;
	template < class OptionType >
	Impl::OptionControl< OptionType >* AddOptionControl( const char* path, const char* name, void (*callbackToGame)( OptionType ), OptionType (*callbackFromGame)() = 0,
														 const Option< OptionType >* begin = 0, const Option< OptionType >* end = 0 )
	{
		if ( callbackFromGame )
			return AddOptionControl( path, name, Callback::Callback1< void, OptionType >( callbackToGame ), Callback::Callback0< OptionType >( callbackFromGame ), begin, end );
		else
			return AddOptionControl( path, name, Callback::Callback1< void, OptionType >( callbackToGame ), Callback::Callback0< OptionType >(), begin, end );
	}
    //! Another work around;
    template < class OptionType >
	Impl::OptionControl< OptionType >* AddOptionControl( const char* path, const char* name, void (*callbackToGame)( OptionType ), const Callback::Callback0< OptionType >& callbackFromGame,
														 const Option< OptionType >* begin = 0, const Option< OptionType >* end = 0 )
	{
		return AddOptionControl( path, name, Callback::Callback1< void, OptionType >( callbackToGame ), callbackFromGame, begin, end );
	}
    //! Another work around;
    template < class OptionType >
	Impl::OptionControl< OptionType >* AddOptionControl( const char* path, const char* name, const Callback::Callback1< void, OptionType >& callbackToGame, OptionType (*callbackFromGame)(),
														 const Option< OptionType >* begin = 0, const Option< OptionType >* end = 0 )
	{
		return AddOptionControl( path, name, callbackToGame, Callback::Callback0< OptionType >( callbackFromGame ), begin, end );
	}


	/*!
		\brief Adds a checkbox control widget to the tweaker system;

		Provides a simple boolean checkbox control widget;
		
		\param path			    Full path from the tweaker though all its submenus, up to the control itself;
		\param name			    Name of the control;
		\param callbackToGame   Callback that takes a boolean and returns void. Used whenever the value is set;
        \param callbackFromGame Callback that takes void and returns a boolean.   Used whenever the value is required. Can
                                be ignored (default constructed, doing nothing, meaning the Tree won't ask the game for the value);
	*/
	void AddCheckBoxControl( 
		const char* path,
		const char* name,
		const Callback::Callback1< void, bool >& callbackToGame, 
		const Callback::Callback0< bool >& callbackFromGame = Callback::Callback0< bool >() 
		);

	/*!
		\brief Adds a checkbox control widget to the tweaker system with an identifying index;

		Provides a simple boolean checkbox control widget;
		
		\param path			    Full path from the tweaker though all its submenus, up to the control itself;
		\param name			    Name of the control;
		\param index			Identifying index of the control;
		\param callbackToGame   Callback that takes an uint32_t and a bool and returns void. Used whenever the value is set;
        \param callbackFromGame Callback that takes an uint32_t and returns a boolean.   Used whenever the value is required. 
	*/
	void AddIndexedCheckBoxControl(
		const char* path,
		const char* name,
		uint32_t index,
		const Callback::Callback2< void, uint32_t, bool >& callbackToGame, 
		const Callback::Callback1< bool, uint32_t >& callbackFromGame
		);

	/*!
		\brief Adds a button control widget to the tweaker system;

		Provides a simple button click to trigger stuff;
		
		\param path			Full path from the tweaker though all its submenus, up to the control itself;
		\param name			Name of the control;
		\param callback		Callback that takes no parameters (void), and returns void. It is called whenever the button is clicked;
	*/
	void AddButtonControl( const char* path, const char* name, const Callback::Callback0< void >& callback );

	/*!
		\brief Adds a color control widget to the tweaker system;

		Provides a way of controlling colors;
		
		\param path			Full path from the tweaker though all its submenus, up to the control itself;
		\param name			Name of the control;
		\param callbackToGame   Callback that takes a const reference to a Reveal::Color and returns void. Used whenever the value is set;
        \param callbackFromGame Callback that takes void and returns a Reveal::Color instance.   Used whenever the value is required. Can
                                be ignored (default constructed, doing nothing, meaning the Tree won't ask the game for the value);
	*/
	void AddColorControl( const char* path, const char* name, const Callback::Callback1< void, const Color& >& callbackFromGame, const Callback::Callback0< Color >& callbackToGame = Callback::Callback0< Color >() );

	/*!
		\brief Sets the help of the item/menu to the STATIC STRING passed in (i.e. NO COPY IS MADE);

		\param help		Help text to associate w/ the item (\n allowed)
	*/
	void AddHelp ( const char* path, const char* help );

	// ------------------------------------------------------------------------------
	// Public member functions only to be used internally;
	// ------------------------------------------------------------------------------

	enum HelpMode
	{
		Off,
		NextToWidget,
		BottomOfScreen
	};
	void SetHelpMode (HelpMode m);

public:

	/*!
		\brief Sets the currently opened most-recently-menu;
		\internal Only used internally (within the Reveal namespace) by other clases;		
		\param mruMenu Most-recently-menu to open, can be NULL if you want them all closed.

		This function closes the currently opened MRU menu, and opens the given menu;
	*/
	void SetOpenedMru( Impl::MruMenuForward* mruMenu );	

	/*!
		\brief Returns true if the given MRU is the opened mru;
	*/
	bool IsOpenedMru(  Impl::MruMenuForward* mruMenu ) const { return mOpenedMru == mruMenu; }

	//! Opens the next MRU;
	void NextMruMenu();

	//! Opens the previous MRU;
	void PrevMruMenu();
	
	/*!
		\brief Tells the tweaker what the most recently used menu is;
		\internal Only used internally (within the Reveal namespace) by other clases;		
		\note The menu may or may not show up in the list of most recently used menus, depending
			  on whether there is room for it (if all mru menus are locked, there won't be a place to put it);
	*/
	void SetMruMenu( Impl::Menu& menu );

	//! Removes a menu that might be an MRU from the MRUs;
	void RemoveFromMrus( Impl::Menu& menu );

	//! Returns the gui colors;
	const GuiColors& GetGuiColors() const { return mGuiColors; }

	//! Sets a new set of gui colors;
	void SetGuiColors( const GuiColors& c ) { mGuiColors = c; }

	//! Returns the current set of gui colors;
	const GuiColors& GetGuiColors() { return mGuiColors; }

	//! Returns the widget that has the focus;
	virtual const Impl::Widget* GetWidgetWithFocus() const { return mWidgetWithFocus; }

	//! Sets the widget that has the focus;
	virtual void SetWidgetWithFocus( Impl::Widget* w );

	//! Sets the Confirm Menu Expansion flag
	void SetConfirmMenuExpansion(bool flag) { mConfirmMenuExpansion = flag; }

	//! Returns the Confirm Menu Expansion flag
	const bool GetConfirmMenuExpansion() 		{ return mConfirmMenuExpansion; }


	//! Updates the tree with game values.
	void Update();

protected:

	//! Set the MRU state to visible;
	void ShowMRU();

	//! Set the Root state to visible;
	void ShowRoot( int x, int y );

	//! Renders the MRU menus;
	void RenderMrus( const Renderer& renderer ) const;

	//! Renders the Root;
	void RenderRoot( const Renderer& renderer ) const;

	//! Let's the MRU's handle a possible input;
	//! \retrun True if an MRU or child handled the input;
	bool LetMrusHandle( const InputState& inputState );

	//! Returns the root of the tree;
	Impl::Menu& Root() { return mRoot; }

	//! Const version;
	const Impl::Menu& Root() const { return mRoot; }

private:

	// Copy constructor and assignment operator disabled;
	Tree( const Tree& );
	Tree& operator = ( const Tree& );

	// ------------------------------------------------------------------------
	// Attributes
	// ------------------------------------------------------------------------

	enum { HelpWait = 10 };

	Impl::MruMenuForward* mOpenedMru;								//!< Currently opened most-recently-used menu (can be NULL);
	Impl::MruMenuForward  mMruMenus[ Impl::GuiMetrics::NUM_MRUS ];	//!< List of most recently used menus;

	Impl::Menu			 mRoot;			//!< Root of the menu system;
	
	GuiColors			 mGuiColors;

	Impl::Widget*		 mWidgetWithFocus;
	Impl::Widget*		 mLastWidgetWithFocus;

	unsigned int mHelpTimer;

	void RenderHelpWindow( const Renderer& renderer ) const;

	bool	mConfirmMenuExpansion;
	HelpMode mHelpMode;
};

	} // Namespace Impl;
} // Namespace Reveal;

#endif

