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

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

/*! \file */

#include "reveal/impl/widget.h"

#include "reveal/impl/util.h"
#include "reveal/impl/menu.h"
#include "reveal/impl/tree.h"

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

#include <ctype.h>
#include <string.h>

#ifdef REVEAL_EXCEPTIONS
	#include <exception>
#endif

namespace Reveal {
	
	namespace Impl {

// ------------------------------------------------------------------------
/*
		Widget::Widget( const char* name, Tree& tree, Menu* parent )
	:
mX( 0 ),
mY( 0 ),
mVisible( false ),
mParent( parent ),
mTree( tree ),
mHelp( NULL )
{
	strncpy( mName, name, MAX_NAME_STRING_LENGTH );
	mName[ MAX_NAME_STRING_LENGTH - 1 ] = '\0';
	for ( size_t i = 0; i < MAX_NAME_STRING_LENGTH; ++i ) mName[ i ] = static_cast< char >( tolower( static_cast< unsigned char >( mName[ i ] ) ) );	
}
*/

// ------------------------------------------------------------------------
Widget::~Widget() 
{
}

// ------------------------------------------------------------------------
void Widget::Update()
{
}

// ------------------------------------------------------------------------
Widget* Widget::Handle( const InputState& inputState )
{
	Widget* widgetThatHandledTheInput = NULL;
	if ( IsVisible() )
	{
		if (   inputState.Down(		InputState::LEFT_MOUSE | InputState::RIGHT_MOUSE ) || inputState.Released( InputState::LEFT_MOUSE | InputState::RIGHT_MOUSE ) )
		{
      if ( mTree.GetWidgetWithFocus() == this )
			{
				if ( ! MouseOver( inputState ) )
				{
					mTree.SetWidgetWithFocus( 0 );

					//return only if confirm menu expansion of NOT enabled
					if (mTree.GetConfirmMenuExpansion() == false)
					{
            return 0;
					}
				}
			}
		}

		//default to no input
		bool inputRecieved = false;

		//is confirm feature enabled?
		if (mTree.GetConfirmMenuExpansion() == true)
		{
			//did we get any input?
			inputRecieved = inputState.Pressed(InputState::LEFT_MOUSE | InputState::CONFIRM);
		}

		if ((inputState.MouseMove()) || (inputRecieved))
		{
			if ( MouseOver( inputState ) )
			{
				if ( !mTree.GetWidgetWithFocus() || !mTree.GetWidgetWithFocus()->MouseOver( inputState ) )
				{
					//if confirm feature is disabled OR if any input was recieved (only set if confirm feature is on)
					if ((mTree.GetConfirmMenuExpansion() == false) || (inputRecieved))
					{
						mTree.SetWidgetWithFocus( this );
					}
				}
					
			}		
			else
			{
				if ( mTree.GetWidgetWithFocus() == this ) mTree.SetWidgetWithFocus( 0 );
			}
		}		
		widgetThatHandledTheInput = DoHandle( inputState );
	}	
	return widgetThatHandledTheInput;
}


// ------------------------------------------------------------------------
void Widget::Render( const Renderer& renderer, const GuiColors& guiColors ) const
{
	if ( IsVisible() ) DoRender( renderer, guiColors );
}

// ------------------------------------------------------------------------
void Widget::DrawBorder( const Renderer& renderer, const GuiColors& guiColors ) const
{
	renderer.Line( X()			, Y()			, X() + Width(), Y()		   , mTree.GetGuiColors().mBorderColor );
	renderer.Line( X()			, Y() + Height(), X() + Width(), Y() + Height(), mTree.GetGuiColors().mBorderColor );
	renderer.Line( X()			, Y()			, X()		   , Y() + Height(), mTree.GetGuiColors().mBorderColor );
	renderer.Line( X() + Width(), Y()			, X() + Width(), Y() + Height(), mTree.GetGuiColors().mBorderColor );
}

// -------------------------------------------------------------------------------
void Widget::MakeParentMostRecentlyUsed()
{
	if ( mParent ) mParent->MakeMostRecentlyUsed();
}

// -------------------------------------------------------------------------------
size_t Widget::PathAndNameToString( const char* path, char* buffer, size_t size ) const
{
	char* s = buffer;

	if  ( size >= 1 )
	{
		*s++ = '\"';
	} else return s - buffer;

    const size_t pathLength = strlen( path );
	if ( pathLength < size )
	{
        strncpy( s, path, pathLength );
		s	 += pathLength;
		size -= pathLength;
	}
	else return s - buffer;

	if ( size >= 4 )
	{
		*s++ = '\"';
		*s++ = ' ';
		*s++ = '\"';
		size -= 3;
	}
	else return s - buffer;

	const size_t nameLength = strlen( mName );
	if ( nameLength < size )
	{
        strncpy( s, mName, nameLength );
		s    += nameLength;
		size -= nameLength;
	}
	else return s - buffer;

	if ( size >= 1 )
	{
		*s++ = '\"';
		*s++ = ' ';
		size += 2;
	}
	else
	{
		s[ -1 ] = ' ';
		s[ -2 ] = '\"';
	}
	
	return s - buffer;
}

// -------------------------------------------------------------------------------
Widget* Widget::GiveFocusToParent()
{
	if ( Parent() )
	{	
		mTree.SetWidgetWithFocus( Parent() );
		return Parent();
	}
	else return this;
}

// -------------------------------------------------------------------------------
void* Widget::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
		}
	}
}

// -------------------------------------------------------------------------------
void Widget::operator delete( void* ptr )
{
	Memory::RevealFree( ptr );
}

// -------------------------------------------------------------------------------
void Widget::LoseFocus()
{
	DoLoseFocus();
}

// -------------------------------------------------------------------------------
void Widget::GiveFocus()
{
	if ( mParent && (mParent->OpenedChild() != this) ) mParent->CloseOpenedChild();
	DoGiveFocus();
}


// -------------------------------------------------------------------------------
size_t Widget::ToString( 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( mValue, 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;*/

	return DoToString( path, buffer, size );
}

// -------------------------------------------------------------------------------
bool Widget::HasFocus() const
{
	return mTree.GetWidgetWithFocus() == this;
}

// -------------------------------------------------------------------------------
bool Widget::MouseOver( const InputState& inputState ) const
{
	return Util::PointInRect( inputState.MouseX(), inputState.MouseY(), X(), Y(), Width(), Height() );
}

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