/** 
 * @file llfloatergroups.cpp
 * @brief LLPanelGroups class implementation
 *
 * $LicenseInfo:firstyear=2002&license=viewergpl$
 * 
 * Copyright (c) 2002-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$
 */

/*
 * Shown from Edit -> Groups...
 * Shows the agent's groups and allows the edit window to be invoked.
 * Also overloaded to allow picking of a single group for assigning 
 * objects and land to groups.
 */

#include "llviewerprecompiledheaders.h"

#include "llfloatergroups.h"
#include "llfloatergroupinvite.h"

#include "message.h"
#include "roles_constants.h"

#include "hbfloatergrouptitles.h"
#include "llagent.h"
#include "llbutton.h"
#include "llfloatergroupinfo.h"
#include "llfloaterdirectory.h"
#include "llfocusmgr.h"
#include "llalertdialog.h"
#include "llselectmgr.h"
#include "llscrolllistctrl.h"
#include "lltextbox.h"
#include "lluictrlfactory.h"
#include "llviewerwindow.h"
#include "llimview.h"

#include "hippolimits.h"

// static
std::map<const LLUUID, LLFloaterGroupPicker*> LLFloaterGroupPicker::sInstances;

// helper functions
void init_group_list(LLScrollListCtrl* ctrl, const LLUUID& highlight_id, const std::string& none_text, bool group_picker, U64 powers_mask = GP_ALL_POWERS);

///----------------------------------------------------------------------------
/// Class LLFloaterGroupPicker
///----------------------------------------------------------------------------

// static
LLFloaterGroupPicker* LLFloaterGroupPicker::findInstance(const LLSD& seed)
{
	instance_map_t::iterator found_it = sInstances.find(seed.asUUID());
	if (found_it != sInstances.end())
	{
		return found_it->second;
	}
	return NULL;
}

// static
LLFloaterGroupPicker* LLFloaterGroupPicker::createInstance(const LLSD &seed)
{
	LLFloaterGroupPicker* pickerp = new LLFloaterGroupPicker(seed);
	LLUICtrlFactory::getInstance()->buildFloater(pickerp, "floater_choose_group.xml");
	return pickerp;
}

LLFloaterGroupPicker::LLFloaterGroupPicker(const LLSD& seed) : 
	mSelectCallback(NULL),
	mCallbackUserdata(NULL),
	mPowersMask(GP_ALL_POWERS)
{
	mID = seed.asUUID();
	sInstances.insert(std::make_pair(mID, this));
}

LLFloaterGroupPicker::~LLFloaterGroupPicker()
{
	sInstances.erase(mID);
}

void LLFloaterGroupPicker::setSelectCallback(void (*callback)(LLUUID, void*), 
									void* userdata)
{
	mSelectCallback = callback;
	mCallbackUserdata = userdata;
}

void LLFloaterGroupPicker::setPowersMask(U64 powers_mask)
{
	mPowersMask = powers_mask;
	postBuild();
}


BOOL LLFloaterGroupPicker::postBuild()
{
	const std::string none_text = getString("none");
	init_group_list(getChild<LLScrollListCtrl>("group list"), gAgent.getGroupID(), none_text, true, mPowersMask);

	childSetAction("OK", onBtnOK, this);

	childSetAction("Cancel", onBtnCancel, this);

	setDefaultBtn("OK");

	childSetDoubleClickCallback("group list", onBtnOK);
	childSetUserData("group list", this);

	childEnable("OK");

	return TRUE;
}

void LLFloaterGroupPicker::onBtnOK(void* userdata)
{
	LLFloaterGroupPicker* self = (LLFloaterGroupPicker*)userdata;
	if(self) self->ok();
}

void LLFloaterGroupPicker::onBtnCancel(void* userdata)
{
	LLFloaterGroupPicker* self = (LLFloaterGroupPicker*)userdata;
	if(self) self->close();
}


void LLFloaterGroupPicker::ok()
{
	LLCtrlListInterface *group_list = childGetListInterface("group list");
	LLUUID group_id;
	if (group_list)
	{
		group_id = group_list->getCurrentID();
	}
	if(mSelectCallback)
	{
		mSelectCallback(group_id, mCallbackUserdata);
	}

	close();
}

///----------------------------------------------------------------------------
/// Class LLPanelGroups
///----------------------------------------------------------------------------

//LLEventListener
//virtual
bool LLPanelGroups::handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
{
	if (event->desc() == "new group")
	{
		reset();
		return true;
	}
	return false;
}

// Default constructor
LLPanelGroups::LLPanelGroups() :
	LLPanel()
{
	gAgent.addListener(this, "new group");
}

LLPanelGroups::~LLPanelGroups()
{
	gAgent.removeListener(this);
}

// clear the group list, and get a fresh set of info.
void LLPanelGroups::reset()
{
	LLCtrlListInterface *group_list = childGetListInterface("group list");
	if (group_list)
	{
		group_list->operateOnAll(LLCtrlListInterface::OP_DELETE);
	}
	childSetTextArg("groupcount", "[COUNT]", llformat("%d",gAgent.mGroups.count()));
	childSetTextArg("groupcount", "[MAX]", llformat("%d", gHippoLimits->getMaxAgentGroups()));

	const std::string none_text = getString("none");
	init_group_list(getChild<LLScrollListCtrl>("group list"), gAgent.getGroupID(), none_text, false);
	enableButtons();
}

BOOL LLPanelGroups::postBuild()
{
	childSetCommitCallback("group list", onGroupList, this);

	LLSearchEditor* group_search = getChild<LLSearchEditor>("group_search");
	if (group_search)
	{
		group_search->setSearchCallback(&onGroupSearchKeystroke, this);
	}

	childSetTextArg("groupcount", "[COUNT]", llformat("%d",gAgent.mGroups.count()));
	childSetTextArg("groupcount", "[MAX]", llformat("%d", gHippoLimits->getMaxAgentGroups()));

	const std::string none_text = getString("none");
	init_group_list(getChild<LLScrollListCtrl>("group list"), gAgent.getGroupID(), none_text, false);

	childSetAction("Activate", onBtnActivate, this);

	childSetAction("Info", onBtnInfo, this);

	childSetAction("IM", onBtnIM, this);

	childSetAction("Leave", onBtnLeave, this);

	childSetAction("Create", onBtnCreate, this);

	childSetAction("Search...", onBtnSearch, this);
	
	childSetAction("Invite...", onBtnInvite, this);

	childSetAction("Titles...", onBtnTitles, this);

	setDefaultBtn("IM");

	childSetDoubleClickCallback("group list", onBtnIM);
	childSetUserData("group list", this);

	reset();

	return TRUE;
}

void LLPanelGroups::enableButtons()
{
	LLCtrlListInterface *group_list = childGetListInterface("group list");
	LLUUID group_id;
	if (group_list)
	{
		group_id = group_list->getCurrentID();
	}

	if(group_id != gAgent.getGroupID())
	{
		childEnable("Activate");
	}
	else
	{
		childDisable("Activate");
	}
	if (group_id.notNull())
	{
		childEnable("Info");
		childEnable("IM");
		childEnable("Leave");
	}
	else
	{
		childDisable("Info");
		childDisable("IM");
		childDisable("Leave");
	}
	if(gAgent.mGroups.count() < gHippoLimits->getMaxAgentGroups())
	{
		childEnable("Create");
	}
	else
	{
		childDisable("Create");
	}
	if (group_id.notNull() && gAgent.hasPowerInGroup(group_id, GP_MEMBER_INVITE))
	{
		LLPanelGroups::childEnable("Invite...");
	}
	else
	{
		LLPanelGroups::childDisable("Invite...");
	}
}


void LLPanelGroups::onBtnCreate(void* userdata)
{
	LLPanelGroups* self = (LLPanelGroups*)userdata;
	if(self) self->create();
}

void LLPanelGroups::onBtnInvite(void* userdata)
{
	LLPanelGroups* self = (LLPanelGroups*)userdata;
	if(self) self->invite();
}

void LLPanelGroups::onBtnActivate(void* userdata)
{
	LLPanelGroups* self = (LLPanelGroups*)userdata;
	if(self) self->activate();
}

void LLPanelGroups::onBtnInfo(void* userdata)
{
	LLPanelGroups* self = (LLPanelGroups*)userdata;
	if(self) self->info();
}

void LLPanelGroups::onBtnIM(void* userdata)
{
	LLPanelGroups* self = (LLPanelGroups*)userdata;
	if(self) self->startIM();
}

void LLPanelGroups::onBtnLeave(void* userdata)
{
	LLPanelGroups* self = (LLPanelGroups*)userdata;
	if(self) self->leave();
}

void LLPanelGroups::onBtnSearch(void* userdata)
{
	LLPanelGroups* self = (LLPanelGroups*)userdata;
	if(self) self->search();
}

void LLPanelGroups::onBtnTitles(void* userdata)
{
	LLPanelGroups* self = (LLPanelGroups*)userdata;
	if(self) self->titles();
}

void LLPanelGroups::create()
{
	//llinfos << "LLPanelGroups::create" << llendl;
	LLFloaterGroupInfo::showCreateGroup(NULL);
}

void LLPanelGroups::activate()
{
	//llinfos << "LLPanelGroups::activate" << llendl;
	LLCtrlListInterface *group_list = childGetListInterface("group list");
	LLUUID group_id;
	if (group_list)
	{
		group_id = group_list->getCurrentID();
	}
	LLMessageSystem* msg = gMessageSystem;
	msg->newMessageFast(_PREHASH_ActivateGroup);
	msg->nextBlockFast(_PREHASH_AgentData);
	msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
	msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
	msg->addUUIDFast(_PREHASH_GroupID, group_id);
	gAgent.sendReliableMessage();
}

void LLPanelGroups::info()
{
	//llinfos << "LLPanelGroups::info" << llendl;
	LLCtrlListInterface *group_list = childGetListInterface("group list");
	LLUUID group_id;
	if (group_list && (group_id = group_list->getCurrentID()).notNull())
	{
		LLFloaterGroupInfo::showFromUUID(group_id);
	}
}

void LLPanelGroups::startIM()
{
	//llinfos << "LLPanelGroups::onClickIM()" << llendl;
	LLCtrlListInterface *group_list = childGetListInterface("group list");
	LLUUID group_id;

	if (group_list && (group_id = group_list->getCurrentID()).notNull())
	{
		LLGroupData group_data;
		if (gAgent.getGroupData(group_id, group_data))
		{
			gIMMgr->setFloaterOpen(TRUE);
			gIMMgr->addSession(
				group_data.mName,
				IM_SESSION_GROUP_START,
				group_id);
			make_ui_sound("UISndStartIM");
		}
		else
		{
			// this should never happen, as starting a group IM session
			// relies on you belonging to the group and hence having the group data
			make_ui_sound("UISndInvalidOp");
		}
	}
}

void LLPanelGroups::leave()
{
	//llinfos << "LLPanelGroups::leave" << llendl;
	LLCtrlListInterface *group_list = childGetListInterface("group list");
	LLUUID group_id;
	if (group_list && (group_id = group_list->getCurrentID()).notNull())
	{
		S32 count = gAgent.mGroups.count();
		S32 i;
		for(i = 0; i < count; ++i)
		{
			if(gAgent.mGroups.get(i).mID == group_id)
				break;
		}
		if(i < count)
		{
			LLSD args;
			args["GROUP"] = gAgent.mGroups.get(i).mName;
			LLSD payload;
			payload["group_id"] = group_id;
			LLNotifications::instance().add("GroupLeaveConfirmMember", args, payload, callbackLeaveGroup);
		}
	}
}

void LLPanelGroups::search()
{
	LLFloaterDirectory::showGroups();
}

void LLPanelGroups::invite()
{
	LLCtrlListInterface *group_list = childGetListInterface("group list");
	LLUUID group_id;

	//if (group_list && (group_id = group_list->getCurrentID()).notNull())
	
	if (group_list)
	{
		group_id = group_list->getCurrentID();
	}

	LLFloaterGroupInvite::showForGroup(group_id);
}

void LLPanelGroups::titles()
{
	HBFloaterGroupTitles::toggle();
}


// static
bool LLPanelGroups::callbackLeaveGroup(const LLSD& notification, const LLSD& response)
{
	S32 option = LLNotification::getSelectedOption(notification, response);
	LLUUID group_id = notification["payload"]["group_id"].asUUID();
	if(option == 0)
	{
		LLMessageSystem* msg = gMessageSystem;
		msg->newMessageFast(_PREHASH_LeaveGroupRequest);
		msg->nextBlockFast(_PREHASH_AgentData);
		msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
		msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
		msg->nextBlockFast(_PREHASH_GroupData);
		msg->addUUIDFast(_PREHASH_GroupID, group_id);
		gAgent.sendReliableMessage();
	}
	return false;
}

void LLPanelGroups::onGroupList(LLUICtrl* ctrl, void* userdata)
{
	LLPanelGroups* self = (LLPanelGroups*)userdata;
	if (self)
	{
		self->enableButtons();
		// check to see if group checkboxes have changed
		self->applyChangesToGroups();
	}
}

void init_group_list(LLScrollListCtrl* ctrl, const LLUUID& highlight_id, const std::string& none_text, bool group_picker, U64 powers_mask)
{
	S32 count = gAgent.mGroups.count();
	LLUUID id;
	LLCtrlListInterface *group_list = ctrl->getListInterface();
	if (!group_list) return;

	group_list->operateOnAll(LLCtrlListInterface::OP_DELETE);

	for(S32 i = 0; i < count; ++i)
	{
		id = gAgent.mGroups.get(i).mID;
		LLGroupData* group_datap = &gAgent.mGroups.get(i);
		if ((powers_mask == GP_ALL_POWERS) || ((group_datap->mPowers & powers_mask) != 0))
		{
			std::string style = "NORMAL";
			if(highlight_id == id)
			{
				style = "BOLD";
			}

			// 0 - Group Name
			// 1 - Group Notices
			// 2 - Group Chat
			// 3 - Group Listing in Profile

			LLSD element;
			element["id"] = id;
			element["columns"][0]["column"] = "name";
			element["columns"][0]["value"] = group_datap->mName;
			element["columns"][0]["font"] = "SANSSERIF";
			element["columns"][0]["font-style"] = style;

			if (!group_picker)
			{
				LLSD& receive_notices_column = element["columns"][1];
				receive_notices_column["column"] = "receive_notices";
				receive_notices_column["type"] = "checkbox";
				receive_notices_column["value"] = group_datap->mAcceptNotices;

				LLSD& join_group_chat_column = element["columns"][2];
				join_group_chat_column["column"] = "join_group_chat";
				join_group_chat_column["type"] = "checkbox";
				join_group_chat_column["value"] = !gIMMgr->getIgnoreGroup(id);

				LLSD& list_in_profile_column = element["columns"][3];
				list_in_profile_column["column"] = "list_in_profile";
				list_in_profile_column["type"] = "checkbox";
				list_in_profile_column["value"] = group_datap->mListInProfile;
			}

			group_list->addElement(element, ADD_SORTED);
		}
	}

	// add "none" to list at top
	std::string style = "NORMAL";
	if (highlight_id.isNull())
	{
		style = "BOLD";
	}
	LLSD element;
	element["id"] = LLUUID::null;
	element["columns"][0]["column"] = "name";
	//UGLY hack to make sure "none" is always on top -- MC
	element["columns"][0]["value"] = "  (" + none_text + ")";
	element["columns"][0]["font"] = "SANSSERIF";
	element["columns"][0]["font-style"] = style;

	if (!group_picker)
	{
		LLSD& receive_notices_column = element["columns"][1];
		receive_notices_column["column"] = "receive_notices";
		receive_notices_column["type"] = "checkbox";
		receive_notices_column["value"] = FALSE;
		receive_notices_column["enabled"] = FALSE;

		LLSD& join_group_chat_column = element["columns"][2];
		join_group_chat_column["column"] = "join_group_chat";
		join_group_chat_column["type"] = "checkbox";
		join_group_chat_column["value"] = FALSE;
		join_group_chat_column["enabled"] = FALSE;

		LLSD& list_in_profile_column = element["columns"][3];
		list_in_profile_column["column"] = "list_in_profile";
		list_in_profile_column["type"] = "checkbox";
		list_in_profile_column["value"] = FALSE;
		list_in_profile_column["enabled"] = FALSE;
	}

	group_list->addElement(element, ADD_TOP);

	group_list->selectByValue(highlight_id);
}

void LLPanelGroups::applyChangesToGroups()
{
	LLScrollListCtrl* group_list = getChild<LLScrollListCtrl>("group list");
	if (group_list)
	{
		// just in case we want to allow selecting multiple groups ever -- MC
		std::vector<LLScrollListItem*> selected = group_list->getAllSelected();
		for (std::vector<LLScrollListItem*>::iterator itr = selected.begin(); itr != selected.end(); ++itr)
		{
			LLUUID group_id = (*itr)->getValue();
			BOOL receive_notices = (*itr)->getColumn(1)->getValue().asBoolean();
			BOOL join_group_chat = (*itr)->getColumn(2)->getValue().asBoolean();
			BOOL list_in_profile = (*itr)->getColumn(3)->getValue().asBoolean();

			LLGroupData group_datap;
			if (gAgent.getGroupData(group_id, group_datap))
			{
				// notices and profile
				if ((receive_notices != group_datap.mAcceptNotices) || (list_in_profile != group_datap.mListInProfile))
				{
					gAgent.setUserGroupFlags(group_id, receive_notices, list_in_profile);
				}

				// chat
				if (!join_group_chat != gIMMgr->getIgnoreGroup(group_id))
				{
					gIMMgr->updateIgnoreGroup(group_id, !join_group_chat);
				}
			}
		}
	}
}

void LLPanelGroups::filterContacts(const std::string& search_string)
{
	std::string search = search_string;
	LLStringUtil::toLower(search);

	if (search.empty())
	{
		// repopulate
		reset();
	}
	else
	{
		LLScrollListCtrl* group_list = getChild<LLScrollListCtrl>("group list");
		if (group_list)
		{
			// don't worry about maintaining selection since we're searching
			std::vector<LLScrollListItem*> vGroups(group_list->getAllData());

			// this should really REALLY use deleteAllItems() to rebuild the list instead
			std::string group_name;
			for (std::vector<LLScrollListItem*>::iterator itr = vGroups.begin(); itr != vGroups.end(); ++itr)
			{
				group_name = (*itr)->getColumn(0)->getValue().asString();
				LLStringUtil::toLower(group_name);
				BOOL show_entry = (group_name.find(search) != std::string::npos);
				if (!show_entry)
				{
					group_list->deleteItems((*itr)->getValue());
				}
			}
			group_list->setScrollPos(0);
			enableButtons();
		}
	}
}

void LLPanelGroups::onGroupSearchKeystroke(const std::string& search_string, void* user_data)
{
	LLPanelGroups* panelp = (LLPanelGroups*)user_data;
	if (panelp)
	{
		panelp->filterContacts(search_string);
	}
}