/******************************************************************************
 * $Id: gfx_gui_opengl.cc,v 1.68 2006/07/30 18:42:30 devel Exp $
 * @file
 * @author	Jim E. Brooks  http://www.jimbrooks.org
 * @brief	Simple lightweight GUI inside of a viewport (OpenGL).
 *          Components are drawn alpha-blended, and include text fonts, buttons, etc.
 *//*
 * LEGAL:	COPYRIGHT (C) 2004 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE (GPL).
 ******************************************************************************/

#define GFX_GUI_OPENGL_CC 1
#include "gfx.hh"
#include "gfx_stream.hh"
#include "gfx_gui.hh"
#include "gfx_gui_opengl.hh"
namespace gfx {

// Colors.
static const RGBA sRgbaNormal			( 0.6, 0.6, 0.6, 1.0 );
static const RGBA sRgbaHilite			( 1.0, 1.0, 1.0, 1.0 );
static const RGBA sRgbaButton			( 0.0, 0.0, 0.6, 0.2 );
static const RGBA sRgbaButtonClicked	( 1.0, 0.4, 0.4, 1.0 );
static const RGBA sRgbaButtonHilite		( 0.8, 1.0, 0.8, 1.0 );
static const RGBA sRgbaButtonBg			( 1.0, 1.0, 1.0, 0.2 );
static const RGBA sRgbaButtonBgHilite	( 0.8, 1.0, 0.8, 0.4 );
static const RGBA sRgbaButtonInsens		( 0.4, 0.4, 0.4, 0.5 );
static const RGBA sRgbaButtonBgInsens	( 0.4, 0.4, 0.4, 0.1 );
static const RGBA sRgbaGray				( 0.4, 0.4, 0.4, 1.0 );
static const RGBA sRgbaRed				( 1.0, 0.1, 0.1, 1.0 );
static const RGBA sRgbaBlue				( 0.1, 0.2, 0.8, 1.0 );
static const RGBA sRgbaBlue2			( 0.2, 0.4, 0.7, 1.0 );

static Gui* sGui; // for use only by pure C functions

////////////////////////////////////////////////////////////////////////////////
/////////////////////////////  Gui class  //////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * Constructor.
 *****************************************************************************/
Gui::Gui( const Geom<int>& viewport, const Geom<int>& window )
{
	DEBUG_CODE( if ( sGui != NULL ) *gStreamDebug << "Gui::Gui() can only be constructed once\n"; )
	sGui = this; // for use only by pure C functions
	mViewportInitial.Copy( viewport );
	ReshapeViewport( viewport, window );
	NewFonts();
#if 0
	// -- Multiple GLUT timers caused concurrency issues.
	// -- Let the application create one and only one timer.
	// -- Left as a warning.
	glutTimerFunc( 1000 /*1 second*/, GfxsysTimerOneSecond, 0 );
#endif
}
Gui::~Gui()
{
	DeleteFonts();
}

/*****************************************************************************
 * Initialize fonts.
 *****************************************************************************/
void
Gui::NewFonts( void )
{
	// ---------- actual fonts --------------------
	// Font #1 with different sizes.
	extern uchar gGfxFont1Data[];
	extern uint  gGfxFont1DataLen;
	static FontDesc fontDescText(   "fontText",   gGfxFont1Data, gGfxFont1DataLen, 32, '!', 64, true/*AA*/, 0.8 );
	static FontDesc fontDescButton( "fontButton", gGfxFont1Data, gGfxFont1DataLen, 32, '!', 64, true/*AA*/, 0.8 );
	mFontText   = new Font( &fontDescText,   this );
	mFontButton = new Font( &fontDescButton, this );

	// (thin) Font #2 with different sizes.
	extern uchar gGfxFont2Data[];
	extern uint  gGfxFont2DataLen;
	static FontDesc fontDescTextThin( "fontTextThin", gGfxFont2Data, gGfxFont2DataLen, 32, '!', 64, true/*AA*/, 0.8 );
	mFontTextThin = new Font( &fontDescTextThin, this );
	// ---------- actual fonts --------------------

	// ---------- logical fonts --------------------
	mLfontText		= mFontText;
	mLfontTextThin	= mFontTextThin;
	mLfontButton	= mFontButton;
	// ---------- logical fonts --------------------
}
void
Gui::DeleteFonts( void )
{
	delete mFontText;
	delete mFontTextThin;
	delete mFontButton;
}

/*****************************************************************************
 * Translate mouse position to a 2D viewport coord.
 * Reason is that OpenGL's notion of Y is reverse of GLUT's mouse Y.
 *****************************************************************************/
void
Gui::MousePos2ViewportPos( const Pos2d<float>& in, Pos2d<float>& out )
{
	out.x =               in.x + mViewportDelta[0];
	out.y = mViewport.h - in.y + mViewportDelta[1];
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////  Button class  ////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * Constructor.
 *****************************************************************************/
GuiButton::GuiButton( GuiScreen& screen, float x, float  y, eFontMode fontMode, fnMouseButtonCallback callback )
	: GuiButtonBase(screen,x,y,fontMode,callback)
{
	ResetRGBA();
}

/*****************************************************************************
 * Render button.
 *****************************************************************************/
void
GuiButton::Render( void )
{
	if ( ! IfVisi() || mText.empty() ) return;

	// Print button text.
	// Need extra padding in order to draw a frame (esp. if at edge of viewport).
	GFX_RGBA( IfSens() ? mRgbaBase : sRgbaButtonInsens );
	mGui.GetButtonFont()->SetPadding( Font::eFontPad_DEFAULT + 2 );
	int errors = mGui.GetButtonFont()->Print( mText, mPos.x, mPos.y, mFontMode );
	mGui.GetButtonFont()->SetPadding( Font::eFontPad_DEFAULT );
	if ( errors ) return;

	// Draw a frame around the button text.
	// The Font class provides an otherwise private rectangle data
	// where the text string was drawn on the 2D viewport.
	const Rect<float>* printRect = mGui.GetButtonFont()->LastPrintRect();
	mRect.x1 = floor(printRect->x1); // discarding fraction improves button appearance on Win32
	mRect.y1 = floor(printRect->y1);
	mRect.x2 = floor(printRect->x2);
	mRect.y2 = floor(printRect->y2);

	GFX_BEGIN_2D( &mGui.mViewport );
	GFX_LINE_WIDTH( 2 );
	GFX_BEGIN_ALPHA_BLEND();

	if ( IfSens() )
		GFX_RGBA( mMouseEvent & eMouseEvent_MOTION_ENTER ? sRgbaButtonBgHilite : sRgbaButtonBg );
	else
		GFX_RGBA( sRgbaButtonBgInsens );
	GFX_BEGIN( GP_QUADS );
	GFX_VERTEX_2D( mRect.x1-2, mRect.y1   ); // NW
	GFX_VERTEX_2D( mRect.x1-2, mRect.y2+2 ); // SW
	GFX_VERTEX_2D( mRect.x2+2, mRect.y2+2 ); // SE
	GFX_VERTEX_2D( mRect.x2+2, mRect.y1   ); // NE
	GFX_END();

	if ( IfSens() )
	{
		if ( mMouseEvent & eMouseEvent_MOTION_ENTER )
		{
			if ( mMouseEvent & eMouseEvent_BUTTON_1 )
				GFX_RGBA( sRgbaButtonClicked );
			else
				GFX_RGBA( sRgbaButtonHilite );
		}
		else
			 GFX_RGBA( mRgbaBase );
	}
	else
		GFX_RGBA( sRgbaButtonInsens );
	GFX_BEGIN( GP_LINE_LOOP );
	GFX_VERTEX_2D( mRect.x1-2, mRect.y1   ); // NW
	GFX_VERTEX_2D( mRect.x1-2, mRect.y2+2 ); // SW
	GFX_VERTEX_2D( mRect.x2+2, mRect.y2+2 ); // SE
	GFX_VERTEX_2D( mRect.x2+2, mRect.y1   ); // NE
	GFX_END();

	GFX_END_ALPHA_BLEND();
	GFX_END_2D();
}

/*****************************************************************************
 * Reset button color.
 *****************************************************************************/
void
GuiButton::ResetRGBA( void )
{
	mRgbaBase = sRgbaButton;
}

} // namespace gfx
