/** 
 * @file llcolorswatch.cpp
 * @brief LLColorSwatch class implementation
 *
 * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
 * $License$
 */

#include "llviewerprecompiledheaders.h"

// File include
#include "llcolorswatch.h"

// Linden library includes
#include "v4color.h"

// Project includes
#include "llui.h"
#include "lluiconstants.h"
#include "llviewerwindow.h"
#include "llviewercontrol.h"
#include "llbutton.h"
#include "lltextbox.h"
#include "llfloatercolorpicker.h"
#include "llviewborder.h"
#include "llviewerimagelist.h"
#include "llfocusmgr.h"

LLColorSwatchCtrl::LLColorSwatchCtrl(const std::string& name, const LLRect& rect, const LLColor4& color,
		void (*commit_callback)(LLUICtrl* ctrl, void* userdata),
		void* userdata )
:	LLUICtrl(name, rect, TRUE, commit_callback, userdata, FOLLOWS_LEFT | FOLLOWS_TOP),
	mValid( TRUE ),
	mColor( color ),
	mBorderColor( gColors.getColor("DefaultHighlightLight") ),
	mCanApplyImmediately(FALSE),
	mOnCancelCallback(NULL),
	mOnSelectCallback(NULL)
{
	mCaption = new LLTextBox( name,
		LLRect( 0, BTN_HEIGHT_SMALL, mRect.getWidth(), 0 ),
		LLString::null,
		LLFontGL::sSansSerifSmall );
	mCaption->setFollows( FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_BOTTOM );
	addChild( mCaption );

	// Scalable UI made this off-by-one, I don't know why. JC
	LLRect border_rect(0, mRect.getHeight()-1, mRect.getWidth()-1, 0);
	border_rect.mBottom += BTN_HEIGHT_SMALL;
	mBorder = new LLViewBorder("border", border_rect, LLViewBorder::BEVEL_IN);
	addChild(mBorder);

	mAlphaGradientImage = gImageList.getImageFromUUID(LLUUID(gViewerArt.getString("color_swatch_alpha.tga")),
													  MIPMAP_FALSE, TRUE, GL_ALPHA8, GL_ALPHA);
}

LLColorSwatchCtrl::LLColorSwatchCtrl(const std::string& name, const LLRect& rect, const std::string& label, const LLColor4& color,
		void (*commit_callback)(LLUICtrl* ctrl, void* userdata),
		void* userdata )
:	LLUICtrl(name, rect, TRUE, commit_callback, userdata, FOLLOWS_LEFT | FOLLOWS_TOP),
	mValid( TRUE ),
	mColor( color ),
	mBorderColor( gColors.getColor("DefaultHighlightLight") ),
	mCanApplyImmediately(FALSE),
	mOnCancelCallback(NULL),
	mOnSelectCallback(NULL)
{
	mCaption = new LLTextBox( label,
		LLRect( 0, BTN_HEIGHT_SMALL, mRect.getWidth(), 0 ),
		LLString::null,
		LLFontGL::sSansSerifSmall );
	mCaption->setFollows( FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_BOTTOM );
	addChild( mCaption );

	// Scalable UI made this off-by-one, I don't know why. JC
	LLRect border_rect(0, mRect.getHeight()-1, mRect.getWidth()-1, 0);
	border_rect.mBottom += BTN_HEIGHT_SMALL;
	mBorder = new LLViewBorder("border", border_rect, LLViewBorder::BEVEL_IN);
	addChild(mBorder);

	mAlphaGradientImage = gImageList.getImageFromUUID(LLUUID(gViewerArt.getString("color_swatch_alpha.tga")),
													  MIPMAP_FALSE, TRUE, GL_ALPHA8, GL_ALPHA);
}

LLColorSwatchCtrl::~LLColorSwatchCtrl ()
{
	// parent dialog is destroyed so we are too and we need to cancel selection
	LLFloaterColorPicker* pickerp = (LLFloaterColorPicker*)LLFloater::getFloaterByHandle(mPickerHandle);
	if (pickerp)
	{
		pickerp->cancelSelection();
		pickerp->close();
	}
	mAlphaGradientImage = NULL;
}

BOOL LLColorSwatchCtrl::handleDoubleClick(S32 x, S32 y, MASK mask)
{
	return handleMouseDown(x, y, mask);
}

BOOL LLColorSwatchCtrl::handleHover(S32 x, S32 y, MASK mask)
{
	getWindow()->setCursor(UI_CURSOR_HAND);
	return TRUE;
}

BOOL LLColorSwatchCtrl::handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent)
{
	if( getVisible() && mEnabled && !called_from_parent && ' ' == uni_char )
	{
		showPicker(TRUE);
	}
	return LLUICtrl::handleUnicodeCharHere(uni_char, called_from_parent);
}

// forces color of this swatch and any associated floater to the input value, if currently invalid
void LLColorSwatchCtrl::setOriginal(const LLColor4& color)
{
	mColor = color;
	LLFloaterColorPicker* pickerp = (LLFloaterColorPicker*)LLFloater::getFloaterByHandle(mPickerHandle);
	if (pickerp)
	{
		pickerp->setOrigRgb(mColor.mV[VRED], mColor.mV[VGREEN], mColor.mV[VBLUE]);
	}
}

void LLColorSwatchCtrl::set(const LLColor4& color, BOOL update_picker, BOOL from_event)
{
	mColor = color; 
	LLFloaterColorPicker* pickerp = (LLFloaterColorPicker*)LLFloater::getFloaterByHandle(mPickerHandle);
	if (pickerp && update_picker)
	{
		pickerp->setCurRgb(mColor.mV[VRED], mColor.mV[VGREEN], mColor.mV[VBLUE]);
	}
	if (!from_event)
	{
		setControlValue(mColor.getValue());
	}
}

void LLColorSwatchCtrl::setLabel(const std::string& label)
{
	mCaption->setText(label);
}

BOOL LLColorSwatchCtrl::handleMouseDown(S32 x, S32 y, MASK mask)
{
	// Route future Mouse messages here preemptively.  (Release on mouse up.)
	// No handler is needed for capture lost since this object has no state that depends on it.
	gViewerWindow->setMouseCapture( this, NULL );

	return TRUE;
}


BOOL LLColorSwatchCtrl::handleMouseUp(S32 x, S32 y, MASK mask)
{
	// We only handle the click if the click both started and ended within us
	if( gViewerWindow->hasMouseCapture( this ) )
	{
		// Release the mouse
		gViewerWindow->setMouseCapture( NULL, NULL );

		// If mouseup in the widget, it's been clicked
		if ( pointInView(x, y) )
		{
			llassert(mEnabled);
			llassert(getVisible());

			showPicker(FALSE);
		}
	}

	return TRUE;
}


// assumes GL state is set for 2D
void LLColorSwatchCtrl::draw()
{
	if( getVisible() )
	{
		mBorder->setKeyboardFocusHighlight(hasFocus());
		// Draw border
		LLRect border( 0, mRect.getHeight(), mRect.getWidth(), BTN_HEIGHT_SMALL );
		gl_rect_2d( border, mBorderColor, FALSE );

		LLRect interior = border;
		interior.stretch( -1 );

		// Check state
		if ( mValid )
		{
			LLGLSTexture gls_texture;

			// Draw the color swatch
			gl_rect_2d_checkerboard( interior );
			gl_rect_2d(interior, mColor, TRUE);
			LLColor4 opaque_color = mColor;
			opaque_color.mV[VALPHA] = 1.f;
			glColor4fv(opaque_color.mV);
			if (mAlphaGradientImage.notNull())
			{
				glPushMatrix();
				{
					glTranslatef((F32)interior.mLeft, (F32)interior.mBottom, 0.f);
					LLViewerImage::bindTexture(mAlphaGradientImage);
					gl_rect_2d_simple_tex(interior.getWidth(), interior.getHeight());
				}
				glPopMatrix();
			}
		}
		else
		{
			// Draw grey and an X
			gl_rect_2d(interior, LLColor4::grey, TRUE);

			gl_draw_x(interior, LLColor4::black);
		}

		LLUICtrl::draw();
	}
}

void LLColorSwatchCtrl::setEnabled( BOOL enabled )
{
	mCaption->setEnabled( enabled );
	LLUICtrl::setEnabled( enabled );

	if (!enabled)
	{
		LLFloaterColorPicker* pickerp = (LLFloaterColorPicker*)LLFloater::getFloaterByHandle(mPickerHandle);
		if (pickerp)
		{
			pickerp->cancelSelection();
			pickerp->close();
		}
	}
}


//////////////////////////////////////////////////////////////////////////////
// called when parent filters down a visibility changed message
void LLColorSwatchCtrl::onVisibilityChange ( BOOL curVisibilityIn )
{
	// visibility changed - moved away to different tab for instance - cancel selection 
	//if ( ! curVisibilityIn)
	//{
	//	LLFloaterColorPicker* pickerp = (LLFloaterColorPicker*)LLFloater::getFloaterByHandle(mPickerHandle);
	//	if (pickerp)
	//	{
	//		pickerp->cancelSelection();
	//	}
	//}
}

void LLColorSwatchCtrl::setValue(const LLSD& value)
{
	set(LLColor4(value), TRUE, TRUE);
}

//////////////////////////////////////////////////////////////////////////////
// called (infrequently) when the color changes so the subject of the swatch can be updated.
void LLColorSwatchCtrl::onColorChanged ( void* data, EColorPickOp pick_op )
{
	LLColorSwatchCtrl* subject = ( LLColorSwatchCtrl* )data;
	if ( subject )
	{
		LLFloaterColorPicker* pickerp = (LLFloaterColorPicker*)LLFloater::getFloaterByHandle(subject->mPickerHandle);
		if (pickerp)
		{
			// move color across from selector to internal widget storage
			LLColor4 updatedColor ( pickerp->getCurR (), 
									pickerp->getCurG (), 
									pickerp->getCurB (), 
									subject->mColor.mV[VALPHA] ); // keep current alpha
			subject->mColor = updatedColor;
			subject->setControlValue(updatedColor.getValue());

			if (pick_op == COLOR_CANCEL && subject->mOnCancelCallback)
			{
				subject->mOnCancelCallback(subject, subject->mCallbackUserData);
			}
			else if (pick_op == COLOR_SELECT && subject->mOnSelectCallback)
			{
				subject->mOnSelectCallback(subject, subject->mCallbackUserData);
			}
			else
			{
				// just commit change
				subject->onCommit ();
			}
		}
	};
}

void LLColorSwatchCtrl::setValid(BOOL valid )
{
	mValid = valid;

	LLFloaterColorPicker* pickerp = (LLFloaterColorPicker*)LLFloater::getFloaterByHandle(mPickerHandle);
	if (pickerp)
	{
		pickerp->setActive(valid);
	}
}

void LLColorSwatchCtrl::showPicker(BOOL take_focus)
{
	LLFloaterColorPicker* pickerp = (LLFloaterColorPicker*)LLFloater::getFloaterByHandle(mPickerHandle);
	if (!pickerp)
	{
		pickerp = new LLFloaterColorPicker(this, mCanApplyImmediately);
		gFloaterView->getParentFloater(this)->addDependentFloater(pickerp);
		mPickerHandle = pickerp->getHandle();
	}

	// initialize picker singleton with current color
	pickerp->initUI ( mColor.mV [ VRED ], mColor.mV [ VGREEN ], mColor.mV [ VBLUE ] );

	// display it
	pickerp->showUI ();

	if (take_focus)
	{
		pickerp->setFocus(TRUE);
	}
}

// virtual
LLXMLNodePtr LLColorSwatchCtrl::getXML(bool save_children) const
{
	LLXMLNodePtr node = LLUICtrl::getXML();

	node->createChild("color", TRUE)->setFloatValue(4, mColor.mV);

	node->createChild("border_color", TRUE)->setFloatValue(4, mBorderColor.mV);

	if (mCaption)
	{
		node->createChild("label", TRUE)->setStringValue(mCaption->getText());
	}

	node->createChild("can_apply_immediately", TRUE)->setBoolValue(mCanApplyImmediately);

	return node;
}

LLView* LLColorSwatchCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
{
	LLString name("colorswatch");
	node->getAttributeString("name", name);

	LLString label;
	node->getAttributeString("label", label);

	LLColor4 color(1.f, 1.f, 1.f, 1.f);
	node->getAttributeColor("initial_color", color);

	LLRect rect;
	createRect(node, rect, parent, LLRect());

	BOOL can_apply_immediately = FALSE;
	node->getAttributeBOOL("can_apply_immediately", can_apply_immediately);

	LLUICtrlCallback callback = NULL;

	if (label.empty())
	{
		label.assign(node->getValue());
	}

	LLColorSwatchCtrl* color_swatch = new LLColorSwatchCtrl(
		name, 
		rect,
		label,
		color,
		callback,
		NULL );

	color_swatch->setCanApplyImmediately(can_apply_immediately);
	color_swatch->initFromXML(node, parent);

	return color_swatch;
}
