/** 
 * @file llbadge.cpp
 * @brief Implementation for badges
 *
 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 * 
 * 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#define LLBADGE_CPP
#include "llbadge.h"

#include "llscrollcontainer.h"
#include "lluictrlfactory.h"


static LLDefaultChildRegistry::Register<LLBadge> r("badge");

static const S32 BADGE_OFFSET_NOT_SPECIFIED = 0x7FFFFFFF;

// Compiler optimization, generate extern template
template class LLBadge* LLView::getChild<class LLBadge>(const std::string& name, BOOL recurse) const;


LLBadge::Params::Params()
	: border_image("border_image")
	, border_color("border_color")
	, image("image")
	, image_color("image_color")
	, label("label")
	, label_color("label_color")
	, label_offset_horiz("label_offset_horiz")
	, label_offset_vert("label_offset_vert")
	, location("location", LLRelPos::TOP_LEFT)
	, location_offset_hcenter("location_offset_hcenter")
	, location_offset_vcenter("location_offset_vcenter")
	, location_percent_hcenter("location_percent_hcenter")
	, location_percent_vcenter("location_percent_vcenter")
	, padding_horiz("padding_horiz")
	, padding_vert("padding_vert")
{}

bool LLBadge::Params::equals(const Params& a) const
{
	bool comp = true;
	
	// skip owner in comparison on purpose
	
	comp &= (border_image() == a.border_image());
	comp &= (border_color() == a.border_color());
	comp &= (image() == a.image());
	comp &= (image_color() == a.image_color());
	comp &= (label() == a.label());
	comp &= (label_color() == a.label_color());
	comp &= (label_offset_horiz() == a.label_offset_horiz());
	comp &= (label_offset_vert() == a.label_offset_vert());
	comp &= (location() == a.location());
	comp &= (location_offset_hcenter() == a.location_offset_hcenter());
	comp &= (location_offset_vcenter() == a.location_offset_vcenter());
	comp &= (location_percent_hcenter() == a.location_percent_hcenter());
	comp &= (location_percent_vcenter() == a.location_percent_vcenter());
	comp &= (padding_horiz() == a.padding_horiz());
	comp &= (padding_vert() == a.padding_vert());
	
	return comp;
}

LLBadge::LLBadge(const LLBadge::Params& p)
	: LLUICtrl(p)
	, mBorderImage(p.border_image)
	, mBorderColor(p.border_color)
	, mGLFont(p.font)
	, mImage(p.image)
	, mImageColor(p.image_color)
	, mLabel(p.label)
	, mLabelColor(p.label_color)
	, mLabelOffsetHoriz(p.label_offset_horiz)
	, mLabelOffsetVert(p.label_offset_vert)
	, mLocation(p.location)
	, mLocationOffsetHCenter(BADGE_OFFSET_NOT_SPECIFIED)
	, mLocationOffsetVCenter(BADGE_OFFSET_NOT_SPECIFIED)
	, mLocationPercentHCenter(0.5f)
	, mLocationPercentVCenter(0.5f)
	, mOwner(p.owner)
	, mPaddingHoriz(p.padding_horiz)
	, mPaddingVert(p.padding_vert)
	, mParentScroller(nullptr)
	, mDrawAtParentTop(false)
{
	if (mImage.isNull())
	{
		LL_WARNS() << "Badge: " << getName() << " with no image!" << LL_ENDL;
	}

	if (p.location_offset_hcenter.isProvided())
	{
		mLocationOffsetHCenter = p.location_offset_hcenter();
	}

	if (p.location_offset_vcenter.isProvided())
	{
		mLocationOffsetVCenter = p.location_offset_vcenter();
	}

	//
	// The following logic is to set the mLocationPercentHCenter and mLocationPercentVCenter
	// based on the Location enum and our horizontal and vertical location percentages.  The
	// draw code then uses this on the owner rectangle to compute the screen location for
	// the badge.
	//

	if (!LLRelPos::IsCenter(mLocation))
	{
		F32 h_center = p.location_percent_hcenter * 0.01f;
		F32 v_center = p.location_percent_vcenter * 0.01f;

		if (LLRelPos::IsRight(mLocation))
		{
			mLocationPercentHCenter = 0.5f * (1.0f + h_center);
		}
		else if (LLRelPos::IsLeft(mLocation))
		{
			mLocationPercentHCenter = 0.5f * (1.0f - h_center);
		}
			
		if (LLRelPos::IsTop(mLocation))
		{
			mLocationPercentVCenter = 0.5f * (1.0f + v_center);
		}
		else if (LLRelPos::IsBottom(mLocation))
		{
			mLocationPercentVCenter = 0.5f * (1.0f - v_center);
		}
	}
}

LLBadge::~LLBadge()
{
}

bool LLBadge::addToView(LLView * view)
{
	bool child_added = view->addChild(this);

	if (child_added)
	{
		setShape(view->getLocalRect());

		// Find a parent scroll container, if there is one in case we need it for positioning

		LLView * parent = mOwner.get();

		while ((parent != nullptr) && ((mParentScroller = dynamic_cast<LLScrollContainer *>(parent)) == nullptr))
		{
			parent = parent->getParent();
		}
	}

	return child_added;
}

void LLBadge::setLabel(const LLStringExplicit& label)
{
	mLabel = label;
}

//
// This is a fallback function to render a rectangle for badges without a valid image
//
void renderBadgeBackground(F32 centerX, F32 centerY, F32 width, F32 height, const LLColor4U &color)
{
	gGL.pushUIMatrix();
	gGL.loadUIIdentity();
	gGL.setSceneBlendType(LLRender::BT_REPLACE);
	gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
	
	gGL.color4ubv(color.mV);
	gGL.texCoord2i(0, 0);
	
	F32 x = LLFontGL::sCurOrigin.mX + centerX - width * 0.5f;
	F32 y = LLFontGL::sCurOrigin.mY + centerY - height * 0.5f;
	
	LLRectf screen_rect(ll_round(x),
						ll_round(y),
						ll_round(x) + width,
						ll_round(y) + height);
	
	LLVector4a vertices[6];
	vertices[0].set(screen_rect.mLeft, screen_rect.mTop, 1.0f);
	vertices[1].set(screen_rect.mLeft, screen_rect.mBottom, 1.0f);
	vertices[2].set(screen_rect.mRight, screen_rect.mTop, 1.0f);
	vertices[3].set(screen_rect.mRight, screen_rect.mTop, 1.0f);
	vertices[4].set(screen_rect.mLeft, screen_rect.mBottom, 1.0f);
	vertices[5].set(screen_rect.mRight, screen_rect.mBottom, 1.0f);
	
	gGL.begin(LLRender::TRIANGLES);
	{
		gGL.vertexBatchPreTransformed(vertices, 6);
	}
	gGL.end();
	
	gGL.setSceneBlendType(LLRender::BT_ALPHA); // <alchemy/>

	gGL.popUIMatrix();
}


// virtual
void LLBadge::draw()
{
	if (!mLabel.empty())
	{
		LLView* owner_view = mOwner.get();

		if (owner_view && owner_view->isInVisibleChain())
		{
			//
			// Calculate badge size based on label text
			//

			LLWString badge_label_wstring = mLabel;

			S32 badge_label_begin_offset = 0;
			S32 badge_char_length = S32_MAX;
			S32 badge_pixel_length = S32_MAX;
			F32 *right_position_out = nullptr;
			BOOL do_not_use_ellipses = false;

			F32 badge_width = (2.0f * mPaddingHoriz) +
				mGLFont->getWidthF32(badge_label_wstring.c_str(), badge_label_begin_offset, badge_char_length);

			F32 badge_height = (2.0f * mPaddingVert) + mGLFont->getLineHeight();

			//
			// Calculate badge position based on owner
			//
			
			LLRect owner_rect;
			owner_view->localRectToOtherView(owner_view->getLocalRect(), & owner_rect, this);

			S32 location_offset_horiz = mLocationOffsetHCenter;
			S32 location_offset_vert = mLocationOffsetVCenter;

			// If we're in a scroll container, do some math to keep us in the same place on screen if applicable
			if (mParentScroller != nullptr)
			{
				LLRect visibleRect = mParentScroller->getVisibleContentRect();

				if (mLocationOffsetHCenter != BADGE_OFFSET_NOT_SPECIFIED)
				{
					if (LLRelPos::IsRight(mLocation))
					{
						location_offset_horiz += visibleRect.mRight;
					}
					else if (LLRelPos::IsLeft(mLocation))
					{
						location_offset_horiz += visibleRect.mLeft;
					}
					else // center
					{
						location_offset_horiz += (visibleRect.mLeft + visibleRect.mRight) / 2;
					}
				}

				if (mLocationOffsetVCenter != BADGE_OFFSET_NOT_SPECIFIED)
				{
					if (LLRelPos::IsTop(mLocation))
					{
						location_offset_vert += visibleRect.mTop;
					}
					else if (LLRelPos::IsBottom(mLocation))
					{
						location_offset_vert += visibleRect.mBottom;
					}
					else // center
					{
						location_offset_vert += (visibleRect.mBottom + visibleRect.mTop) / 2;
					}
				}
			}
			
			F32 badge_center_x;
			F32 badge_center_y;

			// Compute x position
			if (mLocationOffsetHCenter == BADGE_OFFSET_NOT_SPECIFIED)
			{
				badge_center_x = owner_rect.mLeft + owner_rect.getWidth() * mLocationPercentHCenter;
			}
			else
			{
				badge_center_x = location_offset_horiz;
			}

			// Compute y position
			if (mLocationOffsetVCenter == BADGE_OFFSET_NOT_SPECIFIED)
			{
				if(mDrawAtParentTop)
				{
					badge_center_y = owner_rect.mTop - badge_height * 0.5f - 1;
				}
				else
				{
					badge_center_y = owner_rect.mBottom + owner_rect.getHeight() * mLocationPercentVCenter;
				}
			}
			else
			{
				badge_center_y = location_offset_vert;
			}

			//
			// Draw button image, if available.
			// Otherwise draw basic rectangular button.
			//

			F32 alpha = getDrawContext().mAlpha;

			if (!mImage.isNull())
			{
				F32 badge_x = badge_center_x - badge_width * 0.5f;
				F32 badge_y = badge_center_y - badge_height * 0.5f;
			
				mImage->drawSolid((S32) badge_x, (S32) badge_y, (S32) badge_width, (S32) badge_height, mImageColor % alpha);

				if (!mBorderImage.isNull())
				{
					mBorderImage->drawSolid((S32) badge_x, (S32) badge_y, (S32) badge_width, (S32) badge_height, mBorderColor % alpha);
				}
			}
			else
			{
				LL_DEBUGS() << "No image for badge " << getName() << " on owner " << owner_view->getName() << LL_ENDL;
				
				renderBadgeBackground(badge_center_x, badge_center_y,
									  badge_width, badge_height,
									  mImageColor % alpha);
			}

			//
			// Draw the label
			//

			mGLFont->render(badge_label_wstring,
							badge_label_begin_offset,
							badge_center_x + mLabelOffsetHoriz,
							badge_center_y + mLabelOffsetVert,
							mLabelColor % alpha,
							LLFontGL::HCENTER, LLFontGL::VCENTER, // centered around the position
							LLFontGL::NORMAL, // normal text (not bold, italics, etc.)
							LLFontGL::DROP_SHADOW,
							badge_char_length, badge_pixel_length,
							right_position_out, do_not_use_ellipses);
		}
	}
}


namespace LLInitParam
{
	void TypeValues<LLRelPos::Location>::declareValues()
	{
		declare("bottom",		LLRelPos::BOTTOM);
		declare("bottom_left",	LLRelPos::BOTTOM_LEFT);
		declare("bottom_right", LLRelPos::BOTTOM_RIGHT);
		declare("center",		LLRelPos::CENTER);
		declare("left",			LLRelPos::LEFT);
		declare("right",		LLRelPos::RIGHT);
		declare("top",			LLRelPos::TOP);
		declare("top_left",		LLRelPos::TOP_LEFT);
		declare("top_right",	LLRelPos::TOP_RIGHT);
	}
}


// eof
