// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
/** 
* @file llpanelgroupbulk.cpp
* @brief Implementation of llpanelgroupbulk
* @author Baker@lindenlab.com
*
* $LicenseInfo:firstyear=2013&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2013, 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 "llviewerprecompiledheaders.h"

#include "llpanelgroupbulk.h"
#include "llpanelgroupbulkimpl.h"

#include "llagent.h"
#include "llavatarnamecache.h"
#include "llfloateravatarpicker.h"
#include "llbutton.h"
#include "llcallingcard.h"
#include "llcombobox.h"
#include "llgroupactions.h"
#include "llgroupmgr.h"
#include "llnamelistctrl.h"
#include "llnotificationsutil.h"
#include "llscrolllistitem.h"
#include "llspinctrl.h"
#include "lltextbox.h"
#include "llviewerobject.h"
#include "llviewerobjectlist.h"


//////////////////////////////////////////////////////////////////////////
// Implementation of llpanelgroupbulkimpl.h functions
//////////////////////////////////////////////////////////////////////////
LLPanelGroupBulkImpl::LLPanelGroupBulkImpl(const LLUUID& group_id) :
	mGroupID(group_id),
	mBulkAgentList(nullptr),
	mOKButton(nullptr),
	mRemoveButton(nullptr),
	mGroupName(nullptr),
	mLoadingText(),
	mTooManySelected(),
	mCloseCallback(nullptr),
	mCloseCallbackUserData(nullptr),
	mAvatarNameCacheConnection(),
	mRoleNames(nullptr),
	mOwnerWarning(),
	mAlreadyInGroup(),
	mConfirmedOwnerInvite(false),
	mListFullNotificationSent(false)
{}

LLPanelGroupBulkImpl::~LLPanelGroupBulkImpl()
{
	if(mAvatarNameCacheConnection.connected())
	{
		mAvatarNameCacheConnection.disconnect();
	}
}

void LLPanelGroupBulkImpl::callbackClickAdd(void* userdata)
{
	LLPanelGroupBulk* panelp = (LLPanelGroupBulk*)userdata;

	if(panelp)
	{
		//Right now this is hard coded with some knowledge that it is part
		//of a floater since the avatar picker needs to be added as a dependent
		//floater to the parent floater.
		//Soon the avatar picker will be embedded into this panel
		//instead of being it's own separate floater.  But that is next week.
		//This will do for now. -jwolk May 10, 2006
		LLView* button = panelp->findChild<LLButton>("add_button");
		LLFloater* root_floater = gFloaterView->getParentFloater(panelp);
		LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(
			boost::bind(callbackAddUsers, _1, panelp->mImplementation), TRUE, FALSE, FALSE, root_floater->getName(), button);
		if(picker)
		{
			root_floater->addDependentFloater(picker);
			LLGroupMgr::getInstance()->sendCapGroupMembersRequest(panelp->mImplementation->mGroupID);
		}
	}
}

void LLPanelGroupBulkImpl::callbackClickRemove(void* userdata)
{
	LLPanelGroupBulkImpl* selfp = (LLPanelGroupBulkImpl*)userdata;
	if (selfp) 
		selfp->handleRemove();
}

void LLPanelGroupBulkImpl::callbackClickCancel(void* userdata)
{
	LLPanelGroupBulkImpl* selfp = (LLPanelGroupBulkImpl*)userdata;
	if(selfp) 
		(*(selfp->mCloseCallback))(selfp->mCloseCallbackUserData);
}

void LLPanelGroupBulkImpl::callbackSelect(void* userdata)
{
	LLPanelGroupBulkImpl* selfp = (LLPanelGroupBulkImpl*)userdata;
	if (selfp) 
		selfp->handleSelection();
}

void LLPanelGroupBulkImpl::callbackAddUsers(const uuid_vec_t& agent_ids, void* user_data)
{
	for (S32 i = 0; i < (S32)agent_ids.size(); i++)
	{
		LLAvatarName av_name;
		if (LLAvatarNameCache::get(agent_ids[i], &av_name))
		{
			onAvatarNameCache(agent_ids[i], av_name, user_data);
		}
		else 
		{
			LLPanelGroupBulkImpl* selfp = (LLPanelGroupBulkImpl*) user_data;
			if (selfp)
			{
				if (selfp->mAvatarNameCacheConnection.connected())
				{
					selfp->mAvatarNameCacheConnection.disconnect();
				}
				// *TODO : Add a callback per avatar name being fetched.
				selfp->mAvatarNameCacheConnection = LLAvatarNameCache::get(agent_ids[i],boost::bind(onAvatarNameCache, _1, _2, user_data));
			}
		}
	}
}

void LLPanelGroupBulkImpl::onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name, void* user_data)
{
	LLPanelGroupBulkImpl* selfp = (LLPanelGroupBulkImpl*) user_data;

	if (selfp)
	{
		if (selfp->mAvatarNameCacheConnection.connected())
		{
			selfp->mAvatarNameCacheConnection.disconnect();
		}
		std::vector<std::string> names;
		uuid_vec_t agent_ids;
		agent_ids.push_back(agent_id);
		names.push_back(av_name.getCompleteName());

		selfp->addUsers(names, agent_ids);
	}
}

void LLPanelGroupBulkImpl::handleRemove()
{
	std::vector<LLScrollListItem*> selection = mBulkAgentList->getAllSelected();
	if (selection.empty()) 
		return;

	std::vector<LLScrollListItem*>::iterator iter;
	for(iter = selection.begin(); iter != selection.end(); ++iter)
	{
		mInviteeIDs.erase((*iter)->getUUID());
	}

	mBulkAgentList->deleteSelectedItems();
	mRemoveButton->setEnabled(FALSE);

	if(	mOKButton && mOKButton->getEnabled() &&
		mBulkAgentList->isEmpty())
	{
		mOKButton->setEnabled(FALSE);
	}
}

void LLPanelGroupBulkImpl::handleSelection()
{
	std::vector<LLScrollListItem*> selection = mBulkAgentList->getAllSelected();
	if (selection.empty())
		mRemoveButton->setEnabled(FALSE);
	else
		mRemoveButton->setEnabled(TRUE);
}

void LLPanelGroupBulkImpl::addUsers(const std::vector<std::string>& names, const uuid_vec_t& agent_ids)
{
	std::string name;
	LLUUID id;

	if(mListFullNotificationSent)
	{	
		return;
	}

	if(	!mListFullNotificationSent &&
		(names.size() + mInviteeIDs.size() > MAX_GROUP_INVITES))
	{
		mListFullNotificationSent = true;

		// Fail! Show a warning and don't add any names.
		LLSD msg;
		msg["MESSAGE"] = mTooManySelected;
		LLNotificationsUtil::add("GenericAlert", msg);
		return;
	}

	for (S32 i = 0; i < (S32)names.size(); ++i)
	{
		name = names[i];
		id = agent_ids[i];

		if(mInviteeIDs.find(id) != mInviteeIDs.end())
		{
			continue;
		}

		//add the name to the names list
		LLSD row;
		row["id"] = id;
		row["columns"][0]["value"] = name;

		mBulkAgentList->addElement(row);
		mInviteeIDs.insert(id);

		// We've successfully added someone to the list.
		if(mOKButton && !mOKButton->getEnabled())
			mOKButton->setEnabled(TRUE);
	}
}

void LLPanelGroupBulkImpl::setGroupName(std::string name)
{
	if(mGroupName)
		mGroupName->setText(name);
}


LLPanelGroupBulk::LLPanelGroupBulk(const LLUUID& group_id) : 
	LLPanel(),
	mImplementation(new LLPanelGroupBulkImpl(group_id)),
	mPendingGroupPropertiesUpdate(false),
	mPendingRoleDataUpdate(false),
	mPendingMemberDataUpdate(false)
{}

LLPanelGroupBulk::~LLPanelGroupBulk()
{
	delete mImplementation;
}

void LLPanelGroupBulk::clear()
{
	mImplementation->mInviteeIDs.clear();

	if(mImplementation->mBulkAgentList)
		mImplementation->mBulkAgentList->deleteAllItems();
	
	if(mImplementation->mOKButton)
		mImplementation->mOKButton->setEnabled(FALSE);
}

void LLPanelGroupBulk::update()
{
	updateGroupName();
	updateGroupData();
}

void LLPanelGroupBulk::draw()
{
	LLPanel::draw();
	update();
}

void LLPanelGroupBulk::updateGroupName()
{
	LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mImplementation->mGroupID);

	if(	gdatap &&
		gdatap->isGroupPropertiesDataComplete())
	{
		// Only do work if the current group name differs
		if(mImplementation->mGroupName->getText().compare(gdatap->mName) != 0)
			mImplementation->setGroupName(gdatap->mName);
	}
	else
	{
		mImplementation->setGroupName(mImplementation->mLoadingText);
	}
}

void LLPanelGroupBulk::updateGroupData()
{
	LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mImplementation->mGroupID);
	if(gdatap && gdatap->isGroupPropertiesDataComplete())
	{
		mPendingGroupPropertiesUpdate = false;
	}
	else
	{
		if(!mPendingGroupPropertiesUpdate)
		{
			mPendingGroupPropertiesUpdate = true;
			LLGroupMgr::getInstance()->sendGroupPropertiesRequest(mImplementation->mGroupID);
		}
	}

	if(gdatap && gdatap->isRoleDataComplete())
	{
		mPendingRoleDataUpdate = false;
	}
	else
	{
		if(!mPendingRoleDataUpdate)
		{
			mPendingRoleDataUpdate = true;
			LLGroupMgr::getInstance()->sendGroupRoleDataRequest(mImplementation->mGroupID);
		}
	}

	if(gdatap && gdatap->isMemberDataComplete())
	{
		mPendingMemberDataUpdate = false;
	}
	else
	{
		if(!mPendingMemberDataUpdate)
		{
			mPendingMemberDataUpdate = true;
			LLGroupMgr::getInstance()->sendCapGroupMembersRequest(mImplementation->mGroupID);
		}
	}
}

void LLPanelGroupBulk::addUserCallback(const LLUUID& id, const LLAvatarName& av_name)
{
	std::vector<std::string> names;
	uuid_vec_t agent_ids;
	agent_ids.push_back(id);
	names.push_back(av_name.getAccountName());

	mImplementation->addUsers(names, agent_ids);
}

void LLPanelGroupBulk::setCloseCallback(void (*close_callback)(void*), void* data)
{
	mImplementation->mCloseCallback         = close_callback;
	mImplementation->mCloseCallbackUserData = data;
}

void LLPanelGroupBulk::addUsers(uuid_vec_t& agent_ids)
{
	std::vector<std::string> names;
	for (S32 i = 0; i < (S32)agent_ids.size(); i++)
	{
		std::string fullname;
		LLUUID agent_id = agent_ids[i];
		LLViewerObject* dest = gObjectList.findObject(agent_id);
		if(dest && dest->isAvatar())
		{
			LLNameValue* nvfirst = dest->getNVPair("FirstName");
			LLNameValue* nvlast = dest->getNVPair("LastName");
			if(nvfirst && nvlast)
			{
				fullname = LLCacheName::buildFullName(
					nvfirst->getString(), nvlast->getString());

			}
			if (!fullname.empty())
			{
				names.push_back(fullname);
			} 
			else 
			{
				LL_WARNS() << "llPanelGroupBulk: Selected avatar has no name: " << dest->getID() << LL_ENDL;
				names.push_back("(Unknown)");
			}
		}
		else
		{
			//looks like user try to invite offline friend
			//for offline avatar_id gObjectList.findObject() will return null
			//so we need to do this additional search in avatar tracker, see EXT-4732
			if (LLAvatarTracker::instance().isBuddy(agent_id))
			{
				LLAvatarName av_name;
				if (!LLAvatarNameCache::get(agent_id, &av_name))
				{
					// actually it should happen, just in case
					LLAvatarNameCache::get(LLUUID(agent_id), boost::bind(&LLPanelGroupBulk::addUserCallback, this, _1, _2));
					// for this special case!
					//when there is no cached name we should remove resident from agent_ids list to avoid breaking of sequence
					// removed id will be added in callback
					agent_ids.erase(agent_ids.begin() + i);
				}
				else
				{
					names.push_back(av_name.getAccountName());
				}
			}
		}
	}
	mImplementation->mListFullNotificationSent = false;
	mImplementation->addUsers(names, agent_ids);
}

