#pragma once
#include "BaseMenu.h"
#include "IRender.h"

/*
	A set of ready-made menu entries for you to use!
*/

  // A simple menu entry that can display a string of text.
  // Not very usable by itself, because it can't even process Enter key!
class TextMenuEntry : public MenuEntry
{
protected:
	std::string m_label;
	Color m_color;
public:
	TextMenuEntry( BaseMenu *menu, int order, const std::string & label, const Color & color )
		: MenuEntry( menu, order )
		, m_label( label )
		, m_color( color )
	{}

	virtual void Render( IRender *pRender, int x, int y );
	virtual void Update( float dt ) {}
};

  // A more usable menu entry, which calls a callback when
  // Enter key is pressed on it.
class SimpleMenuEntry : public TextMenuEntry
{
public:
	class ICallback
	{
	public:
		virtual void operator()( int id ) = 0;
	};

private:
	  // Callback to call
	ICallback *m_pCallback;
	  // ID which will be passed to callback
	int m_callbackID;

public:
	SimpleMenuEntry( BaseMenu *menu, int order , const std::string & label, const Color & color, int callbackID, ICallback *pCallback )
		: TextMenuEntry( menu, order, label, color )
		, m_pCallback( pCallback )
		, m_callbackID( callbackID )
	{}

	virtual void ProcessInput( IInput *pInput );
};

  // This entry can display a label and a changeable value. 
  // You can scroll through the list of value susing Left/Right keys.
  // It's a base class for such entries. Use ValueMenuEntry<T>.
class IValueMenuEntry : public TextMenuEntry
{
	virtual void NextValue() = 0;
	virtual void PrevValue() = 0;
	virtual const char *CurrentValue() = 0;

public:
	IValueMenuEntry( BaseMenu *menu, int order, const std::string & label, const Color & color )
		: TextMenuEntry( menu, order, label, color )
	{}

	virtual void Render( IRender *pRender, int x, int y );
	virtual void ProcessInput( IInput *pInput );	
};

  // This entry can display a label and a changeable value. 
  // You can scroll through the list of value susing Left/Right keys.
  // With each displayed value a hidden value of type TData is
  // associated.
template<typename TData>
class ValueMenuEntry : public IValueMenuEntry
{
	void NextValue()
	{
		if ( !m_values.empty() )
		{
			++m_selectedValue;
			if ( m_selectedValue > (int)m_values.size() - 1 )
				m_selectedValue = (int)m_values.size() - 1;
		}	
	}
	void PrevValue()
	{
		if ( !m_values.empty() )
		{
			--m_selectedValue;
			if ( m_selectedValue < 0 )
				m_selectedValue = 0;
		}	
	}
	const char *CurrentValue()
	{
		if ( m_selectedValue < 0 || m_selectedValue >= (int)m_values.size() )
			return 0;

		return m_values[ m_selectedValue ].m_value.c_str();
	}

protected:
	struct SData
	{
		SData( const std::string & value, const TData & data )
			: m_value( value )
			, m_data( data )
		{}

		std::string m_value;
		TData m_data;
	};
	
	std::vector< SData > m_values;
	int m_selectedValue;

public:
	ValueMenuEntry( BaseMenu *menu, int order, const std::string & label, const Color & color )
		: IValueMenuEntry( menu, order, label, color )
		, m_selectedValue( -1 )
	{}

	int AddValue( const std::string & value, const TData & data ) { m_values.push_back( SData( value, data ) ); return (int)m_values.size() - 1; }
	void SetSelectedIndex( int index ) { if ( index < 0 || index >= (int)m_values.size() ) return; m_selectedValue = index; }
	int GetSelectedIndex() const { return m_selectedValue; }
	int GetValuesCount() const { return (int)m_values.size(); }
	const TData *GetSelectedData() const
	{
		if ( m_selectedValue < 0 || m_selectedValue >= (int)m_values.size() )
			return 0;

		return &m_values[ m_selectedValue ].m_data;
	}
	void SetSelectedIndexByData( const TData & data )
	{
		for ( size_t i = 0; i < m_values.size(); ++i )
		{
			if ( m_values[ i ].m_data == data )
			{
				SetSelectedIndex( (int)i );
				break;
			}
		}
	}
};

  // This menu entry supports text input
  // Press Enter to start entering text.
  // Press Enter again to finish or Escape to cancel
  // It will call a callback when accepting or canceling text input
class TextInputMenuEntry : public TextMenuEntry
{
public:
	class ICallback
	{
	public:
		virtual void AcceptEdit( const std::string & value ) = 0;
		virtual void CancelEdit() = 0;
	};	

protected:
	  // Current displayed value
	std::string m_value;
	  // Maximum value length
	int m_maxLength;
	  // Old value for cancelling
	std::string m_oldValue;
	  // If true, user is typing text right now
	bool m_editing;
	  // If true, user can cancel input
	bool m_canCancel;
	  // Callback to call when text is entered or cancelled
	ICallback *m_pCallback;
public:

	TextInputMenuEntry( BaseMenu *menu, int order, const std::string & label, const std::string & value, int maxLength, const Color & color, ICallback *pCallback )
		: TextMenuEntry( menu, order, label, color )
		, m_value( value )
		, m_maxLength( maxLength )
		, m_editing( false )
		, m_canCancel( true )
		, m_pCallback( pCallback )
	{
	}

	virtual void Render( IRender *pRender, int x, int y );
	virtual void ProcessInput( IInput *pInput );
	void StartEdit() { m_editing = true; }
	void SetCanCancel( bool value ) { m_canCancel = value; }
	void SetValue( const std::string & value ) { m_value = value; }
};

  // A specialization of ValueMenuEntry for a simple case
  // when you only want to display On/Off choice for player.
class OnOffMenuEntry : public ValueMenuEntry<bool>
{
public:
	OnOffMenuEntry( BaseMenu *menu, int order, const std::string & label, bool on, const Color & color )
		: ValueMenuEntry<bool>( menu, order, label, color )
	{
		AddValue( "Off", false );
		AddValue( "On", true );
		SetSelectedIndex( on ? 1 : 0 );
	}
};
