/*  libatheos.so - the highlevel API library for Syllable
 *  Copyright (C) 2002 - 2003 Rick Caudill
 *  Copyright (C) 2004 Syllable Team
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of version 2 of the GNU Library
 *  General Public License as published by the Free Software
 *  Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the Free
 *  Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 *  MA 02111-1307, USA
 */
 
/* THIS IS A HACK OF THE LIBSYLLABLE IMAGEBUTTON CLASS WITH SUPPORT FOR MOUSEOVER IMAGES */
 
 
#include "cimagebutton.h"
#include 
#include 
#include 
using namespace os;

/** \internal */
class CImageButton::Private
{
      public:
	Private()
	{
		m_pcBitmap = NULL;
		m_pcSelectedBitmap = NULL;
		m_pcGrayBitmap = NULL;
		m_nTextPosition = 0;
		bMouseOver = false;
		bShowFrame = false;
		bMouseTwo = false;
	}

	~Private()
	{
		if( m_pcBitmap )
			delete m_pcBitmap;
			
		if( m_pcSelectedBitmap )
			delete m_pcSelectedBitmap;

		if( m_pcGrayBitmap )
			delete m_pcGrayBitmap;
	}

	void SetImage( Image * pcBitmap )
	{
		if( m_pcBitmap )
			delete m_pcBitmap;

		m_pcBitmap = pcBitmap;
	}
	
	void SetSelectedImage( Image * pcBitmap )
	{
		if( m_pcSelectedBitmap )
			delete m_pcSelectedBitmap;

		m_pcSelectedBitmap = pcBitmap;
	}

	Image *GetImage()
	{
		return m_pcBitmap;
	}
	
	Image *GetSelectedImage()
	{
		if( m_pcSelectedBitmap )
			return m_pcSelectedBitmap;
		return m_pcBitmap;
	}

	Image *GetGrayImage()
	{
		if( !m_pcGrayBitmap )
		{
			/* Note: This is not a good method, since it will only work on Bitmaps. */
			/* We have to add some method to Image for copying Images of unknown type! */
			if( m_pcBitmap && ( typeid( *m_pcBitmap ) == typeid( BitmapImage ) ) )
			{
				m_pcGrayBitmap = new BitmapImage( *( dynamic_cast < BitmapImage * >( m_pcBitmap ) ) );
				m_pcGrayBitmap->ApplyFilter( Image::F_GRAY );
			}
		}
		return m_pcGrayBitmap ? m_pcGrayBitmap : m_pcBitmap;
	}

	Image *m_pcBitmap;
	Image *m_pcSelectedBitmap;
	Image *m_pcGrayBitmap;
	uint32 m_nTextPosition;
	bool bMouseOver;	// true for mouse over mode
	bool bShowFrame;	// true when frames are shown
	bool bMouseTwo;		// true if the mouse is actually over the frame
};

/** Initialize the imagebutton.
 * \par Description:
 *	The imagebutton contructor initialize the local object.
 * \param cFrame - The size and position of the imagebutton.
 * \param pzName - The name of the imagebutton.
 * \param pzLabel - The label of the imagebutton.
 * \param pcMessage - The message passed to the imagebutton.
 * \param pcImage - The bitmapimage for the imagebutton.
 * \param nTextPosition - The Position of the label and the image for the imagebutton.
 * \param bShowFrames - Determines whether the imagebutton will have frames or not.
 * \param bShowText - Determines whether the imagebutton will show the label or not.
 * \param bMouse - Determines whether the imagebutton will have mouseover abilities(this is not working yet).
 * \param nResizeMask - Determines what way the imagebutton will follow the rest of the window.  Default is CF_FOLLOW_LEFT|CF_FOLLOW_TOP.
 * \param nFlags - Deteremines what flags will be sent to the imagebutton.
 * \author	Rick Caudill (cau0730@cup.edu) based on Andrew Keenan's ImageButton class
 *****************************************************************************/

CImageButton::CImageButton( Rect cFrame, const String& cName, const String& cLabel, Message * pcMessage, Image * pcBitmap, uint32 nTextPosition, bool bShowFrames, bool bShowText, bool bMouse, uint32 nResizeMask, uint32 nFlags ):Button( cFrame, cName, cLabel, pcMessage, nResizeMask, nFlags )
{
	m = new Private;

	m->m_nTextPosition = nTextPosition;
	m->bShowFrame = bShowFrames;
	m->bMouseOver = bMouse;

	if( bShowText == true )
		SetLabel( cLabel );
	else
	{
		SetLabel( "" );
	}
	m->SetImage( pcBitmap );

}				/*end ImageButton Constructor */

/** \internal */
CImageButton::~CImageButton()
{
	delete m;
}				/*end ImageButton Destructor */


void CImageButton::Activated( bool bIsActive )
{
	if( ( bIsActive == true ) && ( GetWindow()->IsActive() == true ) )
	{
		Paint( GetBounds() );
	}
}				/*end Activated() */


/** Clears the image...
 * \par Description:
 *	Clears the BitmapImage that is passed to the ImageButton.
 * \author	Rick Caudill (cau0730@cup.edu)
 *****************************************************************************/
void CImageButton::ClearImage( void )
{
	m->SetImage( NULL );
	Invalidate();
	Flush();
}				/*end ClearImage() */

/** Gets the Image...
 * \par Description:
 * Gets the Image that is passed to the ImageButton.
 * \author	Rick Caudill (cau0730@cup.edu)
 *****************************************************************************/
Image *CImageButton::GetImage( void )
{
	return m->m_pcBitmap;
}				/*end GetImage() */

/* GetPreferredSize()*/
Point CImageButton::GetPreferredSize( bool bLargest ) const
{
	if( bLargest )
	{
		return Point( COORD_MAX, COORD_MAX );
	} else {
		Point cSize;
/*		if( !( GetLabel() == "" ) ) {
			cSize = GetTextExtent( GetLabel() );
			cSize.x += 16;
			cSize.y += 8;
		}*/
		if( m->m_pcBitmap ) {
			Point cImgSize( m->m_pcBitmap->GetSize() );

			if( cSize.x > 0 ) {
				if( ( m->m_nTextPosition == IB_TEXT_RIGHT ) || ( m->m_nTextPosition == IB_TEXT_LEFT ) ) {
					cSize.x += cImgSize.x + 2;
					cSize.y = std::max( cSize.y, cImgSize.y );
				} else {
					cSize.y += cImgSize.y + 2;
					cSize.x = std::max( cSize.x, cImgSize.x );
				}
			} else {
				cSize.x = cImgSize.x + 4;
				cSize.y = cImgSize.y + 4;
			}
		}
		return cSize;
	}
/*
	font_height sHeight;

	GetFontHeight( &sHeight );
	float vCharHeight = sHeight.ascender + sHeight.descender + 8;
	float vStrWidth = GetStringWidth( GetLabel() ) + 16;

	float x = vStrWidth;
	float y = vCharHeight;

	if( m->m_pcBitmap )
	{
		Rect cBitmapRect = m->m_pcBitmap->GetBounds();

		if( vStrWidth > 16 )
		{
			if( ( m->m_nTextPosition == IB_TEXT_RIGHT ) || ( m->m_nTextPosition == IB_TEXT_LEFT ) )
			{
				x += cBitmapRect.Width() + 2;
				y = ( ( cBitmapRect.Height() + 8 ) > y ) ? cBitmapRect.Height() + 8 : y;
			}
			else
			{
				x = ( ( cBitmapRect.Width() + 8 ) > x ) ? cBitmapRect.Width() + 8 : x;
				y += cBitmapRect.Height() + 2;
			}
		}
		else
		{
			x = cBitmapRect.Width() + 8;
			y = cBitmapRect.Height() + 8;
		}
	}

	return Point( x, y );*/
}				/*end GetPreferredSize() */

/** Gets the text postion...
 * \par Description:
 *	Gets the text positon of the ImageButton.
 * \author	Rick Caudill (cau0730@cup.edu)
 *****************************************************************************/
uint32 CImageButton::GetTextPosition( void )
{
	return m->m_nTextPosition;
}				/*end GetTextPosition() */

/* MouseDown*/
void CImageButton::MouseDown( const Point & cPosition, uint32 nButton )
{
	Button::MouseDown( cPosition, nButton );
}				/*end MouseDown() */

/* MouseMove */
void CImageButton::MouseMove( const Point & cNewPos, int nCode, uint32 nButtons, Message * pcData )
{
	Button::MouseMove( cNewPos, nCode, nButtons, pcData );

	if( m->bMouseOver )
	{
		bool mouseover = GetBounds().DoIntersect( cNewPos );

		if( mouseover != m->bMouseTwo )
		{
			m->bMouseTwo = mouseover;
			Invalidate();
			Flush();
		}
	}

	return;
}				/*end MouseMove() */

/*MouseUp*/
void CImageButton::MouseUp( const Point & cPosition, uint32 nButton, Message * pcData )
{
	Button::MouseUp( cPosition, nButton, pcData );
}				/*ehd MouseUp() */

void CImageButton::Paint( const Rect & cUpdateRect )
{
	Rect cBounds = GetBounds();
	Rect cTextBounds = GetBounds();
	float vStrWidth = GetStringWidth( GetLabel() );
	Point cBitPoint;
	font_height sHeight;

	GetFontHeight( &sHeight );
	float vCharHeight = sHeight.ascender + sHeight.descender;
	Image *pcImg = IsEnabled()? m->GetImage() : m->GetGrayImage();
	
	if( m->bMouseOver == true && m->bMouseTwo == true && IsEnabled() )
		pcImg = m->GetSelectedImage();

	if( pcImg )
	{

		switch ( m->m_nTextPosition )
		{
		case IB_TEXT_RIGHT:
			cTextBounds.left -= pcImg->GetSize().x - 2;
			cBitPoint.x = 4;
			cBitPoint.y = GetBounds().top + 3;
			break;

		case IB_TEXT_LEFT:
			cTextBounds.left = 0;
			cTextBounds.right = vStrWidth;
			cBitPoint.x = cTextBounds.right + 2;
			cBitPoint.y = GetBounds().top + 3;
			break;

		case IB_TEXT_BOTTOM:
			cTextBounds.top -= pcImg->GetSize().y;
			cBitPoint.x = ( GetBounds().Width() - pcImg->GetSize().x ) / 2;
			cBitPoint.y = 4;
			break;

		case IB_TEXT_TOP:
			cTextBounds.bottom = vCharHeight;
			cTextBounds.left = 0;
			cTextBounds.top = 0;
			cBitPoint.y = cTextBounds.bottom + 2;
			cBitPoint.x = ( GetBounds().Width() / 2 - pcImg->GetSize().x / 2 );
			break;
		default:
			break;
		}

	}

	SetEraseColor( get_default_color( COL_NORMAL ) );
	SetBgColor( get_default_color( COL_NORMAL ) );
	SetFgColor( get_default_color( COL_NORMAL ) );

	Rect border = GetBounds();

	if( m->bShowFrame == false )
	{
		if( m->bMouseOver == true )
		{
			FillRect( cBounds, get_default_color( COL_NORMAL ) );
			SetFgColor( 0, 255, 0 );
			Rect cBounds2 = ConvertToScreen( GetBounds() );
			Rect cWinBounds = GetWindow()->GetFrame();
		}
		else
		{
			FillRect( cBounds, get_default_color( COL_NORMAL ) );
			SetFgColor( 0, 255, 0 );
			Rect cBounds2 = ConvertToScreen( GetBounds() );
			Rect cWinBounds = GetWindow()->GetFrame();
		}
	}
	else
	{
		if( m->bMouseOver == false || ( m->bMouseTwo == true && IsEnabled() ) )
		{
			uint32 nFrameFlags = ( GetValue().AsBool() && IsEnabled() )? FRAME_RECESSED : FRAME_RAISED;

			DrawFrame( cBounds, nFrameFlags );
		}
		else
		{
			FillRect( cBounds, get_default_color( COL_NORMAL ) );
		}
	}

	float y = floor( 2.0f + ( cTextBounds.Height() + 1.0f ) * 0.5f - vCharHeight * 0.5f + sHeight.ascender );
	float x = floor( ( cTextBounds.Width() + 1.0f ) / 2.0f - vStrWidth / 2.0f );

	if( GetWindow()->GetDefaultButton() == this && IsEnabled() )
	{
		SetFgColor( 0, 0, 0 );
		DrawLine( Point( cBounds.left, cBounds.top ), Point( cBounds.right, cBounds.top ) );
		DrawLine( Point( cBounds.right, cBounds.bottom ) );
		DrawLine( Point( cBounds.left, cBounds.bottom ) );
		DrawLine( Point( cBounds.left, cBounds.top ) );
		cBounds.left += 1;
		cBounds.top += 1;
		cBounds.right -= 1;
		cBounds.bottom -= 1;
	}

	if( !GetLabel().empty() )
	{
		if( IsEnabled() )
		{
			if( HasFocus() )
			{
				SetFgColor( 0, 0, 0 );
				DrawLine( Point( ( cTextBounds.Width() + 1.0f ) * 0.5f - vStrWidth * 0.5f, y + sHeight.descender - sHeight.line_gap / 2 - 1.0f ), Point( ( cTextBounds.Width() + 1.0f ) * 0.5f + vStrWidth * 0.5f, y + sHeight.descender - sHeight.line_gap / 2 - 1.0f ) );
			}

			SetFgColor( 0, 0, 0 );
			MovePenTo( x, y );
			DrawString( GetLabel() );
		}
		else
		{
			MovePenTo( x, y );
			DrawString( GetLabel() );
			MovePenTo( x - 1.0f, y - 1.0f );
			SetFgColor( 100, 100, 100 );
			SetDrawingMode( DM_OVER );
			DrawString( GetLabel() );
			SetDrawingMode( DM_COPY );
		}
	}

	if( pcImg )
	{
		SetDrawingMode( DM_BLEND );	//DM_BLEND
		pcImg->Draw( cBitPoint, this );
		SetDrawingMode( DM_OVER );	//sets the mode to dm_over, so that hovering works completely
	}
}				/*end Paint() */

/** Resests the text postion...
 * \par Description:
 *	Resests the text postion and then flushes the imagebutton.
 * \param nTextPosition - int that holds the text position.  Should be: ID_TEXT_BOTTOM,
 	ID_TEXT_RIGHT, ID_TEXT_TOP, or ID_TEXT_LEFT.
 * \author	Rick Caudill (cau0730@cup.edu)
 *****************************************************************************/
void CImageButton::SetTextPosition( uint32 nTextPosition )
{
	m->m_nTextPosition = nTextPosition;
	Invalidate();
	Flush();
}				/*end SetTextPosition() */

/**Load an image from a stream.
 * \par Description:
 *	Reads the BitmapImage for the ImageButton from a data stream.
 * \param pcStream - A StreamableIO data source for the Bitmap data.
 * \author	Kristian Van Der Vliet (vanders@liqwyd.com)
 *****************************************************************************/
void CImageButton::SetImage( StreamableIO* pcStream )
{
	Image *vImage = new BitmapImage( pcStream );
	m->SetImage( vImage );
}


/**Load an image from a stream.
 * \par Description:
 *	Reads the BitmapImage for the ImageButton from a data stream.
 * \param pcStream - A StreamableIO data source for the Bitmap data.
 * \author	Kristian Van Der Vliet (vanders@liqwyd.com)
 *****************************************************************************/
void CImageButton::SetSelectedImage( StreamableIO* pcStream )
{
	Image *vImage = new BitmapImage( pcStream );
	m->SetSelectedImage( vImage );
}

/** Copy an ImageButton
 * \par Description:
 * Copy the Image from one ImageButton to another
 * \param cImageButton - The ImageButton to copy from.
 * \author Kristian Van Der Vliet (vanders@liqwyd.com)
 *****************************************************************************/
CImageButton& CImageButton::operator=( CImageButton& cImageButton )
{
	m->SetImage( cImageButton.GetImage() );
	m->m_nTextPosition = cImageButton.m->m_nTextPosition;
	SetLabel( cImageButton.GetLabel() );
	return( *this );
}

/** Sets the Image from another image...
 * \par Description:
 * Sets the Image for the ImageButton from another Image.
 * \param pcImage - The image.
 * \author  Rick Caudill (cau0730@cup.edu)
 *****************************************************************************/
void CImageButton::SetImage( Image * pcImage )
{
	if( pcImage )
		m->SetImage( pcImage );
}



/** Sets the Image from another image...
 * \par Description:
 * Sets the Image for the ImageButton from another Image.
 * \param pcImage - The image.
 * \author  Rick Caudill (cau0730@cup.edu)
 *****************************************************************************/
void CImageButton::SetSelectedImage( Image * pcImage )
{
	if( pcImage )
		m->SetSelectedImage( pcImage );
}