// ------------------------------------------------------------------------
/*
	\author: Jaap Suter
   
	Copyright (C) Electronic Arts Canada Inc. 2002.  All rights reserved.	
*/
// ------------------------------------------------------------------------

/*! \file */

#ifndef REVEAL_MENU_H
#define REVEAL_MENU_H

#include "reveal/impl/widget.h"
#include "reveal/impl/gui_metrics.h"
#include "reveal/smart_ptr/scoped_ptr.h"

namespace Reveal {
    
    class InputState;
	class Renderer;
	
	namespace Impl {

		class Tree;

// -------------------------------------------------------------------------------
/*!
	\brief A menu widget is able to hold a bunch of controls, and a 
		   bunch of child-menus. This way you can create a tree of menus 
		   and controls;
*/
// -------------------------------------------------------------------------------
class Menu : public Widget
{	

public:

	/*!
		\brief Constructor
		\param name		Name of the menu;
		\param parent	Parent menu, can be NULL;
		\param tree  Tree this menu belongs to;
	*/
    Menu( const char* name, Tree& tree, Menu* parent );

	//! Destructor, removes the menu from a possible MRU that it might be in;
	~Menu();

	/*!
		\brief Adds a child control to this menu (with possibly some menus in between);
		\param path Sub-menu-path from here to the control, with sub-menus seperated by either
					a forward or backward slash. You can have extra slashes at the start or end, but
					these will be ignored (e.g. "/lighting\\camera/" means the same as "lighting/camera");
		\param control Pointer to the control you want to add, this function takes ownership and becomes 
					   responsible for deleting it.

		\note Suppose the path is as follows: "lighting/camera", then this
			  menu will have a child menu called "lighting", which will have a child
			  menu called "camera", which will contain the control.			  

		\note This transfer of ownership would be better captured with an std::auto_ptr. This is what it used to be even,
			  but NBA's STL setup was kinda sketchy, meaning that their auto_ptr didn't live in the std:: namespace, etc. 
			  etc. given us all sorts of grief. In the end I decided to go with a raw ptr instead.
	*/
    void AddControl( const char* path, Widget* control );

	//! 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 Makes this menu one of the most recently used menus in the tree system
	*/
	void MakeMostRecentlyUsed();

	//! Returns the GUI space width of the menu widget;
	virtual int  Width() const { return Impl::GuiMetrics::GetWidgetWidth(); }

	//! Returns the GUI space height of the menu widget;
	virtual int  Height() const { return Impl::GuiMetrics::GetRowHeight(); }

	//! Opens up the menu, so its child-menus and controls are visible;
	void Open();

	//! Closes the menu, so its child-menus and -controls are no longer drawn;
	//! Calls close recursively for its children;
	void Close();

	//! Closes an opened child;
	void CloseOpenedChild() { if ( mOpenedChild ) mOpenedChild->Close(); }

	//! Returns the opened child, can be NULL;
	const Widget* OpenedChild() const { return mOpenedChild; }

	//! Returns the widget pointed to by path, can be NULL if not found;
	Widget* GetWidget ( const char* path );

	//! Returns true if the menu is a root menu;
	bool IsRoot() const { return ! Parent(); }

	virtual void	Update( );

	/*!
		\todo
	*/
	Widget* GiveFocusToNextWidget( Widget* currentWidgetWithFocus );
	Widget* GiveFocusToPrevWidget( Widget* currentWidgetWithFocus );
	Widget* GiveFocusToFirstChildWidget();
    	
private:

	//! Renders the menu and all its children (if its visible);
	virtual void DoRender( const Renderer& renderer, const GuiColors& guiColors ) const;

	/*!
		\brief Handles the menu input;
		\return NULL if it didn't handle the input, or a pointer to the widget that _did_ handle the input ottherwise;

		Gives the menu and its child-menus and -controls a chance to handle the input. However
		they don't have to, so this function may return false. In that case you can continue to look
		for another menu that may handle the input.
	*/
	virtual Widget* DoHandle( const InputState& inputState );

	//! Implementation of pure base virtual;
	virtual size_t DoToString( const char* path, char* buffer, size_t size ) const;

	//! Implementation of pure base virtual;
	virtual void DoFromString( const char* data );
	
	/*!
		\brief Sets the currently opened child-menu.
		\param Child to open, can be NULL if you want no child to be open.
		
		Of all the child-menus of this menu, only one can be opened. This is controlled using
		this function.
	*/
	void  SetOpenedChild( Menu* child );

	//! Implementation of pure base virtual;
	virtual void DoGiveFocus() { Open(); }

	//! Returns the GUI space height of all children together.
	int TotalOpenedHeight();

	//! Returns whether the child widgets are to the left or to the right;
	//! \return True if the children are to the left;
	bool ChildrenToLeft() const;

	//! Returns whether or not the menu is currently being dealth with from an MRU;
	bool IsInMru() const { return Y() < GuiMetrics::BAR_HEIGHT; }

	//! Returns true if the widget is a direct or indirect child of this menu;
	bool IsChildOf( const Widget* w ) const;
		
	//! Class constants;
    enum
	{
		MAX_NUM_CHILD_MENUS		= 24,		//! Maximum number of child-menus a menu can contain; 
		MAX_NUM_CONTROLS		= 24 		//! Maximum number of controls a menu can contain;
	};

	SmartPtr::ScopedPtr< Widget > mControls[ MAX_NUM_CONTROLS ];		//! Array containing the child controls (this menu owns them);
	SmartPtr::ScopedPtr< Menu   > mChildMenus[ MAX_NUM_CHILD_MENUS ];	//! Array containing the child menus (this menu owns them);
	
	size_t       mNumChildMenus;	//!< Number of non-NULL child menus in the child menu array;
	size_t		 mNumControls;		//!< Number of non-NULL controls in the control array;
	Menu*		 mOpenedChild;		//!< Ptr to one of the menus in the \a mChildMenus array, pointing to the menu 
									//!< that is currently open (can be NULL if none are open);

	Menu( const Menu& );				//!< Not implemented
    Menu& operator = ( const Menu& );	//!< Not implemented

};


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

#endif
