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

  // A enum for identifying currently active options sub-menu
enum EOptions
{
	OPTIONS_NONE,
	OPTIONS_VIDEO,
	OPTIONS_SOUND,
	OPTIONS_CONTROLS,
	OPTIONS_GAME,
};

class IOptionsMenuCallback
{
public:
	virtual void BackFromOptions() = 0;
	virtual void BackFromSubOptions() = 0;
	virtual void ShowSubOptions( EOptions opt ) = 0;
};

class IKeyEntryCallback
{
public:
	virtual void KeyChanged( int oldKey, int newKey ) = 0;
};

  // Menu entry which allows exactly one key to be entered and displays its name.
  // For redifining controls
class KeyMenuEntry : public TextMenuEntry
{
protected:
	int m_oldKeyCode;
	int m_keyCode;
	bool m_editing;
	std::string m_keyName;

	IKeyEntryCallback *m_pCallback;

public:
	KeyMenuEntry( BaseMenu *menu, int order, const std::string & label, int keyCode, const Color & color, IKeyEntryCallback *pCallback )
		: TextMenuEntry( menu, order, label, color )
		, m_keyCode( keyCode )
		, m_oldKeyCode( keyCode )
		, m_editing( false )
		, m_pCallback( pCallback )
	{
	}

	int GetKeyCode() const { return m_keyCode; }
	void SetKeyCode( int keycode ) { m_keyCode = keycode; }
	virtual void Render( IRender *pRender, int x, int y );
	virtual void ProcessInput( IInput *pInput );
};

  // Description of a display mode
struct SModeDesc
{
	SModeDesc( int _w, int _h, int _b, int _r )
		: w(_w)
		, h(_h)
		, b(_b)
		, r(_r)
	{}

	int w,h,b,r;

	bool operator < ( const SModeDesc & other ) const
	{
		if ( w != other.w )
			return w < other.w;
		if ( h != other.h )
			return h < other.h;

		return b < other.b;
	}
};

  // Menu options class
class MenuOptions : public BaseMenu, public SimpleMenuEntry::ICallback
{
	static const int EID_VID_OPTIONS	= 1;
	static const int EID_SND_OPTIONS	= 2;
	static const int EID_CTRL_OPTIONS	= 3;
	static const int EID_GAME_OPTIONS	= 4;
	static const int EID_BACK			= 5;

	SimpleMenuEntry m_vidOptions;
	SimpleMenuEntry m_sndOptions;
	SimpleMenuEntry m_ctrlOptions;
	SimpleMenuEntry m_gameOptions;
	SimpleMenuEntry m_back;

	IOptionsMenuCallback *m_pCallback;

public:
	MenuOptions( int x, int y, IOptionsMenuCallback *pCallback )
		: BaseMenu( "OptionsMenu", 450, 256 )
		, m_pCallback( pCallback )
		, m_vidOptions		( this, 1, "Video options",		Color(255,255,255), EID_VID_OPTIONS,	this )
		, m_sndOptions		( this, 2, "Sound options",		Color(255,255,255), EID_SND_OPTIONS,	this )
		, m_ctrlOptions		( this, 3, "Control options",	Color(255,255,255), EID_CTRL_OPTIONS,	this )
		, m_gameOptions		( this, 4, "Gameplay options",	Color(255,255,255), EID_GAME_OPTIONS,	this )
		, m_back			( this, 5, "Back",				Color(255,255,255), EID_BACK,			this )
	{
		SetSelected( &m_back );
	}

	void operator()( int id );
	void Back() { this->operator ()( EID_BACK ); }	
};

  // This menu is show when game is paused. It's a simplified version of Options Menu
class PauseMenuOptions : public BaseMenu, public SimpleMenuEntry::ICallback
{
	static const int EID_SND_OPTIONS	= 2;
	static const int EID_CTRL_OPTIONS	= 3;
	static const int EID_BACK			= 5;

	SimpleMenuEntry m_sndOptions;
	SimpleMenuEntry m_ctrlOptions;
	SimpleMenuEntry m_back;

	IOptionsMenuCallback *m_pCallback;

public:
	PauseMenuOptions( int x, int y, IOptionsMenuCallback *pCallback )
		: BaseMenu( "OptionsMenu", 450, 256 )
		, m_pCallback( pCallback )
		, m_sndOptions		( this, 2, "Sound options",		Color(255,255,255), EID_SND_OPTIONS,	this )
		, m_ctrlOptions		( this, 3, "Control options",	Color(255,255,255), EID_CTRL_OPTIONS,	this )
		, m_back			( this, 5, "Back",				Color(255,255,255), EID_BACK,			this )
	{
		SetSelected( &m_back );
	}

	void operator()( int id );
	void Back() { this->operator ()( EID_BACK ); }	
};

  // Base class for options sub-menu
class BaseOptionsMenu : public BaseMenu, public SimpleMenuEntry::ICallback
{
	static const int CID_APPLY = 1;
	static const int CID_CANCEL = 2;

	SimpleMenuEntry m_applyEntry;
	SimpleMenuEntry m_cancelEntry;

	IOptionsMenuCallback *m_pCallback;

	virtual void Apply() = 0;

public:
	BaseOptionsMenu(  const std::string & caption, int x, int y, IOptionsMenuCallback *pCallback );

	void operator()( int id );

	void Back() { this->operator ()( CID_APPLY ); }	
};

class VideoOptionsMenu : public BaseOptionsMenu, public IEnumerateDisplayModesCallback
{
	ValueMenuEntry<ERenderType> m_renderType;
	ValueMenuEntry<bool> m_fullscreenEntry;
	ValueMenuEntry<size_t> m_resolution;
	OnOffMenuEntry m_vsyncEntry;
	ValueMenuEntry<EScalingMode> m_scaling;

	std::vector<SModeDesc> m_modes;

	void EnumerateDisplayMode( int bits, int w, int h, int refresh );	
	void Apply();
	void OnShow();

public:
	VideoOptionsMenu( int x, int y, IOptionsMenuCallback *pCallback );
};

class SoundOptionsMenu : public BaseOptionsMenu
{
	ValueMenuEntry<int> m_masterVolume;
	ValueMenuEntry<int> m_soundVolume;
	ValueMenuEntry<int> m_musicVolume;

	void Apply();
	void OnShow();

public:
	SoundOptionsMenu( int x, int y, IOptionsMenuCallback *pCallback );
};

class ControlOptionsMenu : public BaseOptionsMenu, public IKeyEntryCallback
{
	KeyMenuEntry m_keyClockEntry;
	KeyMenuEntry m_keyCounterEntry;
	KeyMenuEntry m_keyAccelEntry;
	KeyMenuEntry m_keyFireEntry;
	KeyMenuEntry m_keyBombEntry;

	void KeyChanged( int oldKey, int newKey );

	void Apply();
	void OnShow();

public:
	ControlOptionsMenu( int x, int y, IOptionsMenuCallback *pCallback );
};

class GameplayOptionsMenu : public BaseOptionsMenu
{
	ValueMenuEntry<int> m_livesEntry;
	ValueMenuEntry<int> m_dayLengthEntry;
	OnOffMenuEntry m_aimAidEntry;
	OnOffMenuEntry m_turboFireEntry;
	
	void Apply();
	void OnShow();

public:
	GameplayOptionsMenu( int x, int y, IOptionsMenuCallback *pCallback );
};
