/**
* @file llfloaterpathfindingobjects.cpp
* @brief Base class for both the pathfinding linksets and characters floater.
* @author Stinson@lindenlab.com
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, 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 "llfloaterpathfindingobjects.h"

#include <string>
#include <map>
#include <vector>

#include <boost/bind.hpp>
#include <boost/signals2.hpp>

#include "llagent.h"
#include "llavatarname.h"
#include "llavatarnamecache.h"
#include "llbutton.h"
#include "llcheckboxctrl.h"
#include "llfloater.h"
#include "llfontgl.h"
#include "llnotifications.h"
#include "llnotificationsutil.h"
#include "llpathfindingmanager.h"
#include "llresmgr.h"
#include "llscrolllistcell.h"
#include "llscrolllistctrl.h"
#include "llscrolllistitem.h"
#include "llselectmgr.h"
#include "llsd.h"
#include "llstring.h"
#include "llstyle.h"
#include "lltextbase.h"
#include "lluicolortable.h"
#include "llviewermenu.h"
#include "llviewerobject.h"
#include "llviewerobjectlist.h"
#include "llviewerregion.h"
#include "v3dmath.h"
#include "v3math.h"
#include "v4color.h"

#define DEFAULT_BEACON_WIDTH 6

//---------------------------------------------------------------------------
// LLFloaterPathfindingObjects
//---------------------------------------------------------------------------

void LLFloaterPathfindingObjects::onOpen(const LLSD &pKey)
{
    LLFloater::onOpen(pKey);

    selectNoneObjects();
    mObjectsScrollList->setCommitOnSelectionChange(true);

    if (!mSelectionUpdateSlot.connected())
    {
        mSelectionUpdateSlot = LLSelectMgr::getInstance()->mUpdateSignal.connect(boost::bind(&LLFloaterPathfindingObjects::onInWorldSelectionListChanged, this));
    }

    if (!mRegionBoundaryCrossingSlot.connected())
    {
        mRegionBoundaryCrossingSlot = gAgent.addRegionChangedCallback(boost::bind(&LLFloaterPathfindingObjects::onRegionBoundaryCrossed, this));
    }

    if (!mGodLevelChangeSlot.connected())
    {
        mGodLevelChangeSlot = gAgent.registerGodLevelChanageListener(boost::bind(&LLFloaterPathfindingObjects::onGodLevelChange, this, _1));
    }

    requestGetObjects();
}

void LLFloaterPathfindingObjects::onClose(bool pIsAppQuitting)
{
    if (mGodLevelChangeSlot.connected())
    {
        mGodLevelChangeSlot.disconnect();
    }

    if (mRegionBoundaryCrossingSlot.connected())
    {
        mRegionBoundaryCrossingSlot.disconnect();
    }

    if (mSelectionUpdateSlot.connected())
    {
        mSelectionUpdateSlot.disconnect();
    }

    mObjectsScrollList->setCommitOnSelectionChange(false);
    selectNoneObjects();

    if (mObjectsSelection.notNull())
    {
        mObjectsSelection.clear();
    }

    if (pIsAppQuitting)
    {
        clearAllObjects();
    }
}

void LLFloaterPathfindingObjects::draw()
{
    LLFloater::draw();

    if (isShowBeacons())
    {
        std::vector<LLScrollListItem *> selectedItems = mObjectsScrollList->getAllSelected();
        if (!selectedItems.empty())
        {
            auto numSelectedItems = selectedItems.size();
            S32 nameColumnIndex = getNameColumnIndex();
            const LLColor4 &beaconColor = getBeaconColor();
            const LLColor4 &beaconTextColor = getBeaconTextColor();
            S32 beaconWidth = getBeaconWidth();

            std::vector<LLViewerObject *> viewerObjects;
            viewerObjects.reserve(numSelectedItems);

            for (std::vector<LLScrollListItem *>::const_iterator selectedItemIter = selectedItems.begin();
                selectedItemIter != selectedItems.end(); ++selectedItemIter)
            {
                const LLScrollListItem *selectedItem = *selectedItemIter;

                LLViewerObject *viewerObject = gObjectList.findObject(selectedItem->getUUID());
                if (viewerObject != NULL)
                {
                    const std::string &objectName = selectedItem->getColumn(nameColumnIndex)->getValue().asString();
                    gObjectList.addDebugBeacon(viewerObject->getPositionAgent(), objectName, beaconColor, beaconTextColor, beaconWidth);
                }
            }
        }
    }
}

LLFloaterPathfindingObjects::LLFloaterPathfindingObjects(const LLSD &pSeed)
    : LLFloater(pSeed),
    mObjectsScrollList(NULL),
    mMessagingStatus(NULL),
    mRefreshListButton(NULL),
    mSelectAllButton(NULL),
    mSelectNoneButton(NULL),
    mShowBeaconCheckBox(NULL),
    mTakeButton(NULL),
    mTakeCopyButton(NULL),
    mReturnButton(NULL),
    mDeleteButton(NULL),
    mTeleportButton(NULL),
    mDefaultBeaconColor(),
    mDefaultBeaconTextColor(),
    mErrorTextColor(),
    mWarningTextColor(),
    mMessagingState(kMessagingUnknown),
    mMessagingRequestId(0U),
    mMissingNameObjectsScrollListItems(),
    mObjectList(),
    mObjectsSelection(),
    mHasObjectsToBeSelected(false),
    mObjectsToBeSelected(),
    mSelectionUpdateSlot(),
    mRegionBoundaryCrossingSlot()
{
}

LLFloaterPathfindingObjects::~LLFloaterPathfindingObjects()
{
    clearAllObjects();
}

bool LLFloaterPathfindingObjects::postBuild()
{
    mDefaultBeaconColor = LLUIColorTable::getInstance()->getColor("PathfindingDefaultBeaconColor");
    mDefaultBeaconTextColor = LLUIColorTable::getInstance()->getColor("PathfindingDefaultBeaconTextColor");
    mErrorTextColor = LLUIColorTable::getInstance()->getColor("PathfindingErrorColor");
    mWarningTextColor = LLUIColorTable::getInstance()->getColor("PathfindingWarningColor");

    mObjectsScrollList = findChild<LLScrollListCtrl>("objects_scroll_list");
    llassert(mObjectsScrollList != NULL);
    mObjectsScrollList->setCommitCallback(boost::bind(&LLFloaterPathfindingObjects::onScrollListSelectionChanged, this));
    mObjectsScrollList->sortByColumnIndex(static_cast<U32>(getNameColumnIndex()), true);

    mMessagingStatus = findChild<LLTextBase>("messaging_status");
    llassert(mMessagingStatus != NULL);

    mRefreshListButton = findChild<LLButton>("refresh_objects_list");
    llassert(mRefreshListButton != NULL);
    mRefreshListButton->setCommitCallback(boost::bind(&LLFloaterPathfindingObjects::onRefreshObjectsClicked, this));

    mSelectAllButton = findChild<LLButton>("select_all_objects");
    llassert(mSelectAllButton != NULL);
    mSelectAllButton->setCommitCallback(boost::bind(&LLFloaterPathfindingObjects::onSelectAllObjectsClicked, this));

    mSelectNoneButton = findChild<LLButton>("select_none_objects");
    llassert(mSelectNoneButton != NULL);
    mSelectNoneButton->setCommitCallback(boost::bind(&LLFloaterPathfindingObjects::onSelectNoneObjectsClicked, this));

    mShowBeaconCheckBox = findChild<LLCheckBoxCtrl>("show_beacon");
    llassert(mShowBeaconCheckBox != NULL);

    mTakeButton = findChild<LLButton>("take_objects");
    llassert(mTakeButton != NULL);
    mTakeButton->setCommitCallback(boost::bind(&LLFloaterPathfindingObjects::onTakeClicked, this));

    mTakeCopyButton = findChild<LLButton>("take_copy_objects");
    llassert(mTakeCopyButton != NULL);
    mTakeCopyButton->setCommitCallback(boost::bind(&LLFloaterPathfindingObjects::onTakeCopyClicked, this));

    mReturnButton = findChild<LLButton>("return_objects");
    llassert(mReturnButton != NULL);
    mReturnButton->setCommitCallback(boost::bind(&LLFloaterPathfindingObjects::onReturnClicked, this));

    mDeleteButton = findChild<LLButton>("delete_objects");
    llassert(mDeleteButton != NULL);
    mDeleteButton->setCommitCallback(boost::bind(&LLFloaterPathfindingObjects::onDeleteClicked, this));

    mTeleportButton = findChild<LLButton>("teleport_me_to_object");
    llassert(mTeleportButton != NULL);
    mTeleportButton->setCommitCallback(boost::bind(&LLFloaterPathfindingObjects::onTeleportClicked, this));

    return LLFloater::postBuild();
}

void LLFloaterPathfindingObjects::requestGetObjects()
{
    llassert(0);
}

LLPathfindingManager::request_id_t LLFloaterPathfindingObjects::getNewRequestId()
{
    return ++mMessagingRequestId;
}

void LLFloaterPathfindingObjects::handleNewObjectList(LLPathfindingManager::request_id_t pRequestId, LLPathfindingManager::ERequestStatus pRequestStatus, LLPathfindingObjectListPtr pObjectList)
{
    llassert(pRequestId <= mMessagingRequestId);
    if (pRequestId == mMessagingRequestId)
    {
        switch (pRequestStatus)
        {
        case LLPathfindingManager::kRequestStarted :
            setMessagingState(kMessagingGetRequestSent);
            break;
        case LLPathfindingManager::kRequestCompleted :
            mObjectList = pObjectList;
            rebuildObjectsScrollList();
            setMessagingState(kMessagingComplete);
            break;
        case LLPathfindingManager::kRequestNotEnabled :
            clearAllObjects();
            setMessagingState(kMessagingNotEnabled);
            break;
        case LLPathfindingManager::kRequestError :
            clearAllObjects();
            setMessagingState(kMessagingGetError);
            break;
        default :
            clearAllObjects();
            setMessagingState(kMessagingGetError);
            llassert(0);
            break;
        }
    }
}

void LLFloaterPathfindingObjects::handleUpdateObjectList(LLPathfindingManager::request_id_t pRequestId, LLPathfindingManager::ERequestStatus pRequestStatus, LLPathfindingObjectListPtr pObjectList)
{
    // We current assume that handleUpdateObjectList is called only when objects are being SET
    llassert(pRequestId <= mMessagingRequestId);
    if (pRequestId == mMessagingRequestId)
    {
        switch (pRequestStatus)
        {
        case LLPathfindingManager::kRequestStarted :
            setMessagingState(kMessagingSetRequestSent);
            break;
        case LLPathfindingManager::kRequestCompleted :
            if (mObjectList == NULL)
            {
                mObjectList = pObjectList;
            }
            else
            {
                mObjectList->update(pObjectList);
            }
            rebuildObjectsScrollList();
            setMessagingState(kMessagingComplete);
            break;
        case LLPathfindingManager::kRequestNotEnabled :
            clearAllObjects();
            setMessagingState(kMessagingNotEnabled);
            break;
        case LLPathfindingManager::kRequestError :
            clearAllObjects();
            setMessagingState(kMessagingSetError);
            break;
        default :
            clearAllObjects();
            setMessagingState(kMessagingSetError);
            llassert(0);
            break;
        }
    }
}

void LLFloaterPathfindingObjects::rebuildObjectsScrollList(bool update_if_needed)
{
    if (!mHasObjectsToBeSelected)
    {
        std::vector<LLScrollListItem*> selectedItems = mObjectsScrollList->getAllSelected();
        auto numSelectedItems = selectedItems.size();
        if (numSelectedItems > 0)
        {
            mObjectsToBeSelected.reserve(selectedItems.size());
            for (std::vector<LLScrollListItem*>::const_iterator itemIter = selectedItems.begin();
                itemIter != selectedItems.end(); ++itemIter)
            {
                const LLScrollListItem *listItem = *itemIter;
                mObjectsToBeSelected.push_back(listItem->getUUID());
            }
        }
    }

    S32 origScrollPosition = mObjectsScrollList->getScrollPos();
    mObjectsScrollList->deleteAllItems();
    mMissingNameObjectsScrollListItems.clear();

    if ((mObjectList != NULL) && !mObjectList->isEmpty())
    {
        buildObjectsScrollList(mObjectList);

        if(mObjectsScrollList->selectMultiple(mObjectsToBeSelected) == 0)
        {
            if(update_if_needed && mRefreshListButton->getEnabled())
            {
                requestGetObjects();
                return;
            }
        }
        if (mHasObjectsToBeSelected)
        {
            mObjectsScrollList->scrollToShowSelected();
        }
        else
        {
            mObjectsScrollList->setScrollPos(origScrollPosition);
        }
    }

    mObjectsToBeSelected.clear();
    mHasObjectsToBeSelected = false;

    updateControlsOnScrollListChange();
}

void LLFloaterPathfindingObjects::buildObjectsScrollList(const LLPathfindingObjectListPtr pObjectListPtr)
{
    llassert(0);
}

void LLFloaterPathfindingObjects::addObjectToScrollList(const LLPathfindingObjectPtr pObjectPtr, const LLSD &pScrollListItemData)
{
    LLScrollListCell::Params cellParams;
    cellParams.font = LLFontGL::getFontSansSerif();

    LLScrollListItem::Params rowParams;
    rowParams.value = pObjectPtr->getUUID().asString();

    llassert(pScrollListItemData.isArray());
    for (LLSD::array_const_iterator cellIter = pScrollListItemData.beginArray();
        cellIter != pScrollListItemData.endArray(); ++cellIter)
    {
        const LLSD &cellElement = *cellIter;

        llassert(cellElement.has("column"));
        llassert(cellElement.get("column").isString());
        cellParams.column = cellElement.get("column").asString();

        llassert(cellElement.has("value"));
        llassert(cellElement.get("value").isString());
        cellParams.value = cellElement.get("value").asString();

        rowParams.columns.add(cellParams);
    }

    LLScrollListItem *scrollListItem = mObjectsScrollList->addRow(rowParams);

    if (pObjectPtr->hasOwner() && !pObjectPtr->hasOwnerName())
    {
        mMissingNameObjectsScrollListItems.insert(scroll_list_item_map::value_type(pObjectPtr->getUUID().asString(), scrollListItem));
        pObjectPtr->registerOwnerNameListener(boost::bind(&LLFloaterPathfindingObjects::handleObjectNameResponse, this, _1));
    }
}

void LLFloaterPathfindingObjects::updateControlsOnScrollListChange()
{
    updateMessagingStatus();
    updateStateOnListControls();
    selectScrollListItemsInWorld();
    updateStateOnActionControls();
}

void LLFloaterPathfindingObjects::updateControlsOnInWorldSelectionChange()
{
    updateStateOnActionControls();
}

S32 LLFloaterPathfindingObjects::getNameColumnIndex() const
{
    return 0;
}

S32 LLFloaterPathfindingObjects::getOwnerNameColumnIndex() const
{
    return 2;
}

std::string LLFloaterPathfindingObjects::getOwnerName(const LLPathfindingObject *pObject) const
{
    llassert(0);
    std::string returnVal;
    return returnVal;
}

const LLColor4 &LLFloaterPathfindingObjects::getBeaconColor() const
{
    return mDefaultBeaconColor;
}

const LLColor4 &LLFloaterPathfindingObjects::getBeaconTextColor() const
{
    return mDefaultBeaconTextColor;
}

S32 LLFloaterPathfindingObjects::getBeaconWidth() const
{
    return DEFAULT_BEACON_WIDTH;
}

void LLFloaterPathfindingObjects::showFloaterWithSelectionObjects()
{
    mObjectsToBeSelected.clear();

    LLObjectSelectionHandle selectedObjectsHandle = LLSelectMgr::getInstance()->getSelection();
    if (selectedObjectsHandle.notNull())
    {
        LLObjectSelection *selectedObjects = selectedObjectsHandle.get();
        if (!selectedObjects->isEmpty())
        {
            for (LLObjectSelection::valid_iterator objectIter = selectedObjects->valid_begin();
                objectIter != selectedObjects->valid_end(); ++objectIter)
            {
                LLSelectNode *object = *objectIter;
                LLViewerObject *viewerObject = object->getObject();
                mObjectsToBeSelected.push_back(viewerObject->getID());
            }
        }
    }
    mHasObjectsToBeSelected = true;

    if (!isShown())
    {
        openFloater();
        setVisibleAndFrontmost();
    }
    else
    {
        rebuildObjectsScrollList(true);
        if (isMinimized())
        {
            setMinimized(false);
        }
        setVisibleAndFrontmost();
    }
    setFocus(true);
}

bool LLFloaterPathfindingObjects::isShowBeacons() const
{
    return mShowBeaconCheckBox->get();
}

void LLFloaterPathfindingObjects::clearAllObjects()
{
    selectNoneObjects();
    mObjectsScrollList->deleteAllItems();
    mMissingNameObjectsScrollListItems.clear();
    mObjectList.reset();
}

void LLFloaterPathfindingObjects::selectAllObjects()
{
    mObjectsScrollList->selectAll();
}

void LLFloaterPathfindingObjects::selectNoneObjects()
{
    mObjectsScrollList->deselectAllItems();
}

void LLFloaterPathfindingObjects::teleportToSelectedObject()
{
    std::vector<LLScrollListItem*> selectedItems = mObjectsScrollList->getAllSelected();
    llassert(selectedItems.size() == 1);
    if (selectedItems.size() == 1)
    {
        std::vector<LLScrollListItem*>::const_reference selectedItemRef = selectedItems.front();
        const LLScrollListItem *selectedItem = selectedItemRef;
        llassert(mObjectList != NULL);
        LLVector3d teleportLocation;
        LLViewerObject *viewerObject = gObjectList.findObject(selectedItem->getUUID());
        if (viewerObject == NULL)
        {
            // If we cannot find the object in the viewer list, teleport to the last reported position
            const LLPathfindingObjectPtr objectPtr = mObjectList->find(selectedItem->getUUID().asString());
            teleportLocation = gAgent.getPosGlobalFromAgent(objectPtr->getLocation());
        }
        else
        {
            // If we can find the object in the viewer list, teleport to the known current position
            teleportLocation = viewerObject->getPositionGlobal();
        }
        gAgent.teleportViaLocationLookAt(teleportLocation);
    }
}

LLPathfindingObjectListPtr LLFloaterPathfindingObjects::getEmptyObjectList() const
{
    llassert(0);
    LLPathfindingObjectListPtr objectListPtr(new LLPathfindingObjectList());
    return objectListPtr;
}

int LLFloaterPathfindingObjects::getNumSelectedObjects() const
{
    return mObjectsScrollList->getNumSelected();
}

LLPathfindingObjectListPtr LLFloaterPathfindingObjects::getSelectedObjects() const
{
    LLPathfindingObjectListPtr selectedObjects = getEmptyObjectList();

    std::vector<LLScrollListItem*> selectedItems = mObjectsScrollList->getAllSelected();
    if (!selectedItems.empty())
    {
        for (std::vector<LLScrollListItem*>::const_iterator itemIter = selectedItems.begin();
            itemIter != selectedItems.end(); ++itemIter)
        {
            LLPathfindingObjectPtr objectPtr = findObject(*itemIter);
            if (objectPtr != NULL)
            {
                selectedObjects->update(objectPtr);
            }
        }
    }

    return selectedObjects;
}

LLPathfindingObjectPtr LLFloaterPathfindingObjects::getFirstSelectedObject() const
{
    LLPathfindingObjectPtr objectPtr;

    std::vector<LLScrollListItem*> selectedItems = mObjectsScrollList->getAllSelected();
    if (!selectedItems.empty())
    {
        objectPtr = findObject(selectedItems.front());
    }

    return objectPtr;
}

LLFloaterPathfindingObjects::EMessagingState LLFloaterPathfindingObjects::getMessagingState() const
{
    return mMessagingState;
}

void LLFloaterPathfindingObjects::setMessagingState(EMessagingState pMessagingState)
{
    mMessagingState = pMessagingState;
    updateControlsOnScrollListChange();
}

void LLFloaterPathfindingObjects::onRefreshObjectsClicked()
{
    requestGetObjects();
}

void LLFloaterPathfindingObjects::onSelectAllObjectsClicked()
{
    selectAllObjects();
}

void LLFloaterPathfindingObjects::onSelectNoneObjectsClicked()
{
    selectNoneObjects();
}

void LLFloaterPathfindingObjects::onTakeClicked()
{
    handle_take();
    requestGetObjects();
}

void LLFloaterPathfindingObjects::onTakeCopyClicked()
{
    handle_take_copy();
}

void LLFloaterPathfindingObjects::onReturnClicked()
{
    LLNotification::Params params("PathfindingReturnMultipleItems");
    params.functor.function(boost::bind(&LLFloaterPathfindingObjects::handleReturnItemsResponse, this, _1, _2));

    LLSD substitutions;
    int numItems = getNumSelectedObjects();
    substitutions["NUM_ITEMS"] = static_cast<LLSD::Integer>(numItems);
    params.substitutions = substitutions;

    if (numItems == 1)
    {
        LLNotifications::getInstance()->forceResponse(params, 0);
    }
    else if (numItems > 1)
    {
        LLNotifications::getInstance()->add(params);
    }
}

void LLFloaterPathfindingObjects::onDeleteClicked()
{
    LLNotification::Params params("PathfindingDeleteMultipleItems");
    params.functor.function(boost::bind(&LLFloaterPathfindingObjects::handleDeleteItemsResponse, this, _1, _2));

    LLSD substitutions;
    int numItems = getNumSelectedObjects();
    substitutions["NUM_ITEMS"] = static_cast<LLSD::Integer>(numItems);
    params.substitutions = substitutions;

    if (numItems == 1)
    {
        LLNotifications::getInstance()->forceResponse(params, 0);
    }
    else if (numItems > 1)
    {
        LLNotifications::getInstance()->add(params);
    }
}

void LLFloaterPathfindingObjects::onTeleportClicked()
{
    teleportToSelectedObject();
}

void LLFloaterPathfindingObjects::onScrollListSelectionChanged()
{
    updateControlsOnScrollListChange();
}

void LLFloaterPathfindingObjects::onInWorldSelectionListChanged()
{
    updateControlsOnInWorldSelectionChange();
}

void LLFloaterPathfindingObjects::onRegionBoundaryCrossed()
{
    requestGetObjects();
}

void LLFloaterPathfindingObjects::onGodLevelChange(U8 pGodLevel)
{
    requestGetObjects();
}

void LLFloaterPathfindingObjects::handleObjectNameResponse(const LLPathfindingObject *pObject)
{
    llassert(pObject != NULL);
    const std::string uuid = pObject->getUUID().asString();
    scroll_list_item_map::iterator scrollListItemIter = mMissingNameObjectsScrollListItems.find(uuid);
    if (scrollListItemIter != mMissingNameObjectsScrollListItems.end())
    {
        LLScrollListItem *scrollListItem = scrollListItemIter->second;
        llassert(scrollListItem != NULL);

        LLScrollListCell *scrollListCell = scrollListItem->getColumn(getOwnerNameColumnIndex());
        LLSD ownerName = getOwnerName(pObject);

        scrollListCell->setValue(ownerName);

        mMissingNameObjectsScrollListItems.erase(scrollListItemIter);
    }
}

void LLFloaterPathfindingObjects::updateMessagingStatus()
{
    std::string statusText("");
    LLStyle::Params styleParams;

    switch (getMessagingState())
    {
    case kMessagingUnknown:
        statusText = getString("messaging_initial");
        styleParams.color = mErrorTextColor;
        break;
    case kMessagingGetRequestSent :
        statusText = getString("messaging_get_inprogress");
        styleParams.color = mWarningTextColor;
        break;
    case kMessagingGetError :
        statusText = getString("messaging_get_error");
        styleParams.color = mErrorTextColor;
        break;
    case kMessagingSetRequestSent :
        statusText = getString("messaging_set_inprogress");
        styleParams.color = mWarningTextColor;
        break;
    case kMessagingSetError :
        statusText = getString("messaging_set_error");
        styleParams.color = mErrorTextColor;
        break;
    case kMessagingComplete :
        if (mObjectsScrollList->isEmpty())
        {
            statusText = getString("messaging_complete_none_found");
        }
        else
        {
            S32 numItems = mObjectsScrollList->getItemCount();
            S32 numSelectedItems = mObjectsScrollList->getNumSelected();

            LLLocale locale(LLStringUtil::getLocale());
            std::string numItemsString;
            LLResMgr::getInstance()->getIntegerString(numItemsString, numItems);

            std::string numSelectedItemsString;
            LLResMgr::getInstance()->getIntegerString(numSelectedItemsString, numSelectedItems);

            LLStringUtil::format_map_t string_args;
            string_args["[NUM_SELECTED]"] = numSelectedItemsString;
            string_args["[NUM_TOTAL]"] = numItemsString;
            statusText = getString("messaging_complete_available", string_args);
        }
        break;
    case kMessagingNotEnabled :
        statusText = getString("messaging_not_enabled");
        styleParams.color = mErrorTextColor;
        break;
    default:
        statusText = getString("messaging_initial");
        styleParams.color = mErrorTextColor;
        llassert(0);
        break;
    }

    mMessagingStatus->setText((LLStringExplicit)statusText, styleParams);
}

void LLFloaterPathfindingObjects::updateStateOnListControls()
{
    switch (getMessagingState())
    {
    case kMessagingUnknown:
    case kMessagingGetRequestSent :
    case kMessagingSetRequestSent :
        mRefreshListButton->setEnabled(false);
        mSelectAllButton->setEnabled(false);
        mSelectNoneButton->setEnabled(false);
        break;
    case kMessagingGetError :
    case kMessagingSetError :
    case kMessagingNotEnabled :
        mRefreshListButton->setEnabled(true);
        mSelectAllButton->setEnabled(false);
        mSelectNoneButton->setEnabled(false);
        break;
    case kMessagingComplete :
        {
            int numItems = mObjectsScrollList->getItemCount();
            int numSelectedItems = mObjectsScrollList->getNumSelected();
            mRefreshListButton->setEnabled(true);
            mSelectAllButton->setEnabled(numSelectedItems < numItems);
            mSelectNoneButton->setEnabled(numSelectedItems > 0);
        }
        break;
    default:
        llassert(0);
        break;
    }
}

void LLFloaterPathfindingObjects::updateStateOnActionControls()
{
    int numSelectedItems = mObjectsScrollList->getNumSelected();
    bool isEditEnabled = (numSelectedItems > 0);

    mShowBeaconCheckBox->setEnabled(isEditEnabled);
    mTakeButton->setEnabled(isEditEnabled && visible_take_object());
    mTakeCopyButton->setEnabled(isEditEnabled && enable_object_take_copy());
    mReturnButton->setEnabled(isEditEnabled && enable_object_return());
    mDeleteButton->setEnabled(isEditEnabled && enable_object_delete());
    mTeleportButton->setEnabled(numSelectedItems == 1);
}

void LLFloaterPathfindingObjects::selectScrollListItemsInWorld()
{
    mObjectsSelection.clear();
    LLSelectMgr::getInstance()->deselectAll();

    std::vector<LLScrollListItem *> selectedItems = mObjectsScrollList->getAllSelected();
    if (!selectedItems.empty())
    {
        auto numSelectedItems = selectedItems.size();

        std::vector<LLViewerObject *>viewerObjects;
        viewerObjects.reserve(numSelectedItems);

        for (std::vector<LLScrollListItem *>::const_iterator selectedItemIter = selectedItems.begin();
            selectedItemIter != selectedItems.end(); ++selectedItemIter)
        {
            const LLScrollListItem *selectedItem = *selectedItemIter;

            LLViewerObject *viewerObject = gObjectList.findObject(selectedItem->getUUID());
            if (viewerObject != NULL)
            {
                viewerObjects.push_back(viewerObject);
            }
        }

        if (!viewerObjects.empty())
        {
            mObjectsSelection = LLSelectMgr::getInstance()->selectObjectAndFamily(viewerObjects);
        }
    }
}

void LLFloaterPathfindingObjects::handleReturnItemsResponse(const LLSD &pNotification, const LLSD &pResponse)
{
    if (LLNotificationsUtil::getSelectedOption(pNotification, pResponse) == 0)
    {
        handle_object_return();
        requestGetObjects();
    }
}

void LLFloaterPathfindingObjects::handleDeleteItemsResponse(const LLSD &pNotification, const LLSD &pResponse)
{
    if (LLNotificationsUtil::getSelectedOption(pNotification, pResponse) == 0)
    {
        handle_object_delete();
        requestGetObjects();
    }
}

LLPathfindingObjectPtr LLFloaterPathfindingObjects::findObject(const LLScrollListItem *pListItem) const
{
    LLPathfindingObjectPtr objectPtr;

    LLUUID uuid = pListItem->getUUID();
    const std::string &uuidString = uuid.asString();
    llassert(mObjectList != NULL);
    objectPtr = mObjectList->find(uuidString);

    return objectPtr;
}