/** 
 * @file llsyswellwindow.cpp
 * @brief                                    // TODO
 * $LicenseInfo:firstyear=2000&license=viewergpl$
 * 
 * Copyright (c) 2000-2009, Linden Research, Inc.
 * 
 * Second Life Viewer Source Code
 * The source code in this file ("Source Code") is provided by Linden Lab
 * to you under the terms of the GNU General Public License, version 2.0
 * ("GPL"), unless you have obtained a separate licensing agreement
 * ("Other License"), formally executed by you and Linden Lab.  Terms of
 * the GPL can be found in doc/GPL-license.txt in this distribution, or
 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
 * 
 * There are special exceptions to the terms and conditions of the GPL as
 * it is applied to this Source Code. View the full text of the exception
 * in the file doc/FLOSS-exception.txt in this software distribution, or
 * online at
 * http://secondlifegrid.net/programs/open_source/licensing/flossexception
 * 
 * By copying, modifying or distributing this software, you acknowledge
 * that you have read and understood your obligations described above,
 * and agree to abide by those obligations.
 * 
 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
 * COMPLETENESS OR PERFORMANCE.
 * $/LicenseInfo$
 */


#include "llviewerprecompiledheaders.h" // must be first include

#include "llsyswellwindow.h"

#include "llbottomtray.h"
#include "llviewercontrol.h"
#include "llviewerwindow.h"

#include "llchiclet.h"
//---------------------------------------------------------------------------------
LLSysWellWindow::LLSysWellWindow(const LLSD& key) : LLDockableFloater(NULL, key),
													mChannel(NULL),
													mScrollContainer(NULL),
													mNotificationList(NULL)
{
	LLIMMgr::getInstance()->addSessionObserver(this);
	LLIMChiclet::sFindChicletsSignal.connect(boost::bind(&LLSysWellWindow::findIMChiclet, this, _1));
}

//---------------------------------------------------------------------------------
BOOL LLSysWellWindow::postBuild()
{
	mScrollContainer = getChild<LLScrollContainer>("notification_list_container");
	mTwinListPanel = getChild<LLPanel>("twin_list_panel");
	mNotificationList = getChild<LLScrollingPanelList>("notification_list");
	mIMRowList = getChild<LLScrollingPanelList>("im_row_panel_list");

	mScrollContainer->setBorderVisible(FALSE);

	return LLDockableFloater::postBuild();
}

//---------------------------------------------------------------------------------
LLSysWellWindow::~LLSysWellWindow()
{
	LLIMMgr::getInstance()->removeSessionObserver(this);
}

//---------------------------------------------------------------------------------
void LLSysWellWindow::addItem(LLSysWellItem::Params p)
{
	// do not add clones
	if( findItemByID(p.notification_id) >= 0 )
		return;

	LLSysWellItem* new_item = new LLSysWellItem(p);
	mNotificationList->addPanel(dynamic_cast<LLScrollingPanel*>(new_item));
	reshapeWindow();

	new_item->setOnItemCloseCallback(boost::bind(&LLSysWellWindow::onItemClose, this, _1));
	new_item->setOnItemClickCallback(boost::bind(&LLSysWellWindow::onItemClick, this, _1));
}

//---------------------------------------------------------------------------------
void LLSysWellWindow::clear()
{
 // *TODO: fill later
}

//---------------------------------------------------------------------------------
S32 LLSysWellWindow::findItemByID(const LLUUID& id)
{
	const LLScrollingPanelList::panel_list_t list = mNotificationList->getPanelList();
	if(list.size() == 0)
		return -1;

	LLScrollingPanelList::panel_list_t::const_iterator it; 
	S32 index = 0;
	for(it = list.begin(); it != list.end(); ++it, ++index)
	{
		if( dynamic_cast<LLSysWellItem*>(*it)->getID() == id )
			break;
	}

	if(it == list.end())
		return -1;
	else
		return index;

}

//---------------------------------------------------------------------------------
void LLSysWellWindow::removeItemByID(const LLUUID& id)
{
	S32 index = findItemByID(id);

	if(index >= 0)
		mNotificationList->removePanel(index);
	else
		return;

	reshapeWindow();
}

//---------------------------------------------------------------------------------
void LLSysWellWindow::onItemClick(LLSysWellItem* item)
{
	LLUUID id = item->getID();
	if(mChannel)
		mChannel->loadStoredToastByIDToChannel(id);
}

//---------------------------------------------------------------------------------
void LLSysWellWindow::onItemClose(LLSysWellItem* item)
{
	LLUUID id = item->getID();
	removeItemByID(id);
	if(mChannel)
		mChannel->killToastByNotificationID(id);

	// hide chiclet window if there are no items left
	setVisible(!isWindowEmpty());
}

//---------------------------------------------------------------------------------
void LLSysWellWindow::toggleWindow()
{
	if (getDockControl() == NULL)
	{
		setDockControl(new LLDockControl(
				LLBottomTray::getInstance()->getSysWell(), this,
				getDockTongue(), LLDockControl::TOP, isDocked()));
	}
	setVisible(!getVisible());
	//set window in foreground
	setFocus(getVisible());
}

//---------------------------------------------------------------------------------
void LLSysWellWindow::setVisible(BOOL visible)
{
	// on Show adjust position of SysWell chiclet's window
	if(visible)
	{
		if (LLBottomTray::instanceExists())
		{
			LLBottomTray::getInstance()->getSysWell()->setToggleState(TRUE);
		}
		if(mChannel)
			mChannel->removeAndStoreAllVisibleToasts();
	}
	else
	{
		if (LLBottomTray::instanceExists())
		{
			LLBottomTray::getInstance()->getSysWell()->setToggleState(FALSE);
		}
	}
	if(mChannel)
		mChannel->setShowToasts(!visible);

	LLDockableFloater::setVisible(visible);
}

//---------------------------------------------------------------------------------
void LLSysWellWindow::reshapeWindow()
{
	// Get size for scrollbar and floater's header
	const LLUICachedControl<S32> SCROLLBAR_SIZE("UIScrollbarSize", 0);
	const LLUICachedControl<S32> HEADER_SIZE("UIFloaterHeaderSize", 0);

	LLRect notif_list_rect = mNotificationList->getRect();
	LLRect im_list_rect = mIMRowList->getRect();
	LLRect panel_rect = mTwinListPanel->getRect();

	S32 notif_list_height = notif_list_rect.getHeight();
	S32 im_list_height = im_list_rect.getHeight();

	S32 new_panel_height = notif_list_height + LLScrollingPanelList::GAP_BETWEEN_PANELS + im_list_height;
	S32 new_window_height = new_panel_height + LLScrollingPanelList::GAP_BETWEEN_PANELS + HEADER_SIZE;

	U32 twinListWidth = 0;

	if (new_window_height > MAX_WINDOW_HEIGHT)
	{
		twinListWidth = MIN_PANELLIST_WIDTH - SCROLLBAR_SIZE;
		new_window_height = MAX_WINDOW_HEIGHT;
	}
	else
	{
		twinListWidth = MIN_PANELLIST_WIDTH;
	}

	reshape(MIN_WINDOW_WIDTH, new_window_height, FALSE);
	mTwinListPanel->reshape(twinListWidth, new_panel_height, TRUE);
	mNotificationList->reshape(twinListWidth, notif_list_height, TRUE);
	mIMRowList->reshape(twinListWidth, im_list_height, TRUE);

	// arrange panel and lists
	// move panel
	panel_rect.setLeftTopAndSize(1, new_panel_height, twinListWidth, new_panel_height);
	mTwinListPanel->setRect(panel_rect);
	// move notif list panel
	notif_list_rect.setLeftTopAndSize(notif_list_rect.mLeft, new_panel_height, twinListWidth, notif_list_height);
	mNotificationList->setRect(notif_list_rect);
	// move IM list panel
	im_list_rect.setLeftTopAndSize(im_list_rect.mLeft, notif_list_rect.mBottom - LLScrollingPanelList::GAP_BETWEEN_PANELS, twinListWidth, im_list_height);
	mIMRowList->setRect(im_list_rect);

	mNotificationList->updatePanels(TRUE);
	mIMRowList->updatePanels(TRUE);
}

//---------------------------------------------------------------------------------
LLSysWellWindow::RowPanel * LLSysWellWindow::findIMRow(const LLUUID& sessionId)
{
	RowPanel * res = NULL;
	const LLScrollingPanelList::panel_list_t &list = mIMRowList->getPanelList();
	if (!list.empty())
	{
		for (LLScrollingPanelList::panel_list_t::const_iterator iter = list.begin(); iter != list.end(); ++iter)
		{
			RowPanel *panel = static_cast<RowPanel*> (*iter);
			if (panel->mChiclet->getSessionId() == sessionId)
			{
				res = panel;
				break;
			}
		}
	}
	return res;
}

//---------------------------------------------------------------------------------
LLChiclet* LLSysWellWindow::findIMChiclet(const LLUUID& sessionId)
{
	LLChiclet* res = NULL;
	RowPanel* panel = findIMRow(sessionId);
	if (panel != NULL)
	{
		res = panel->mChiclet;
	}

	return res;
}

//---------------------------------------------------------------------------------
void LLSysWellWindow::addIMRow(const LLUUID& sessionId, S32 chicletCounter,
		const std::string& name, const LLUUID& otherParticipantId)
{

	mIMRowList->addPanel(new RowPanel(this, sessionId, chicletCounter, name, otherParticipantId));
}

//---------------------------------------------------------------------------------
void LLSysWellWindow::delIMRow(const LLUUID& sessionId)
{
	RowPanel *panel = findIMRow(sessionId);
	if (panel != NULL)
	{
		mIMRowList->removePanel(panel);
	}

	// hide chiclet window if there are no items left
	setVisible(!isWindowEmpty());
}

//---------------------------------------------------------------------------------
bool LLSysWellWindow::isWindowEmpty()
{
	if(mIMRowList->getPanelList().size() == 0 && LLBottomTray::getInstance()->getSysWell()->getCounter() == 0)
	{
		return true;
	}
	else
	{
		return false;
	}
}

//---------------------------------------------------------------------------------
//virtual
void LLSysWellWindow::sessionAdded(const LLUUID& sessionId,
		const std::string& name, const LLUUID& otherParticipantId)
{
	if (findIMRow(sessionId) == NULL)
	{
		S32 chicletCounter = 0;
		LLIMModel::LLIMSession* session = get_if_there(LLIMModel::sSessionsMap,
				sessionId, (LLIMModel::LLIMSession*) NULL);
		if (session != NULL)
		{
			chicletCounter = session->mNumUnread;
		}
		addIMRow(sessionId, chicletCounter, name, otherParticipantId);
		reshapeWindow();
	}
}

//---------------------------------------------------------------------------------
//virtual
void LLSysWellWindow::sessionRemoved(const LLUUID& sessionId)
{
	delIMRow(sessionId);
	reshapeWindow();
	LLBottomTray::getInstance()->getSysWell()->updateUreadIMNotifications();
}

//---------------------------------------------------------------------------------
LLSysWellWindow::RowPanel::RowPanel(const LLSysWellWindow* parent, const LLUUID& sessionId,
		S32 chicletCounter, const std::string& name, const LLUUID& otherParticipantId) :
		LLScrollingPanel(LLPanel::Params()), mChiclet(NULL), mParent(parent)
{
	LLUICtrlFactory::getInstance()->buildPanel(this, "panel_activeim_row.xml", NULL);

	// Choose which of the pre-created chiclets (IM/group) to use.
	// The other one gets hidden.

	LLIMChiclet::EType im_chiclet_type = LLIMChiclet::getIMSessionType(sessionId);
	switch (im_chiclet_type)
	{
	case LLIMChiclet::TYPE_GROUP:
		mChiclet = getChild<LLIMChiclet>("group_chiclet");
		childSetVisible("p2p_chiclet", false);
		break;
	case LLIMChiclet::TYPE_UNKNOWN: // assign mChiclet a non-null value anyway
	case LLIMChiclet::TYPE_IM:
		mChiclet = getChild<LLIMChiclet>("p2p_chiclet");
		childSetVisible("group_chiclet", false);
		break;
	}

	// Initialize chiclet.
	mChiclet->setCounter(chicletCounter);
	mChiclet->setSessionId(sessionId);
	mChiclet->setIMSessionName(name);
	mChiclet->setOtherParticipantId(otherParticipantId);

	LLTextBox* contactName = getChild<LLTextBox>("contact_name");
	contactName->setValue(name);

	mCloseBtn = getChild<LLButton>("hide_btn");
	mCloseBtn->setCommitCallback(boost::bind(&LLSysWellWindow::RowPanel::onClose, this));
}

//---------------------------------------------------------------------------------
LLSysWellWindow::RowPanel::~RowPanel()
{
}

//---------------------------------------------------------------------------------
void LLSysWellWindow::RowPanel::onClose()
{
	mParent->mIMRowList->removePanel(this);
	gIMMgr->removeSession(mChiclet->getSessionId());
}

//---------------------------------------------------------------------------------
void LLSysWellWindow::RowPanel::onMouseEnter(S32 x, S32 y, MASK mask)
{
	setTransparentColor(LLUIColorTable::instance().getColor("SysWellItemSelected"));
}

//---------------------------------------------------------------------------------
void LLSysWellWindow::RowPanel::onMouseLeave(S32 x, S32 y, MASK mask)
{
	setTransparentColor(LLUIColorTable::instance().getColor("SysWellItemUnselected"));
}

//---------------------------------------------------------------------------------
// virtual
BOOL LLSysWellWindow::RowPanel::handleMouseDown(S32 x, S32 y, MASK mask)
{
	// Pass the mouse down event to the chiclet (EXT-596).
	if (!mChiclet->pointInView(x, y) && !mCloseBtn->getRect().pointInRect(x, y)) // prevent double call of LLIMChiclet::onMouseDown()
		mChiclet->onMouseDown();

	return LLPanel::handleMouseDown(x, y, mask);
}

//---------------------------------------------------------------------------------
void LLSysWellWindow::RowPanel::updatePanel(BOOL allow_modify)
{
	S32 parent_width = getParent()->getRect().getWidth();
	S32 panel_height = getRect().getHeight();

	reshape(parent_width, panel_height, TRUE);
}

//---------------------------------------------------------------------------------
