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

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

/*! \file */

#include "reveal/impl/color_control.h"
#include "reveal/impl/menu.h"
#include "reveal/impl/tree.h"
#include "reveal/gui_colors.h"

namespace Reveal {
	namespace Impl {

// -------------------------------------------------------------------------------
void ColorControl::DoRender( const Renderer& renderer, const GuiColors& guiColors ) const
{
	Reveal::uint32_t bgColor= guiColors.mWidgetBackgroundColor;
	if ( GetTree().GetWidgetWithFocus() && GetTree().GetWidgetWithFocus()->Parent() == Parent() ) bgColor= guiColors.mWidgetSameLevelBackgroundColor;
	Reveal::uint32_t bgColor2 = bgColor;
	if ( HasFocus() ) bgColor = guiColors.mWidgetHighlightedBackgroundColor;
	
	renderer.Bar( X(), Y(), Width(), GuiMetrics::GetRowHeight(), mState == IDLE ? bgColor : bgColor2 );
	renderer.String( X() + GuiMetrics::TEXT_BORDER_WIDTH, Y() + GuiMetrics::TEXT_BORDER_HEIGHT, Name(), guiColors.mTextColor );
	const char * numberHeader = (mType == HSV) ? "hsv" : "rgb";
	renderer.String( NumberX(), Y() + GuiMetrics::TEXT_BORDER_HEIGHT, numberHeader, guiColors.mTextColor );

	int previewBarOffset = Parent()->X() < X() ? Width() : -(Width() / 2);
	renderer.Bar( X() + previewBarOffset, Y() + Height()/4, Width()/2, Height()/2, mColor.Get32BitARGB() );

	for ( int i = 0; i < 4; ++i )
	{
		renderer.Bar( X(), Y() + (i+1) * GuiMetrics::GetRowHeight(), Width(), GuiMetrics::GetRowHeight(), i + 1 == mState ? bgColor : bgColor2 );

		// Draw the two slider bar arrows on either side of the slider bar;
		{
			renderer.Bar(	   LeftArrowX(), SliderY( i ), GuiMetrics::GetArrowBoxSize(), GuiMetrics::GetItemSize(), guiColors.mInteractiveBackgroundColor );
			renderer.Triangle( LeftArrowX() + GuiMetrics::GetArrowBoxSize() - GuiMetrics::BORDER_WIDTH, SliderY( i ) + GuiMetrics::BORDER_HEIGHT,
							   LeftArrowX() + GuiMetrics::GetArrowBoxSize() - GuiMetrics::BORDER_WIDTH, SliderY( i ) + GuiMetrics::GetItemSize() - GuiMetrics::BORDER_HEIGHT,
							   LeftArrowX() + GuiMetrics::BORDER_WIDTH,
							   SliderY( i ) + GuiMetrics::BORDER_HEIGHT + (GuiMetrics::GetItemSize() - 2 * GuiMetrics::BORDER_HEIGHT) / 2,
							   guiColors.mInteractiveForegroundColor );
			renderer.Bar(	   RightArrowX(), SliderY( i ), GuiMetrics::GetArrowBoxSize(), GuiMetrics::GetItemSize(), guiColors.mInteractiveBackgroundColor );
			renderer.Triangle( RightArrowX() + GuiMetrics::BORDER_WIDTH, SliderY( i ) + GuiMetrics::BORDER_HEIGHT,
							   RightArrowX() + GuiMetrics::BORDER_WIDTH, SliderY( i ) + GuiMetrics::GetItemSize() - GuiMetrics::BORDER_HEIGHT,
							   RightArrowX() + GuiMetrics::GetArrowBoxSize() - GuiMetrics::BORDER_WIDTH,
							   SliderY( i )  + GuiMetrics::BORDER_HEIGHT + (GuiMetrics::GetItemSize() - 2 * GuiMetrics::BORDER_HEIGHT) / 2,
							   guiColors.mInteractiveForegroundColor );		
		}

		// Get the position of the cursor;
		unsigned char pos = static_cast< unsigned char >( mColor[ static_cast< size_t >( i ) ] * 255.0f );
		if ( mType == HSV )
		{
			float h = 0, s = 0, v = 0; mColor.ToHSV( h, s, v );
			switch ( i )
			{
				// case 0: pos = alpha already;
				case 1: pos = static_cast< unsigned char >( h * 255.0f ); break;
				case 2: pos = static_cast< unsigned char >( s * 255.0f ); break;
				case 3: pos = static_cast< unsigned char >( v * 255.0f ); break;
			}
		}
	
		// Draw the slider bar;
		int sliderX = LeftArrowX() + GuiMetrics::GetArrowBoxSize() + (SliderBarWidth() - GuiMetrics::GetSliderSize()) * pos / 256;
		
		//renderer.Bar( SliderBarX(), SliderY( i ) + 8, SliderBarWidth(), GuiMetrics::GetItemSize() - 16, guiColors.mSliderColor);
		renderer.Bar( SliderBarX(), SliderY( i ) + 8, SliderBarWidth(), GuiMetrics::GetItemSize() - 16, mState == IDLE ? guiColors.mInteractiveBackgroundColor : guiColors.mInteractiveForegroundColor);
		renderer.Bar( sliderX, SliderY( i ), GuiMetrics::GetSliderSize(), GuiMetrics::GetItemSize(), guiColors.mSliderColor);

		// Draw the value;
		{
			const int maxStringLength = 32;
			char numberRepr[ maxStringLength ];
			if ( mType == RGB )
			{
				switch ( i )
				{
					case 0: renderer.String( X() + GuiMetrics::TEXT_BORDER_WIDTH, SliderY( i ), "a", guiColors.mTextColor ); break;
					case 1: renderer.String( X() + GuiMetrics::TEXT_BORDER_WIDTH, SliderY( i ), "r", guiColors.mTextColor ); break;
					case 2: renderer.String( X() + GuiMetrics::TEXT_BORDER_WIDTH, SliderY( i ), "g", guiColors.mTextColor ); break;
					case 3: renderer.String( X() + GuiMetrics::TEXT_BORDER_WIDTH, SliderY( i ), "b", guiColors.mTextColor ); break;
				}
			}
			else
			{
				switch ( i )
				{
					case 0: renderer.String( X() + GuiMetrics::TEXT_BORDER_WIDTH, SliderY( i ), "a", guiColors.mTextColor ); break;
					case 1: renderer.String( X() + GuiMetrics::TEXT_BORDER_WIDTH, SliderY( i ), "h", guiColors.mTextColor ); break;
					case 2: renderer.String( X() + GuiMetrics::TEXT_BORDER_WIDTH, SliderY( i ), "s", guiColors.mTextColor ); break;
					case 3: renderer.String( X() + GuiMetrics::TEXT_BORDER_WIDTH, SliderY( i ), "v", guiColors.mTextColor ); break;					
				}
			}
			
			Util::ToString( pos, numberRepr, maxStringLength );
			renderer.String( NumberX() + GuiMetrics::TEXT_BORDER_WIDTH, SliderY( i ), numberRepr, guiColors.mTextColor );
		}
	}

	DrawBorder( renderer, guiColors );
}

// -------------------------------------------------------------------------------
Widget* ColorControl::DoHandle( const InputState& inputState )
{
	Widget* widgetThatHandledTheInput = NULL;

	if ( mCallbackFromGame.Valid() ) mColor = mCallbackFromGame();

    if ( HasFocus() )
	{
		widgetThatHandledTheInput = this;

        if ( mState != IDLE )
		{
			if ( inputState.Released( InputState::DOWN ) )
			{
				if ( mState == DRAGGING_BLUE )
				{
					// Give control to next widget, if it exists.
					if ( Parent() ) widgetThatHandledTheInput = Parent()->GiveFocusToNextWidget( this );
				}
				else ++reinterpret_cast< int& >( mState );
			}
			else
			if ( inputState.Released( InputState::UP ) )
			{
				if ( mState == DRAGGING_ALPHA )
				{
					mState = IDLE;
				}
				else --reinterpret_cast< int& >( mState );					
			}
			else
			if ( inputState.Down( InputState::LEFT ) )
			{
				if ( inputState.Down( InputState::AUX_1   ) ) { mColor[ mState - 1 ] -= 0.010f; if ( mColor[ mState - 1 ] < 0 ) mColor[ mState - 1 ] = 0.0f; } else
				if ( inputState.Pressed( InputState::LEFT ) ) { mColor[ mState - 1 ] -= 0.005f; if ( mColor[ mState - 1 ] < 0 ) mColor[ mState - 1 ] = 0.0f; }
			}
			else
			if ( inputState.Down( InputState::RIGHT ) )
			{
				if ( inputState.Down( InputState::AUX_1    ) ) { mColor[ mState - 1 ] += 0.010f; if ( mColor[ mState - 1 ] > 1 ) mColor[ mState - 1 ] = 1; } else
				if ( inputState.Pressed( InputState::RIGHT ) ) { mColor[ mState - 1 ] += 0.005f; if ( mColor[ mState - 1 ] > 1 ) mColor[ mState - 1 ] = 1; }
			}
			else
			if ( Util::PointInRect( inputState.MouseX(), inputState.MouseY(), X(), Y(), Width(), Height() ) )
			{		
				if ( inputState.MouseMove() || inputState.Released( InputState::LEFT_MOUSE ) )
				{
					int c = 256 * (inputState.MouseX() - SliderBarX()) / (SliderBarWidth() - GuiMetrics::GetSliderSize());
					c = c < 0 ? 0 : c > 255 ? 255 : c;
				
					if ( (mState == DRAGGING_ALPHA) || (mType == RGB) )
					{
						mColor[ mState - 1 ] = static_cast< float >( c ) / 255.0f;
					}
					else
					{
						float h = 0, s = 0, v = 0;
						mColor.ToHSV( h, s, v );
						if ( mState == DRAGGING_RED )   h = static_cast< float >( c ) / 255.0f;
						if ( mState == DRAGGING_GREEN ) s = static_cast< float >( c ) / 255.0f;
						if ( mState == DRAGGING_BLUE )  v = static_cast< float >( c ) / 255.0f;
						mColor.SetFromHSV( h, s, v );
					}
				}
			}
			
			if ( inputState.Released( InputState::LEFT_MOUSE | InputState::BACK ) )
			{
				MakeParentMostRecentlyUsed();
				mState = IDLE;
			}
		}
		else
		{
			if ( inputState.Released( InputState::CONFIRM ) )
			{
				mState = DRAGGING_ALPHA;
			}
			else
			if ( inputState.Released( InputState::UP ) )
			{					
				if ( Parent() ) widgetThatHandledTheInput = Parent()->GiveFocusToPrevWidget( this );
				// If no previous widget exists, handle as though in choosing state.
				if( widgetThatHandledTheInput == this ) {
					mState = DRAGGING_BLUE;
				}
			}
			else
			if ( inputState.Released( InputState::DOWN ) )
			{					
				if ( Parent() ) widgetThatHandledTheInput = Parent()->GiveFocusToNextWidget( this );
				// If no previous widget exists, handle as though in choosing state.
				if( widgetThatHandledTheInput == this ) {
					mState = DRAGGING_ALPHA;
				}
			}
			else
			if ( inputState.Released( InputState::BACK | InputState::LEFT ) )
			{					
				widgetThatHandledTheInput = GiveFocusToParent();
			}
			else				
			if ( inputState.Released( InputState::LEFT_MOUSE ) )
			{
				MakeParentMostRecentlyUsed();

				if ( Util::PointInRect( inputState.MouseX(), inputState.MouseY(), X(), Y(), Width(), GuiMetrics::GetRowHeight() ) )
				{
                    if ( mType == RGB ) mType = HSV; else mType = RGB;
				}
				else
				{		
					float hsv[ 3 ] = { 0.0f, 0.0f, 0.0f };
					if ( mType == HSV ) mColor.ToHSV( hsv[ 0 ], hsv[ 1 ], hsv[ 2 ] );

					for ( int i = 0; i < 4; ++i )
					{					
						if ( Util::PointInRect( inputState.MouseX(), inputState.MouseY(), LeftArrowX(), SliderY( i ),
												Impl::GuiMetrics::GetArrowBoxSize(), Impl::GuiMetrics::GetItemSize() ) )
						{
							if ( (i != 0 /*alpha*/) && (mType == HSV) && (hsv[ i - 1 ] > 0.0f) ) hsv[ i - 1 ] -= 1.0f / 255.0f;
							else if ( mColor[ static_cast< size_t >( i ) ] > 0.0f ) mColor[ static_cast< size_t >( i ) ] -= 1.0f / 255.0f;
							break;
						}
						else
						if ( Util::PointInRect( inputState.MouseX(), inputState.MouseY(), RightArrowX(), SliderY( i ),
												Impl::GuiMetrics::GetArrowBoxSize(), Impl::GuiMetrics::GetItemSize() ) )
						{
							if ( (i != 0 /*alpha*/) && (mType == HSV) && (hsv[ i - 1 ] < 1.0f) ) hsv[ i - 1 ] += 1.0f / 255.0f;
							else if ( mColor[ static_cast< size_t >( i ) ] < 1.0f ) mColor[ static_cast< size_t >( i ) ] += 1.0f / 255.0f;
							break;
						}
					}

					if ( mType == HSV ) mColor.SetFromHSV( hsv[ 0 ], hsv[ 1 ], hsv[ 2 ] );
				}
			}
			else
			if ( inputState.Down( InputState::LEFT_MOUSE ) )
			{
				for ( int i = 0; i < 4; ++i )
				{
					if ( Util::PointInRect( inputState.MouseX(), inputState.MouseY(), SliderBarX(), SliderY( i ), SliderBarWidth(), GuiMetrics::GetItemSize() ) )
					{
						if ( i == 0 ) mState = DRAGGING_ALPHA; else
						if ( i == 1 ) mState = DRAGGING_RED;   else
						if ( i == 2 ) mState = DRAGGING_GREEN; else
						if ( i == 3 ) mState = DRAGGING_BLUE;
					}
				}
			}
		}

		mCallbackToGame( mColor );
	}
	return widgetThatHandledTheInput;
}

// -------------------------------------------------------------------------------
size_t ColorControl::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( mColor, 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;
}

// -------------------------------------------------------------------------------
void ColorControl::DoFromString( const char* data )
{
    ++data;
	for ( size_t i = 0; i < 4; ++i )
	{	
		while ( (*data == ' ') || (*data == '\t') || (*data == '\n') ) ++data; // Skip whitespace;
		float component = Util::StringToFloat( data );
		mColor[ i ] = component;	
		while ( (*data != ' ') && (*data != '\0') && (*data != '\"') ) ++data; // Skip non-whitespace;
	}

	mCallbackToGame( mColor );
}


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