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

/*! \file */

#ifndef REVEAL_OPTION_CONTROL_H
#define REVEAL_OPTION_CONTROL_H

#include "reveal/impl/widget.h"
#include "reveal/callback/callback_0.h"
#include "reveal/callback/callback_1.h"

#include "reveal/impl/gui_metrics.h"
#include "reveal/impl/util.h"

#include "reveal/renderer.h"
#include "reveal/input_state.h"

#include "reveal/stdint.h"

#include "reveal/smart_ptr/scoped_array.h"

#include "reveal/custom_memory.h"

#include <string.h>

#ifdef REVEAL_EXCEPTIONS
	#include <memory>
#endif

namespace Reveal {

// -------------------------------------------------------------------------------
/*!
	\brief Utility structure used in the public interface of the \a Impl::OptionControl class
		   and consequently by the tree as well;
			
		   Holds an option-value and its description;
*/
// -------------------------------------------------------------------------------
template < class T >
struct Option
{ 
	T			mOption;		//!< Actual instance of the option you're referring to;
	const char* mDescription;	//!< Description of the option;
};
// -------------------------------------------------------------------------------

namespace Impl {

// -------------------------------------------------------------------------------
/*!
    \brief Option control
*/
// -------------------------------------------------------------------------------
template < class OptionType >
class OptionControl : public Widget
{
    typedef Callback::Callback1< void, OptionType > CallbackToGame;
    typedef Callback::Callback0< OptionType >       CallbackFromGame;

	//! Warning: this class must remain a POD
	struct OptionImpl
	{ 
		enum {  MAX_OPTION_DESC_LENGTH = 32 };

		OptionType mOption; 
		char mDescription[ MAX_OPTION_DESC_LENGTH ]; 

		//! Custom new, using the allocator;
		void* operator new( size_t size )
		{
			for (;;)
			{
				void* p = Memory::RevealMalloc( size );			
				if ( p ) 
				{
					return p;
				}
				else
				{
					#ifdef REVEAL_EXCEPTIONS
						std::new_handler globalHandler = std::set_new_handler( NULL );
						std::set_new_handler( globalHandler );
						if (globalHandler) (*globalHandler)();
						else throw std::bad_alloc();
					#else
						assert( !"Out of memory, no exception handler enabled, fatal error." );
					#endif
				}
			}
		}

		//! Custom delete, using the allocator;
		void operator delete( void* ptr ) { if ( ! ptr ) return; Memory::RevealFree( ptr ); }

		//! Custom array new, using the allocator;
		void* operator new [] ( size_t size ) 
		{ 
			return operator new( size ); 
		}
		
		//! Custom array delete, using the allocator;
		void operator delete [] ( void* ptr ) { operator delete( ptr ); }
	};

public:

    // Constructor, does nothing unexpected;
    OptionControl( const char* name, 
				   Tree& tree, 
                   const Callback::Callback1< void, OptionType >& callbackToGame, 
                   const Callback::Callback0< OptionType >& callbackFromGame, 
                   const Option< OptionType >* begin, 
                   const Option< OptionType >* end );

	//! Returns the width of the control;
	virtual int  Width()  const;

	//! Returns the height of the control;
	virtual int  Height() const;

	//! Adds a range of options to the option control;
	void AddOptions(	  const Option< OptionType >* begin, const Option< OptionType >* end );

	//! Removes a range of options from the option control;
	void RemoveOptions( const Option< OptionType >* begin, const Option< OptionType >* end );

	//! Removes an option, with the given index, from the option control;
	void RemoveOption( size_t index, const Reveal::Callback::Callback1< void, OptionType& >& updateRest );

	//! Sets the number of options displayed at once;
	//! \param n Must be bigger than 2;
	void SetNumOptionsVisible( size_t n )
	{
		assert( n > 2 );
		if ( n <= mNumOptions ) mNumOptionsDisplayed = n;
	}

private:

	// Copy ctor and assigment op are disabled;
	OptionControl( const OptionControl& );
	OptionControl& operator = ( const OptionControl& );

	//! Class constants;
	enum
	{ 
		DEFAULT_NUM_VISIBLE_AT_ONCE	 = 8
	};

	//! Implementation of pure base virtual;
	void DoRender( const Renderer& renderer, const GuiColors& guiColors ) const;

	//! Implementation of pure base virtual;
    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 base virtual;
	virtual void DoLoseFocus() { mHighlightedIndex = mSelectedIndex; mState = VIEWING; }

    //! Updates the current selected option, asking the game for its value. Only does it if the callback from the game is valid.
    //! Does nothing otherwise.
    void GetCurrentOptionFromGame();

	//! Given the index of an option in focus, returns the index of the first displayed option (at the top of the list).
	size_t IndexOfFirstDisplayed( size_t focusIndex ) const;

	//! Returns -1 if top button is being pressed, and +1 if bottom button is being pressed, and 0 if neither.
	int SliderButton( int mouseX, int mouseY ) const;

	//! If slider visible and clicked, returns the index of the selected by the slider.
	int IndexOfSlider( int mouseX, int mouseY ) const;

	//! If VIEWING, returns the index of the item that the mouse cursor is hovering over, if any, or -1 otherwise.
	int IndexOfMouseCursor( int mouseX, int mouseY ) const;

    // Gui metrics;
	int BottomArrowY() const;
	int TopArrowY()	   const;
	int ArrowsX()	   const;
	int SliderY()	   const { return TopArrowY() + Impl::GuiMetrics::GetArrowBoxSize();				   }
	int SliderHeight() const { return BottomArrowY() - TopArrowY() - Impl::GuiMetrics::GetArrowBoxSize(); }

    //! Returns the number of options displayed at once;
	size_t	NumOptionsDisplayed() const { return mNumOptionsDisplayed == 0 ? (mNumOptions < DEFAULT_NUM_VISIBLE_AT_ONCE ? mNumOptions : (size_t)DEFAULT_NUM_VISIBLE_AT_ONCE) : mNumOptionsDisplayed; }

	bool SliderBarVisible() const { return mNumOptions > NumOptionsDisplayed() && mState == VIEWING; }

    // Attributes;

	CallbackToGame	 mCallbackToGame;
    CallbackFromGame mCallbackFromGame;

	size_t	    mNumOptionsDisplayed;   //! Number of options that appear on-screen. If more exist a slider bar will be added.
	size_t		mSelectedIndex;			//! Index of option currently selected. Matches value in user code.
	size_t		mHighlightedIndex;		//! Index of option that is the selection candidate. Pressing CONFIRM will select this option.
	size_t      mFirstDisplayedIndex;	//! Index of option that is displayed at the top of the list. 
	size_t		mNumOptions;
	size_t		mMaxNumOptions;
	SmartPtr::ScopedArray< OptionImpl > mOptions;

	enum { VIEWING, CHOOSING } mState;
	
};

// -------------------------------------------------------------------------------
template < class OptionType >
OptionControl< OptionType >::OptionControl( const char* name, Tree& tree, 
							  			    const typename OptionControl< OptionType >::CallbackToGame& callbackToGame, 
                                            const typename OptionControl< OptionType >::CallbackFromGame& callbackFromGame, 
										    const Option< OptionType >* begin, const Option< OptionType >* end )
: 
	Widget( name, tree ), 
	mCallbackToGame( callbackToGame ), 
    mCallbackFromGame( callbackFromGame ),
	mNumOptionsDisplayed( static_cast< size_t >( end - begin ) < DEFAULT_NUM_VISIBLE_AT_ONCE ? static_cast< size_t >( end - begin ) : (size_t)DEFAULT_NUM_VISIBLE_AT_ONCE ),
	mSelectedIndex( 0 ),
	mHighlightedIndex( 0 ),
	mFirstDisplayedIndex( 0 ),
	mNumOptions( static_cast< size_t >( end - begin ) ),
	mMaxNumOptions( static_cast< size_t >( end - begin ) ),
	mOptions( mNumOptions ? new OptionImpl[ mNumOptions ] : 0 ),
	mState( VIEWING )
{
	for ( size_t i = 0; begin != end; ++i, ++begin )
    {
		mOptions[ i ].mOption = begin->mOption;
		strncpy( mOptions[ i ].mDescription, begin->mDescription, OptionImpl::MAX_OPTION_DESC_LENGTH - 1 );
		mOptions[ i ].mDescription[ OptionImpl::MAX_OPTION_DESC_LENGTH - 1 ] = '\0';
	}

    GetCurrentOptionFromGame();

	SetHelp( "Confirm: Selection mode\n"
				"Left mouse button: Make selection\n"
				"WHEN IN SELECTION MODE:\n"
				"Confirm: Make selection\n"
				"Back button: Exit selection mode\n");
}

// -------------------------------------------------------------------------------
template < class OptionType >
void OptionControl< OptionType >::GetCurrentOptionFromGame()
{
	if ( mNumOptions == 0 ) return;
    if (  mCallbackFromGame.Valid() )
    {
        OptionType o = mCallbackFromGame();
		if ( mOptions[ mSelectedIndex ].mOption == o ) 
			return;
		else        
			for ( size_t i = 0; i < mNumOptions; ++i )
			{
				if ( mOptions[ i ].mOption == o )
				{
					mSelectedIndex = i; break;
				}
			}
    }
}

// -------------------------------------------------------------------------------
template < class OptionType >
void OptionControl< OptionType >::DoRender( const Renderer& renderer, const GuiColors& guiColors ) const
{
	Reveal::uint32_t titleColor;
	Reveal::uint32_t selectedColor;
	
	if( HasFocus() ) {
		if( mState == VIEWING ) {
			titleColor         = guiColors.mWidgetHighlightedBackgroundColor;
			selectedColor      = guiColors.mOptionBoxSelectionColor;
		} else {
			titleColor         = guiColors.mWidgetBackgroundColor;
			selectedColor      = guiColors.mWidgetHighlightedBackgroundColor;
		}
	} else {
		titleColor         = guiColors.mWidgetBackgroundColor;
		selectedColor      = guiColors.mOptionBoxSelectionColor;
	}
   
	// Draw title bar.
	renderer.Bar( X(), Y(), Width(), GuiMetrics::GetRowHeight(), titleColor );
	renderer.Bar( X(), Y() + GuiMetrics::GetRowHeight(), Width(), Height() - GuiMetrics::GetRowHeight(), guiColors.mWidgetBackgroundColor );
	renderer.Line( X(), Y() + GuiMetrics::GetRowHeight(), X() + Width(), Y() + GuiMetrics::GetRowHeight(), guiColors.mBorderColor );
	renderer.String( X() + GuiMetrics::TEXT_BORDER_WIDTH, Y() + GuiMetrics::TEXT_BORDER_HEIGHT, Name(), guiColors.mTextColor );

	// Draw selection items.
	for( size_t i = 0; i < NumOptionsDisplayed(); ++i )
	{
		Reveal::uint32_t const * bgColor = NULL;
		if( i + mFirstDisplayedIndex == mHighlightedIndex ) bgColor = &guiColors.mOptionBoxMouseOverColor;
		if( i + mFirstDisplayedIndex == mSelectedIndex )    bgColor = &selectedColor;
		if( bgColor != NULL ) {
			renderer.Bar( X(), Y() + GuiMetrics::GetRowHeight() + static_cast< int >( i ) * GuiMetrics::GetRowHeight(), Width(), GuiMetrics::GetRowHeight(), *bgColor );
		}
		
		const int indentMultiplier = 4;
		renderer.String( X() + indentMultiplier * GuiMetrics::TEXT_BORDER_WIDTH, 
				         Y() + GuiMetrics::GetRowHeight() + static_cast< int >( i ) * GuiMetrics::GetRowHeight() + GuiMetrics::TEXT_BORDER_HEIGHT, 
						 mOptions[ i + mFirstDisplayedIndex ].mDescription, 
						 guiColors.mTextColor );
	}

  	if( SliderBarVisible() ) {
  		renderer.Bar( ArrowsX(), TopArrowY(), 
			          GuiMetrics::GetItemSize(), GuiMetrics::GetArrowBoxSize(), 
					  guiColors.mInteractiveBackgroundColor );		
  		renderer.Triangle( ArrowsX() + GuiMetrics::BORDER_WIDTH + (GuiMetrics::GetItemSize() - 2 * GuiMetrics::BORDER_WIDTH) / 2,
  						   TopArrowY() + GuiMetrics::BORDER_HEIGHT, 
  						   ArrowsX() + GuiMetrics::GetItemSize() - GuiMetrics::BORDER_WIDTH, 
						   TopArrowY() + GuiMetrics::GetArrowBoxSize() - GuiMetrics::BORDER_HEIGHT, 
  						   ArrowsX() + GuiMetrics::BORDER_WIDTH,						  
						   TopArrowY() + GuiMetrics::GetArrowBoxSize() - GuiMetrics::BORDER_HEIGHT, 
  						   guiColors.mInteractiveForegroundColor );		
  		
  		renderer.Bar( ArrowsX(), BottomArrowY(), 
			          GuiMetrics::GetItemSize(), GuiMetrics::GetArrowBoxSize(), 
					  guiColors.mInteractiveBackgroundColor );
  		renderer.Triangle( ArrowsX() + GuiMetrics::BORDER_WIDTH + (GuiMetrics::GetItemSize() - 2 * GuiMetrics::BORDER_WIDTH) / 2,
  						   BottomArrowY() + GuiMetrics::GetArrowBoxSize() - GuiMetrics::BORDER_HEIGHT, 
  						   ArrowsX() + GuiMetrics::GetItemSize() - GuiMetrics::BORDER_WIDTH, 
						   BottomArrowY() + GuiMetrics::BORDER_HEIGHT, 
  						   ArrowsX() + GuiMetrics::BORDER_WIDTH,
						   BottomArrowY() + GuiMetrics::BORDER_HEIGHT, 
  						   guiColors.mInteractiveForegroundColor );
		renderer.Bar( ArrowsX(), TopArrowY() + GuiMetrics::GetArrowBoxSize(), 
					  GuiMetrics::GetItemSize(), SliderHeight(), 
					  guiColors.mInteractiveBackgroundColor );
	}

	DrawBorder( renderer, guiColors );
}

template < class OptionType >
size_t OptionControl< OptionType >::IndexOfFirstDisplayed( size_t focusIndex ) const
{
	// numOptionsAbove + numOptionsBelow + 1 = mNumOptionsDisplayed
	const size_t numOptionsAbove = NumOptionsDisplayed() / 2;
	const size_t numOptionsBelow = NumOptionsDisplayed() - numOptionsAbove - 1;

	// Not all the options above can fit, so display starting from first option.
	if( focusIndex < numOptionsAbove ) return 0;

	// Not all the options below can fit, so display with the last option at the bottom.
	if( focusIndex + numOptionsBelow >= mNumOptions ) return mNumOptions - NumOptionsDisplayed();

	// All options above and below can fit, so display the focusIndex in the middle i.
	return focusIndex - numOptionsAbove;
}

template < class OptionType >
int OptionControl< OptionType >::SliderButton( int mouseX, int mouseY ) const
{
	if( !SliderBarVisible() ) return 0;

	// Up button pressed.
  	if( Util::PointInRect( mouseX, mouseY, ArrowsX(), TopArrowY(), 
  						   Impl::GuiMetrics::GetItemSize(), Impl::GuiMetrics::GetArrowBoxSize() ) )
  	{
  		return -1; 
  	}

	// Down button pressed.
  	if ( Util::PointInRect( mouseX, mouseY, ArrowsX(), BottomArrowY(), 
  							Impl::GuiMetrics::GetItemSize(), Impl::GuiMetrics::GetArrowBoxSize() ) )
  	{
  		return 1;
  	}
	return 0;
}

template < class OptionType >
int OptionControl< OptionType >::IndexOfSlider( int mouseX, int mouseY ) const
{
	if( !SliderBarVisible() ) return -1;
	bool inSlider = Util::PointInRect( mouseX, mouseY, ArrowsX(), SliderY(), 
		                               Impl::GuiMetrics::GetItemSize(), SliderHeight() );
	if( !inSlider ) return -1;

	int y = mouseY - SliderY();
	if ( y < 0 ) y = 0;
	if ( y > SliderHeight() ) y = SliderHeight();
	return y * static_cast<int>(mNumOptions) / SliderHeight();
}

template < class OptionType >
int OptionControl< OptionType >::IndexOfMouseCursor( int mouseX, int mouseY ) const
{
	// Mouse cursor outside of the list or in slider.
	bool inList = Util::PointInRect( mouseX, mouseY, X(), Y() + GuiMetrics::GetRowHeight(), 
		                             Width(), Height() - GuiMetrics::GetRowHeight() );
	if( !inList || IndexOfSlider( mouseX, mouseY ) != -1 || SliderButton( mouseX, mouseY ) != 0 ) return -1;

	int y = mouseY - Y() - GuiMetrics::GetRowHeight();
	return static_cast<int>(mFirstDisplayedIndex) + y * static_cast< int >( NumOptionsDisplayed() ) / (Height() - GuiMetrics::GetRowHeight());
}

// -------------------------------------------------------------------------------
template < class OptionType >
Widget* OptionControl< OptionType >::DoHandle( const InputState& inputState )
{ 
	Widget* widgetThatHandledTheInput = NULL;

    GetCurrentOptionFromGame();

	if ( HasFocus() )
	{
		widgetThatHandledTheInput = this;

		if( mState == VIEWING ) {
			// If the mouse cursor is travelling over the list, highlight accordingly.
			int mouseIndex = IndexOfMouseCursor( inputState.MouseX(), inputState.MouseY() );
			if( mouseIndex >= 0 ) {
				mHighlightedIndex = mouseIndex;
			}
		}

		if( inputState.Released( InputState::LEFT ) || (inputState.Released( InputState::BACK ) && mState == VIEWING) ) {
			// Go up the menu tree.
			mState = VIEWING;
			widgetThatHandledTheInput = GiveFocusToParent();

		} else if( inputState.Pressed( InputState::BACK ) ) {
			// Exit choosing state.
			assert( mState == CHOOSING );
			mState = VIEWING;

		} else if( inputState.Pressed( InputState::UP ) ) {
			if( mState == VIEWING && Parent() ) {
				// Go to the previous item in the menu.
				widgetThatHandledTheInput = Parent()->GiveFocusToPrevWidget( this );
				// If no previous widget exists, handle as though in choosing state.
				if( widgetThatHandledTheInput == this ) {
					mState = CHOOSING;
				}
			} 
			if( mState == CHOOSING ) {
				// Highlight goes up one.
				if( mHighlightedIndex > 0 ) {
					mHighlightedIndex--;
					mFirstDisplayedIndex = IndexOfFirstDisplayed( mHighlightedIndex );
				}
			}

		} else if( inputState.Pressed( InputState::DOWN ) ) {
			if( mState == VIEWING && Parent() ) {
				// Go to the next item in the menu.
				widgetThatHandledTheInput = Parent()->GiveFocusToNextWidget( this );
				// If no previous widget exists, handle as though in choosing state.
				if( widgetThatHandledTheInput == this ) {
					mState = CHOOSING;
				}
			} 
			if( mState == CHOOSING ) {
				// Highlight goes down one.
				if( mHighlightedIndex < mNumOptions - 1 ) {
					mHighlightedIndex++;
					mFirstDisplayedIndex = IndexOfFirstDisplayed( mHighlightedIndex );
				}
			}

		} else if( inputState.Pressed( InputState::CONFIRM | InputState::LEFT_MOUSE ) ) {
			int sliderButton = SliderButton( inputState.MouseX(), inputState.MouseY() );
			int sliderIndex  = IndexOfSlider( inputState.MouseX(), inputState.MouseY() );

			if( sliderButton != 0 ) {
				// Move first displayed index as appropriate.
				if( mFirstDisplayedIndex > 0 && sliderButton < 0 ) {
					mFirstDisplayedIndex--;
				} else if( mFirstDisplayedIndex < mNumOptions - mNumOptionsDisplayed && sliderButton > 0 ) {
					mFirstDisplayedIndex++;
				}
			} else if( sliderIndex >= 0 ) {
				// Move the highlighted index to the slider location.
				mFirstDisplayedIndex = IndexOfFirstDisplayed( sliderIndex );
			} else if( mHighlightedIndex != mSelectedIndex ) {
				// A new selection has been chosen, so do callback.
				mSelectedIndex = mHighlightedIndex;
				mCallbackToGame( mOptions[ mSelectedIndex ].mOption );
				MakeParentMostRecentlyUsed();
			} else {
				// The mouse pointer has not highlighted a new selection, so enter choosing mode.
				if( IndexOfMouseCursor( inputState.MouseX(), inputState.MouseY() ) == -1 ) {
					mState = CHOOSING;
				}
			}
		}
	}

	return widgetThatHandledTheInput;
}

// -------------------------------------------------------------------------------
template < class OptionType >
int OptionControl< OptionType >::Width() const 
{ 
	return Impl::GuiMetrics::GetWidgetWidth(); 
}

// -------------------------------------------------------------------------------
template < class OptionType >
int OptionControl< OptionType >::Height() const 
{ 
	return static_cast< int >( Impl::GuiMetrics::GetRowHeight() + Impl::GuiMetrics::GetRowHeight() * NumOptionsDisplayed() ); 
}

// -------------------------------------------------------------------------------
template < class OptionType >
int OptionControl< OptionType >::BottomArrowY() const 
{ 
	return Y() + Height() - GuiMetrics::GetArrowBoxSize() - GuiMetrics::BORDER_HEIGHT; 
}

// -------------------------------------------------------------------------------
template < class OptionType >
int OptionControl< OptionType >::TopArrowY() const 
{ 
	return Y() + GuiMetrics::GetRowHeight() + GuiMetrics::BORDER_HEIGHT; 
}

// -------------------------------------------------------------------------------
template < class OptionType >
int OptionControl< OptionType >::ArrowsX() const 
{ 
	return X() + Width() - GuiMetrics::GetItemSize() - GuiMetrics::BORDER_WIDTH;
}

// -------------------------------------------------------------------------------
template < class OptionType >
size_t OptionControl< OptionType >::DoToString( const char* path, char* buffer, size_t size ) const
{
	const size_t pathAndNameLength = PathAndNameToString( path, buffer, size );
	assert( size >= pathAndNameLength );
	size -= pathAndNameLength; 
	buffer += pathAndNameLength;	

	if ( size >= 1 )
	{
		*buffer++ = '\"';
		--size;
	}
	else return pathAndNameLength;
	
	const size_t valueLength = Util::ToString( static_cast< int >( mSelectedIndex ), buffer, size );
	assert( size >= valueLength );
	size -= valueLength;
	buffer += valueLength;
	
	if ( size >= 1 )
	{
		*buffer++ = '\"';
		--size;
	} else return valueLength + pathAndNameLength + 1;

	*buffer = '\n';

	return pathAndNameLength + valueLength + 3;
}

// -------------------------------------------------------------------------------
template < class OptionType >
void OptionControl< OptionType >::AddOptions( const Option< OptionType >* begin, const Option< OptionType >* end )
{
	assert( begin );
	assert( end );
    size_t numOptions = mNumOptions + (end - begin);

	if ( numOptions > mMaxNumOptions )
	{
		mMaxNumOptions = numOptions;
		SmartPtr::ScopedArray< OptionImpl > options( new OptionImpl[ mMaxNumOptions ] );		
		memcpy( options.Get(), mOptions.Get(), sizeof( OptionImpl ) * mNumOptions );
		options.Swap( mOptions );
	}

	for ( size_t i = mNumOptions; begin != end; ++i, ++begin )
    {
		mOptions[ i ].mOption = begin->mOption;
		strncpy( mOptions[ i ].mDescription, begin->mDescription, OptionImpl::MAX_OPTION_DESC_LENGTH - 1 );
		mOptions[ i ].mDescription[ OptionImpl::MAX_OPTION_DESC_LENGTH - 1 ] = '\0';
	}

	mNumOptions = numOptions;
}

// -------------------------------------------------------------------------------
template < class OptionType >
void OptionControl< OptionType >::RemoveOptions( const Option< OptionType >* begin, const Option< OptionType >* end )
{
	for ( const Option< OptionType >* iter = begin; iter != end; ++iter )
	{
		for ( size_t i = 0; i < mNumOptions; ++i )
		{
			if ( begin->mOption == mOptions[ i ].mOption )
			{
	            memmove( &mOptions[ i ], &mOptions[ i + 1 ], sizeof( OptionImpl ) * (mNumOptions - i - 1) );
				--mNumOptions;
			}
		}
	}
}

// -------------------------------------------------------------------------------
template < class OptionType >
void OptionControl< OptionType >::RemoveOption( size_t index, const Reveal::Callback::Callback1< void, OptionType& >& updateRest )
{
	assert( index < mNumOptions );
	if ( mNumOptions )
	{
		--mNumOptions;
		if ( index != mNumOptions ) memmove( &mOptions[ index ], &mOptions[ index + 1 ], sizeof( OptionImpl ) * (mNumOptions - index) );	
	}
	Reveal::Callback::Callback1< void, OptionType& > copyOfCallback( updateRest );
	for ( size_t i = 0; i < mNumOptions; ++i )
	{
		copyOfCallback( mOptions[ i ].mOption );
	}
}

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

#endif
