/** 
 * @file llcontainerview.cpp
 * @brief Container for all statistics info
 *
 * $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$
 */

#include "linden_common.h"

#include "llcontainerview.h"

#include "llerror.h"
#include "llfontgl.h"
#include "llgl.h"
#include "llui.h"
#include "llstring.h"
#include "llscrollcontainer.h"
#include "lluictrlfactory.h"

static LLDefaultChildRegistry::Register<LLContainerView> r1("container_view");

#include "llpanel.h"
#include "llstatview.h"
static ContainerViewRegistry::Register<LLStatView> r2("stat_view");
static ContainerViewRegistry::Register<LLPanel> r3("panel", &LLPanel::fromXML);

LLContainerView::LLContainerView(const LLContainerView::Params& p)
:	LLView(p),
	mShowLabel(p.show_label),
	mLabel(p.label),
	mDisplayChildren(p.display_children)
{
	mCollapsible = TRUE;
	mScrollContainer = NULL;
}

LLContainerView::~LLContainerView()
{
	// Children all cleaned up by default view destructor.
}

BOOL LLContainerView::postBuild()
{
	setDisplayChildren(mDisplayChildren);
	reshape(getRect().getWidth(), getRect().getHeight(), FALSE);
	return TRUE;
}

bool LLContainerView::addChild(LLView* child, S32 tab_group)
{
	bool res = LLView::addChild(child, tab_group);
	if (res)
	{
		sendChildToBack(child);
	}
	return res;
}

BOOL LLContainerView::handleMouseDown(S32 x, S32 y, MASK mask)
{
	BOOL handled = FALSE;
	if (mDisplayChildren)
	{
		handled = (LLView::childrenHandleMouseDown(x, y, mask) != NULL);
	}
	if (!handled)
	{
		if( mCollapsible && mShowLabel && (y >= getRect().getHeight() - 10) )
		{
			setDisplayChildren(!mDisplayChildren);
			reshape(getRect().getWidth(), getRect().getHeight(), FALSE);
			handled = TRUE;
		}
	}
	return handled;
}

BOOL LLContainerView::handleMouseUp(S32 x, S32 y, MASK mask)
{
	BOOL handled = FALSE;
	if (mDisplayChildren)
	{
		handled = (LLView::childrenHandleMouseUp(x, y, mask) != NULL);
	}
	return handled;
}


void LLContainerView::draw()
{
	{
		gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);

		gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, LLColor4(0.f, 0.f, 0.f, 0.25f));
	}
		
	// Draw the label
	if (mShowLabel)
	{
		LLFontGL::getFontMonospace()->renderUTF8(
			mLabel, 0, 2, getRect().getHeight() - 2, LLColor4(1,1,1,1), LLFontGL::LEFT, LLFontGL::TOP);
	}

	LLView::draw();
}


void LLContainerView::reshape(S32 width, S32 height, BOOL called_from_parent)
{
	LLRect scroller_rect;
	scroller_rect.setOriginAndSize(0, 0, width, height);

	if (mScrollContainer)
	{
		scroller_rect = mScrollContainer->getContentWindowRect();
	}
	else
	{
		// if we're uncontained - make height as small as possible
		scroller_rect.mTop = 0;
	}

	arrange(scroller_rect.getWidth(), scroller_rect.getHeight(), called_from_parent);

	// sometimes, after layout, our container will change size (scrollbars popping in and out)
	// if so, attempt another layout
	if (mScrollContainer)
	{
		LLRect new_container_rect = mScrollContainer->getContentWindowRect();

		if ((new_container_rect.getWidth() != scroller_rect.getWidth()) ||
			(new_container_rect.getHeight() != scroller_rect.getHeight()))  // the container size has changed, attempt to arrange again
		{
			arrange(new_container_rect.getWidth(), new_container_rect.getHeight(), called_from_parent);
		}
	}
}

void LLContainerView::arrange(S32 width, S32 height, BOOL called_from_parent)
{
	// Determine the sizes and locations of all contained views
	S32 total_height = 0;
	S32 top, left, right, bottom;
	//LLView *childp;

	// These will be used for the children
	left = 4;
	top = getRect().getHeight() - 4;
	right = width - 2;
	bottom = top;
	
	// Leave some space for the top label/grab handle
	if (mShowLabel)
	{
		total_height += 20;
	}

	if (mDisplayChildren)
	{
		// Determine total height
		U32 child_height = 0;
		for (child_list_const_iter_t child_iter = getChildList()->begin();
			 child_iter != getChildList()->end(); ++child_iter)
		{
			LLView *childp = *child_iter;
			if (!childp->getVisible())
			{
				llwarns << "Incorrect visibility!" << llendl;
			}
			LLRect child_rect = childp->getRequiredRect();
			child_height += child_rect.getHeight();
			child_height += 2;
		}
		total_height += child_height;
	}

	if (total_height < height)
		total_height = height;
	
	if (followsTop())
	{
		// HACK: casting away const. Should use setRect or some helper function instead.
		const_cast<LLRect&>(getRect()).mBottom = getRect().mTop - total_height;
	}
	else
	{
		// HACK: casting away const. Should use setRect or some helper function instead.
		const_cast<LLRect&>(getRect()).mTop = getRect().mBottom + total_height;
	}
	// HACK: casting away const. Should use setRect or some helper function instead.
		const_cast<LLRect&>(getRect()).mRight = getRect().mLeft + width;

	top = total_height;
	if (mShowLabel)
		{
			top -= 20;
		}
	
	bottom = top;

	if (mDisplayChildren)
	{
		// Iterate through all children, and put in container from top down.
		for (child_list_const_iter_t child_iter = getChildList()->begin();
			 child_iter != getChildList()->end(); ++child_iter)
		{
			LLView *childp = *child_iter;
			LLRect child_rect = childp->getRequiredRect();
			bottom -= child_rect.getHeight();
			LLRect r(left, bottom + child_rect.getHeight(), right, bottom);
			childp->setRect(r);
			childp->reshape(right - left, top - bottom);
			top = bottom - 2;
			bottom = top;
		}
	}
	
	if (!called_from_parent)
	{
		if (getParent())
		{
			getParent()->reshape(getParent()->getRect().getWidth(), getParent()->getRect().getHeight(), FALSE);
		}
	}

}

LLRect LLContainerView::getRequiredRect()
{
	LLRect req_rect;
	//LLView *childp;
	U32 total_height = 0;
	
	// Determine the sizes and locations of all contained views

	// Leave some space for the top label/grab handle

	if (mShowLabel)
	{
		total_height = 20;
	}
		

	if (mDisplayChildren)
	{
		// Determine total height
		U32 child_height = 0;
		for (child_list_const_iter_t child_iter = getChildList()->begin();
			 child_iter != getChildList()->end(); ++child_iter)
		{
			LLView *childp = *child_iter;
			LLRect child_rect = childp->getRequiredRect();
			child_height += child_rect.getHeight();
			child_height += 2;
		}

		total_height += child_height;
	}
	req_rect.mTop = total_height;
	return req_rect;
}

void LLContainerView::setLabel(const std::string& label)
{
	mLabel = label;
}

void LLContainerView::setDisplayChildren(const BOOL displayChildren)
{
	mDisplayChildren = displayChildren;
	for (child_list_const_iter_t child_iter = getChildList()->begin();
		 child_iter != getChildList()->end(); ++child_iter)
	{
		LLView *childp = *child_iter;
		childp->setVisible(mDisplayChildren);
	}
}
