/**
 * @file llpaneloutfitedit.cpp
 * @brief Displays outfit edit information in Side Tray.
 *
 * $LicenseInfo:firstyear=2009&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 "llviewerprecompiledheaders.h"

#include "llpaneloutfitedit.h"

// *TODO: reorder includes to match the coding standard
#include "llagent.h"
#include "llagentcamera.h"
#include "llagentwearables.h"
#include "llappearancemgr.h"
#include "lloutfitobserver.h"
#include "llcofwearables.h"
#include "llfilteredwearablelist.h"
#include "llfolderview.h"
#include "llinventory.h"
#include "llinventoryitemslist.h"
#include "llviewercontrol.h"
#include "llui.h"
#include "llfloater.h"
#include "llfloaterreg.h"
#include "llinventoryfunctions.h"
#include "llinventorypanel.h"
#include "llviewermenu.h"
#include "llviewerwindow.h"
#include "llviewerinventory.h"
#include "llbutton.h"
#include "llcombobox.h"
#include "llfiltereditor.h"
#include "llinventorybridge.h"
#include "llinventorymodel.h"
#include "llinventorymodelbackgroundfetch.h"
#include "llloadingindicator.h"
#include "llmenubutton.h"
#include "llpaneloutfitsinventory.h"
#include "lluiconstants.h"
#include "llscrolllistctrl.h"
#include "lltextbox.h"
#include "lltoggleablemenu.h"
#include "lltrans.h"
#include "lluictrlfactory.h"
#include "llsdutil.h"
#include "llsidepanelappearance.h"
#include "lltoggleablemenu.h"
#include "llvoavatarself.h"
#include "llwearablelist.h"
#include "llwearableitemslist.h"
#include "llwearabletype.h"
#include "llweb.h"

static LLPanelInjector<LLPanelOutfitEdit> t_outfit_edit("panel_outfit_edit");

const U64 WEARABLE_MASK = (1LL << LLInventoryType::IT_WEARABLE);
const U64 ATTACHMENT_MASK = (1LL << LLInventoryType::IT_ATTACHMENT) | (1LL << LLInventoryType::IT_OBJECT);
const U64 ALL_ITEMS_MASK = WEARABLE_MASK | ATTACHMENT_MASK;

static const std::string REVERT_BTN("revert_btn");
static const std::string SAVE_AS_BTN("save_as_btn");
static const std::string SAVE_BTN("save_btn");


///////////////////////////////////////////////////////////////////////////////
// LLShopURLDispatcher
///////////////////////////////////////////////////////////////////////////////

class LLShopURLDispatcher
{
public:
    std::string resolveURL(LLWearableType::EType wearable_type, ESex sex);
    std::string resolveURL(LLAssetType::EType asset_type, ESex sex);
};

std::string LLShopURLDispatcher::resolveURL(LLWearableType::EType wearable_type, ESex sex)
{
    const std::string prefix = "MarketplaceURL";
    const std::string sex_str = (sex == SEX_MALE) ? "Male" : "Female";
    const std::string type_str = LLWearableType::getInstance()->getTypeName(wearable_type);

    std::string setting_name = prefix;

    switch (wearable_type)
    {
    case LLWearableType::WT_ALPHA:
    case LLWearableType::WT_NONE:
    case LLWearableType::WT_INVALID:    // just in case, this shouldn't happen
    case LLWearableType::WT_COUNT:      // just in case, this shouldn't happen
        break;

    default:
        setting_name += '_';
        setting_name += type_str;
        setting_name += sex_str;
        break;
    }

    return gSavedSettings.getString(setting_name);
}

std::string LLShopURLDispatcher::resolveURL(LLAssetType::EType asset_type, ESex sex)
{
    const std::string prefix = "MarketplaceURL";
    const std::string sex_str = (sex == SEX_MALE) ? "Male" : "Female";
    const std::string type_str = LLAssetType::lookup(asset_type);

    std::string setting_name = prefix;

    switch (asset_type)
    {
    case LLAssetType::AT_CLOTHING:
    case LLAssetType::AT_OBJECT:
    case LLAssetType::AT_BODYPART:
        setting_name += '_';
        setting_name += type_str;
        setting_name += sex_str;
        break;

    // to suppress warnings
    default:
        break;
    }

    return gSavedSettings.getString(setting_name);
}

///////////////////////////////////////////////////////////////////////////////
// LLPanelOutfitEditGearMenu
///////////////////////////////////////////////////////////////////////////////

class LLPanelOutfitEditGearMenu
{
public:
    static LLToggleableMenu* create()
    {
        LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;

        registrar.add("Wearable.Create", { boost::bind(onCreate, _2), LLUICtrl::cb_info::UNTRUSTED_BLOCK });

        llassert(LLMenuGL::sMenuContainer != NULL);
        LLToggleableMenu* menu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>(
            "menu_cof_gear.xml", LLMenuGL::sMenuContainer, LLViewerMenuHolderGL::child_registry_t::instance());
        llassert(menu);
        if (menu)
        {
            populateCreateWearableSubmenus(menu);
        }

        return menu;
    }

private:
    static void onCreate(const LLSD& param)
    {
        LLWearableType::EType type = LLWearableType::getInstance()->typeNameToType(param.asString());
        if (type == LLWearableType::WT_NONE)
        {
            LL_WARNS() << "Invalid wearable type" << LL_ENDL;
            return;
        }

        LLAgentWearables::createWearable(type, true);
    }

    // Populate the menu with items like "New Skin", "New Pants", etc.
    static void populateCreateWearableSubmenus(LLMenuGL* menu)
    {
        LLView* menu_clothes    = gMenuHolder->getChildView("COF.Gear.New_Clothes", false);
        LLView* menu_bp         = gMenuHolder->getChildView("COF.Gear.New_Body_Parts", false);
        LLWearableType * wearable_type_inst = LLWearableType::getInstance();

        for (U8 i = LLWearableType::WT_SHAPE; i != (U8) LLWearableType::WT_COUNT; ++i)
        {
            LLWearableType::EType type = (LLWearableType::EType) i;
            const std::string& type_name = wearable_type_inst->getTypeName(type);

            LLMenuItemCallGL::Params p;
            p.name = type_name;
            p.label = LLTrans::getString(wearable_type_inst->getTypeDefaultNewName(type));
            p.on_click.function_name = "Wearable.Create";
            p.on_click.parameter = LLSD(type_name);

            LLView* parent = wearable_type_inst->getAssetType(type) == LLAssetType::AT_CLOTHING ? menu_clothes : menu_bp;
            LLUICtrlFactory::create<LLMenuItemCallGL>(p, parent);
        }
    }
};

///////////////////////////////////////////////////////////////////////////////
// LLAddWearablesGearMenu
///////////////////////////////////////////////////////////////////////////////

class LLAddWearablesGearMenu : public LLInitClass<LLAddWearablesGearMenu>
{
public:
    static LLToggleableMenu* create(LLWearableItemsList* flat_list, LLInventoryPanel* inventory_panel)
    {
        LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
        LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar;

        llassert(flat_list);
        llassert(inventory_panel);

        LLHandle<LLView> flat_list_handle = flat_list->getHandle();
        LLHandle<LLPanel> inventory_panel_handle = inventory_panel->getHandle();

        registrar.add("AddWearable.Gear.Sort", { boost::bind(onSort, flat_list_handle, inventory_panel_handle, _2) });
        enable_registrar.add("AddWearable.Gear.Check", boost::bind(onCheck, flat_list_handle, inventory_panel_handle, _2));
        enable_registrar.add("AddWearable.Gear.Visible", boost::bind(onVisible, inventory_panel_handle, _2));

        llassert(LLMenuGL::sMenuContainer != NULL);
        LLToggleableMenu* menu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>(
            "menu_add_wearable_gear.xml",
            LLMenuGL::sMenuContainer, LLViewerMenuHolderGL::child_registry_t::instance());

        return menu;
    }

private:
    static void onSort(LLHandle<LLView> flat_list_handle,
                       LLHandle<LLPanel> inventory_panel_handle,
                       LLSD::String sort_order_str)
    {
        if (flat_list_handle.isDead() || inventory_panel_handle.isDead()) return;

        LLWearableItemsList* flat_list = dynamic_cast<LLWearableItemsList*>(flat_list_handle.get());
        LLInventoryPanel* inventory_panel = dynamic_cast<LLInventoryPanel*>(inventory_panel_handle.get());

        if (!flat_list || !inventory_panel) return;

        LLWearableItemsList::ESortOrder sort_order;

        if ("by_most_recent" == sort_order_str)
        {
            sort_order = LLWearableItemsList::E_SORT_BY_MOST_RECENT;
        }
        else if ("by_name" == sort_order_str)
        {
            sort_order = LLWearableItemsList::E_SORT_BY_NAME;
        }
        else if ("by_type" == sort_order_str)
        {
            sort_order = LLWearableItemsList::E_SORT_BY_TYPE_NAME;
        }
        else
        {
            LL_WARNS() << "Unrecognized sort order action" << LL_ENDL;
            return;
        }

        if (inventory_panel->getVisible())
        {
            inventory_panel->getFolderViewModel()->setSorter(sort_order);
        }
        else
        {
            flat_list->setSortOrder(sort_order);
        }
    }

    static bool onCheck(LLHandle<LLView> flat_list_handle,
                        LLHandle<LLPanel> inventory_panel_handle,
                        LLSD::String sort_order_str)
    {
        if (flat_list_handle.isDead() || inventory_panel_handle.isDead()) return false;

        LLWearableItemsList* flat_list = dynamic_cast<LLWearableItemsList*>(flat_list_handle.get());
        LLInventoryPanel* inventory_panel = dynamic_cast<LLInventoryPanel*>(inventory_panel_handle.get());

        if (!inventory_panel || !flat_list) return false;

        // Inventory panel uses its own sort order independent from
        // flat list view so this flag is used to distinguish between
        // currently visible "tree" or "flat" representation of inventory.
        bool inventory_tree_visible = inventory_panel->getVisible();

        if (inventory_tree_visible)
        {
            U32 sort_order = inventory_panel->getSortOrder();

            if ("by_most_recent" == sort_order_str)
            {
                return LLWearableItemsList::E_SORT_BY_MOST_RECENT & sort_order;
            }
            else if ("by_name" == sort_order_str)
            {
                // If inventory panel is not sorted by date then it is sorted by name.
                return LLWearableItemsList::E_SORT_BY_MOST_RECENT & ~sort_order;
            }
            LL_WARNS() << "Unrecognized inventory panel sort order" << LL_ENDL;
        }
        else
        {
            LLWearableItemsList::ESortOrder sort_order = flat_list->getSortOrder();

            if ("by_most_recent" == sort_order_str)
            {
                return LLWearableItemsList::E_SORT_BY_MOST_RECENT == sort_order;
            }
            else if ("by_name" == sort_order_str)
            {
                return LLWearableItemsList::E_SORT_BY_NAME == sort_order;
            }
            else if ("by_type" == sort_order_str)
            {
                return LLWearableItemsList::E_SORT_BY_TYPE_NAME == sort_order;
            }
            LL_WARNS() << "Unrecognized wearable list sort order" << LL_ENDL;
        }
        return false;
    }

    static bool onVisible(LLHandle<LLPanel> inventory_panel_handle,
                          LLSD::String sort_order_str)
    {
        if (inventory_panel_handle.isDead()) return false;

        LLInventoryPanel* inventory_panel = dynamic_cast<LLInventoryPanel*>(inventory_panel_handle.get());

        // Enable sorting by type only for the flat list of items
        // because inventory panel doesn't support this kind of sorting.
        return ( "by_type" == sort_order_str )
                &&  ( !inventory_panel || !inventory_panel->getVisible() );
    }
};

///////////////////////////////////////////////////////////////////////////////
// LLCOFDragAndDropObserver
///////////////////////////////////////////////////////////////////////////////

class LLCOFDragAndDropObserver : public LLInventoryAddItemByAssetObserver
{
public:
    LLCOFDragAndDropObserver(LLInventoryModel* model);

    virtual ~LLCOFDragAndDropObserver();

    virtual void done();

private:
    LLInventoryModel* mModel;
};

inline LLCOFDragAndDropObserver::LLCOFDragAndDropObserver(LLInventoryModel* model):
        mModel(model)
{
    if (model != NULL)
    {
        model->addObserver(this);
    }
}

inline LLCOFDragAndDropObserver::~LLCOFDragAndDropObserver()
{
    if (mModel != NULL && mModel->containsObserver(this))
    {
        mModel->removeObserver(this);
    }
}

void LLCOFDragAndDropObserver::done()
{
    LLAppearanceMgr::instance().updateAppearanceFromCOF();
}

///////////////////////////////////////////////////////////////////////////////
// LLPanelOutfitEdit
///////////////////////////////////////////////////////////////////////////////

LLPanelOutfitEdit::LLPanelOutfitEdit()
:   LLPanel(),
    mSearchFilter(NULL),
    mCOFWearables(NULL),
    mInventoryItemsPanel(NULL),
    mGearMenu(NULL),
    mAddWearablesGearMenu(NULL),
    mCOFDragAndDropObserver(NULL),
    mInitialized(false),
    mAddWearablesPanel(NULL),
    mFolderViewFilterCmbBox(NULL),
    mListViewFilterCmbBox(NULL),
    mWearableListManager(NULL),
    mPlusBtn(NULL),
    mWearablesGearMenuBtn(NULL),
    mGearMenuBtn(NULL)
{
    mSavedFolderState = new LLSaveFolderState();
    mSavedFolderState->setApply(false);


    LLOutfitObserver& observer = LLOutfitObserver::instance();
    observer.addBOFReplacedCallback(boost::bind(&LLPanelOutfitEdit::updateCurrentOutfitName, this));
    observer.addBOFChangedCallback(boost::bind(&LLPanelOutfitEdit::updateVerbs, this));
    observer.addOutfitLockChangedCallback(boost::bind(&LLPanelOutfitEdit::updateVerbs, this));
    observer.addCOFChangedCallback(boost::bind(&LLPanelOutfitEdit::onCOFChanged, this));

    gAgentWearables.addLoadingStartedCallback(boost::bind(&LLPanelOutfitEdit::onOutfitChanging, this, true));
    gAgentWearables.addLoadedCallback(boost::bind(&LLPanelOutfitEdit::onOutfitChanging, this, false));

    mFolderViewItemTypes.reserve(NUM_FOLDER_VIEW_ITEM_TYPES);
    for (U32 i = 0; i < NUM_FOLDER_VIEW_ITEM_TYPES; i++)
    {
        mFolderViewItemTypes.push_back(LLLookItemType());
    }

}

LLPanelOutfitEdit::~LLPanelOutfitEdit()
{
    delete mWearableListManager;
    delete mSavedFolderState;

    delete mCOFDragAndDropObserver;

    while (!mListViewItemTypes.empty()) {
        delete mListViewItemTypes.back();
        mListViewItemTypes.pop_back();
    }
}

bool LLPanelOutfitEdit::postBuild()
{
    // gInventory.isInventoryUsable() no longer needs to be tested per Richard's fix for race conditions between inventory and panels

    mFolderViewItemTypes[FVIT_ALL] = LLLookItemType(getString("Filter.All"), ALL_ITEMS_MASK);
    mFolderViewItemTypes[FVIT_WEARABLE] = LLLookItemType(getString("Filter.Clothes/Body"), WEARABLE_MASK);
    mFolderViewItemTypes[FVIT_ATTACHMENT] = LLLookItemType(getString("Filter.Objects"), ATTACHMENT_MASK);

    //order is important, see EListViewItemType for order information
    mListViewItemTypes.push_back(new LLFilterItem(getString("Filter.All"), new LLFindNonLinksByMask(ALL_ITEMS_MASK)));
    mListViewItemTypes.push_back(new LLFilterItem(getString("Filter.Clothing"), new LLIsTypeActual(LLAssetType::AT_CLOTHING)));
    mListViewItemTypes.push_back(new LLFilterItem(getString("Filter.Bodyparts"), new LLIsTypeActual(LLAssetType::AT_BODYPART)));
    mListViewItemTypes.push_back(new LLFilterItem(getString("Filter.Objects"), new LLFindNonLinksByMask(ATTACHMENT_MASK)));;
    mListViewItemTypes.push_back(new LLFilterItem(LLTrans::getString("shape"), new LLFindActualWearablesOfType(LLWearableType::WT_SHAPE)));
    mListViewItemTypes.push_back(new LLFilterItem(LLTrans::getString("skin"), new LLFindActualWearablesOfType(LLWearableType::WT_SKIN)));
    mListViewItemTypes.push_back(new LLFilterItem(LLTrans::getString("hair"), new LLFindActualWearablesOfType(LLWearableType::WT_HAIR)));
    mListViewItemTypes.push_back(new LLFilterItem(LLTrans::getString("eyes"), new LLFindActualWearablesOfType(LLWearableType::WT_EYES)));
    mListViewItemTypes.push_back(new LLFilterItem(LLTrans::getString("shirt"), new LLFindActualWearablesOfType(LLWearableType::WT_SHIRT)));
    mListViewItemTypes.push_back(new LLFilterItem(LLTrans::getString("pants"), new LLFindActualWearablesOfType(LLWearableType::WT_PANTS)));
    mListViewItemTypes.push_back(new LLFilterItem(LLTrans::getString("shoes"), new LLFindActualWearablesOfType(LLWearableType::WT_SHOES)));
    mListViewItemTypes.push_back(new LLFilterItem(LLTrans::getString("socks"), new LLFindActualWearablesOfType(LLWearableType::WT_SOCKS)));
    mListViewItemTypes.push_back(new LLFilterItem(LLTrans::getString("jacket"), new LLFindActualWearablesOfType(LLWearableType::WT_JACKET)));
    mListViewItemTypes.push_back(new LLFilterItem(LLTrans::getString("gloves"), new LLFindActualWearablesOfType(LLWearableType::WT_GLOVES)));
    mListViewItemTypes.push_back(new LLFilterItem(LLTrans::getString("undershirt"), new LLFindActualWearablesOfType(LLWearableType::WT_UNDERSHIRT)));
    mListViewItemTypes.push_back(new LLFilterItem(LLTrans::getString("underpants"), new LLFindActualWearablesOfType(LLWearableType::WT_UNDERPANTS)));
    mListViewItemTypes.push_back(new LLFilterItem(LLTrans::getString("skirt"), new LLFindActualWearablesOfType(LLWearableType::WT_SKIRT)));
    mListViewItemTypes.push_back(new LLFilterItem(LLTrans::getString("alpha"), new LLFindActualWearablesOfType(LLWearableType::WT_ALPHA)));
    mListViewItemTypes.push_back(new LLFilterItem(LLTrans::getString("tattoo"), new LLFindActualWearablesOfType(LLWearableType::WT_TATTOO)));
    mListViewItemTypes.push_back(new LLFilterItem(LLTrans::getString("physics"), new LLFindActualWearablesOfType(LLWearableType::WT_PHYSICS)));
    mListViewItemTypes.push_back(new LLFilterItem(LLTrans::getString("universal"), new LLFindActualWearablesOfType(LLWearableType::WT_UNIVERSAL)));

    mCurrentOutfitName = getChild<LLTextBox>("curr_outfit_name");
    mStatus = getChild<LLTextBox>("status");

    mFolderViewBtn = getChild<LLButton>("folder_view_btn");
    mListViewBtn = getChild<LLButton>("list_view_btn");

    childSetCommitCallback("filter_button", boost::bind(&LLPanelOutfitEdit::showWearablesFilter, this), NULL);
    childSetCommitCallback("folder_view_btn", boost::bind(&LLPanelOutfitEdit::showWearablesFolderView, this), NULL);
    childSetCommitCallback("folder_view_btn", boost::bind(&LLPanelOutfitEdit::saveListSelection, this), NULL);
    childSetCommitCallback("list_view_btn", boost::bind(&LLPanelOutfitEdit::showWearablesListView, this), NULL);
    childSetCommitCallback("list_view_btn", boost::bind(&LLPanelOutfitEdit::saveListSelection, this), NULL);
    childSetCommitCallback("shop_btn_1", boost::bind(&LLPanelOutfitEdit::onShopButtonClicked, this), NULL);
    childSetCommitCallback("shop_btn_2", boost::bind(&LLPanelOutfitEdit::onShopButtonClicked, this), NULL);

    setVisibleCallback(boost::bind(&LLPanelOutfitEdit::onVisibilityChanged, this, _2));

    mWearablesGearMenuBtn = getChild<LLMenuButton>("wearables_gear_menu_btn");
    mGearMenuBtn = getChild<LLMenuButton>("gear_menu_btn");

    mCOFWearables = findChild<LLCOFWearables>("cof_wearables_list");
    mCOFWearables->setCommitCallback(boost::bind(&LLPanelOutfitEdit::filterWearablesBySelectedItem, this));

    mCOFWearables->getCOFCallbacks().mAddWearable = boost::bind(&LLPanelOutfitEdit::onAddWearableClicked, this);
    mCOFWearables->getCOFCallbacks().mEditWearable = boost::bind(&LLPanelOutfitEdit::onEditWearableClicked, this);
    mCOFWearables->getCOFCallbacks().mDeleteWearable = boost::bind(&LLPanelOutfitEdit::onRemoveFromOutfitClicked, this);
    mCOFWearables->getCOFCallbacks().mMoveWearableCloser = boost::bind(&LLPanelOutfitEdit::moveWearable, this, true);
    mCOFWearables->getCOFCallbacks().mMoveWearableFurther = boost::bind(&LLPanelOutfitEdit::moveWearable, this, false);

    mAddWearablesPanel = getChild<LLPanel>("add_wearables_panel");

    mInventoryItemsPanel = getChild<LLInventoryPanel>("folder_view");
    mInventoryItemsPanel->setFilterTypes(ALL_ITEMS_MASK);
    mInventoryItemsPanel->setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS);
    mInventoryItemsPanel->setSelectCallback(boost::bind(&LLPanelOutfitEdit::updatePlusButton, this));
    mInventoryItemsPanel->getRootFolder()->setReshapeCallback(boost::bind(&LLPanelOutfitEdit::updatePlusButton, this));

    mCOFDragAndDropObserver = new LLCOFDragAndDropObserver(mInventoryItemsPanel->getModel());

    mFolderViewFilterCmbBox = getChild<LLComboBox>("folder_view_filter_combobox");
    mFolderViewFilterCmbBox->setCommitCallback(boost::bind(&LLPanelOutfitEdit::onFolderViewFilterCommitted, this, _1));
    mFolderViewFilterCmbBox->removeall();
    for (U32 i = 0; i < mFolderViewItemTypes.size(); ++i)
    {
        mFolderViewFilterCmbBox->add(mFolderViewItemTypes[i].displayName);
    }
    mFolderViewFilterCmbBox->setCurrentByIndex(FVIT_ALL);

    mListViewFilterCmbBox = getChild<LLComboBox>("list_view_filter_combobox");
    mListViewFilterCmbBox->setCommitCallback(boost::bind(&LLPanelOutfitEdit::onListViewFilterCommitted, this, _1));
    mListViewFilterCmbBox->removeall();
    for (U32 i = 0; i < mListViewItemTypes.size(); ++i)
    {
        mListViewFilterCmbBox->add(mListViewItemTypes[i]->displayName);
    }
    mListViewFilterCmbBox->setCurrentByIndex(LVIT_ALL);

    mSearchFilter = getChild<LLFilterEditor>("look_item_filter");
    mSearchFilter->setCommitCallback(boost::bind(&LLPanelOutfitEdit::onSearchEdit, this, _2));

    childSetAction("show_add_wearables_btn", boost::bind(&LLPanelOutfitEdit::onAddMoreButtonClicked, this));

    mPlusBtn = getChild<LLButton>("plus_btn");
    mPlusBtn->setClickedCallback(boost::bind(&LLPanelOutfitEdit::onPlusBtnClicked, this));

    childSetAction(REVERT_BTN, boost::bind(&LLAppearanceMgr::wearBaseOutfit, LLAppearanceMgr::getInstance()));

    /*
     * By default AT_CLOTHING are sorted by (in in MY OUTFITS):
     *  - by type (types order determined in LLWearableType::EType)
     *  - each LLWearableType::EType by outer layer on top
     *
     * In Add More panel AT_CLOTHING should be sorted in a such way:
     *  - by type (types order determined in LLWearableType::EType)
     *  - each LLWearableType::EType by name (EXT-8205)
    */
    mWearableListViewItemsComparator = new LLWearableItemTypeNameComparator();
    mWearableListViewItemsComparator->setOrder(LLAssetType::AT_CLOTHING, LLWearableItemTypeNameComparator::ORDER_RANK_1, false, true);

    mWearablesListViewPanel = getChild<LLPanel>("filtered_wearables_panel");
    mWearableItemsList = getChild<LLWearableItemsList>("list_view");
    mWearableItemsList->setCommitOnSelectionChange(true);
    mWearableItemsList->setCommitCallback(boost::bind(&LLPanelOutfitEdit::updatePlusButton, this));
    mWearableItemsList->setDoubleClickCallback(boost::bind(&LLPanelOutfitEdit::onPlusBtnClicked, this));

    mWearableItemsList->setComparator(mWearableListViewItemsComparator);

    // Creating "Add Wearables" panel gear menu after initialization of mWearableItemsList and mInventoryItemsPanel.
    mAddWearablesGearMenu = LLAddWearablesGearMenu::create(mWearableItemsList, mInventoryItemsPanel);
    mWearablesGearMenuBtn->setMenu(mAddWearablesGearMenu);

    mGearMenu = LLPanelOutfitEditGearMenu::create();
    mGearMenuBtn->setMenu(mGearMenu);

    getChild<LLButton>(SAVE_BTN)->setCommitCallback(boost::bind(&LLPanelOutfitEdit::saveOutfit, this, false));
    getChild<LLButton>(SAVE_AS_BTN)->setCommitCallback(boost::bind(&LLPanelOutfitEdit::saveOutfit, this, true));

    onOutfitChanging(gAgentWearables.isCOFChangeInProgress());
    return true;
}

// virtual
void LLPanelOutfitEdit::onOpen(const LLSD& key)
{
    if (!mInitialized)
    {
        // *TODO: this method is called even panel is not visible to user because its parent layout panel is hidden.
        // So, we can defer initializing a bit.
        mWearableListManager = new LLFilteredWearableListManager(mWearableItemsList, mListViewItemTypes[LVIT_ALL]->collector);
        displayCurrentOutfit();
        mInitialized = true;
    }
}

void LLPanelOutfitEdit::moveWearable(bool closer_to_body)
{
    LLUUID item_id = mCOFWearables->getSelectedUUID();
    if (item_id.isNull()) return;

    LLViewerInventoryItem* wearable_to_move = gInventory.getItem(item_id);
    LLAppearanceMgr::getInstance()->moveWearable(wearable_to_move, closer_to_body);
}

void LLPanelOutfitEdit::toggleAddWearablesPanel()
{
    bool current_visibility = mAddWearablesPanel->getVisible();
    showAddWearablesPanel(!current_visibility);
}

void LLPanelOutfitEdit::showAddWearablesPanel(bool show_add_wearables)
{
    mAddWearablesPanel->setVisible(show_add_wearables);

    getChild<LLUICtrl>("show_add_wearables_btn")->setValue(show_add_wearables);

    updateFiltersVisibility();
    getChildView("filter_button")->setVisible( show_add_wearables);

    //search filter should be disabled
    if (!show_add_wearables)
    {
        getChild<LLUICtrl>("filter_button")->setValue(false);

        mFolderViewFilterCmbBox->setVisible(false);
        mListViewFilterCmbBox->setVisible(false);

        showWearablesFilter();

        /*
         * By default AT_CLOTHING are sorted by (in in MY OUTFITS):
         *  - by type (types order determined in LLWearableType::EType)
         *  - each LLWearableType::EType by outer layer on top
         *
         * In Add More panel AT_CLOTHING should be sorted in a such way:
         *  - by type (types order determined in LLWearableType::EType)
         *  - each LLWearableType::EType by name (EXT-8205)
        */
        mWearableItemsList->setSortOrder(LLWearableItemsList::E_SORT_BY_TYPE_NAME);

        // Reset mWearableItemsList position to top. See EXT-8180.
        mWearableItemsList->goToTop();
    }
    else
    {
        mWearableListManager->populateIfNeeded();
    }

    //switching button bars
    getChildView("no_add_wearables_button_bar")->setVisible( !show_add_wearables);
    getChildView("add_wearables_button_bar")->setVisible( show_add_wearables);
}

void LLPanelOutfitEdit::showWearablesFilter()
{
    bool filter_visible = getChild<LLUICtrl>("filter_button")->getValue();

    getChildView("filter_panel")->setVisible( filter_visible);

    if(!filter_visible)
    {
        mSearchFilter->clear();
        onSearchEdit(LLStringUtil::null);
    }
    else
    {
        mSearchFilter->setFocus(true);
    }
}

void LLPanelOutfitEdit::showWearablesListView()
{
    if(switchPanels(mInventoryItemsPanel, mWearablesListViewPanel))
    {
        updateWearablesPanelVerbButtons();
        updateFiltersVisibility();
        mWearableListManager->populateIfNeeded();
    }
    mListViewBtn->setToggleState(true);
}

void LLPanelOutfitEdit::showWearablesFolderView()
{
    if(switchPanels(mWearablesListViewPanel, mInventoryItemsPanel))
    {
        updateWearablesPanelVerbButtons();
        updateFiltersVisibility();
    }
    mFolderViewBtn->setToggleState(true);
}

void LLPanelOutfitEdit::updateFiltersVisibility()
{
    mListViewFilterCmbBox->setVisible(mWearablesListViewPanel->getVisible());
    mFolderViewFilterCmbBox->setVisible(mInventoryItemsPanel->getVisible());
}

void LLPanelOutfitEdit::onFolderViewFilterCommitted(LLUICtrl* ctrl)
{
    S32 curr_filter_type = mFolderViewFilterCmbBox->getCurrentIndex();
    if (curr_filter_type < 0) return;

    mInventoryItemsPanel->setFilterTypes(mFolderViewItemTypes[curr_filter_type].inventoryMask);

    mSavedFolderState->setApply(true);
    mInventoryItemsPanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState);

    LLOpenFoldersWithSelection opener;
    mInventoryItemsPanel->getRootFolder()->applyFunctorRecursively(opener);
    mInventoryItemsPanel->getRootFolder()->scrollToShowSelection();

    if (!LLInventoryModelBackgroundFetch::instance().inventoryFetchStarted())
    {
        llassert(false); // this should have been done on startup
        LLInventoryModelBackgroundFetch::instance().start();
    }
}

void LLPanelOutfitEdit::onListViewFilterCommitted(LLUICtrl* ctrl)
{
    S32 curr_filter_type = mListViewFilterCmbBox->getCurrentIndex();
    if (curr_filter_type < 0) return;

    if (curr_filter_type >= LVIT_SHAPE)
    {
        mWearableItemsList->setMenuWearableType(LLWearableType::EType(curr_filter_type - LVIT_SHAPE));
    }
    mWearableListManager->setFilterCollector(mListViewItemTypes[curr_filter_type]->collector);
}

void LLPanelOutfitEdit::onSearchEdit(const std::string& string)
{
    if (mSearchString != string)
    {
        mSearchString = string;

        // Searches are case-insensitive
        LLStringUtil::toUpper(mSearchString);
        LLStringUtil::trimHead(mSearchString);
    }

    if (mSearchString == "")
    {
        mInventoryItemsPanel->setFilterSubString(LLStringUtil::null);
        mWearableItemsList->setFilterSubString(LLStringUtil::null, true);
        // re-open folders that were initially open
        mSavedFolderState->setApply(true);
        mInventoryItemsPanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState);
        LLOpenFoldersWithSelection opener;
        mInventoryItemsPanel->getRootFolder()->applyFunctorRecursively(opener);
        mInventoryItemsPanel->getRootFolder()->scrollToShowSelection();
    }

    if (!LLInventoryModelBackgroundFetch::instance().inventoryFetchStarted())
    {
        llassert(false); // this should have been done on startup
        LLInventoryModelBackgroundFetch::instance().start();
    }

    if (mInventoryItemsPanel->getFilterSubString().empty() && mSearchString.empty())
    {
        // current filter and new filter empty, do nothing
        return;
    }

    // save current folder open state if no filter currently applied
    if (mInventoryItemsPanel->getFilterSubString().empty())
    {
        mSavedFolderState->setApply(false);
        mInventoryItemsPanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState);
    }

    // set new filter string
    mInventoryItemsPanel->setFilterSubString(mSearchString);
    mWearableItemsList->setFilterSubString(mSearchString, true);
}

void LLPanelOutfitEdit::onPlusBtnClicked(void)
{
    uuid_vec_t selected_items;
    getSelectedItemsUUID(selected_items);

    LLPointer<LLInventoryCallback> link_waiter = new LLUpdateAppearanceOnDestroy;

    for(uuid_vec_t::iterator iter = selected_items.begin(); iter != selected_items.end(); iter++)
    {
        LLUUID selected_id = *iter;
        if (!selected_id.isNull())
        {
            //replacing instead of adding the item
            LLAppearanceMgr::getInstance()->wearItemOnAvatar(selected_id, false, true, link_waiter);
        }
    }
}

void LLPanelOutfitEdit::onVisibilityChanged(const LLSD &in_visible_chain)
{
    showAddWearablesPanel(false);
    mWearableItemsList->resetSelection();
    mInventoryItemsPanel->clearSelection();

    if (in_visible_chain.asBoolean())
    {
        update();
    }
    else
    {
        mWearableListManager->holdProgress(); //list population restarts with visibility
    }
}

void LLPanelOutfitEdit::onAddWearableClicked(void)
{
    LLPanelDummyClothingListItem* item = dynamic_cast<LLPanelDummyClothingListItem*>(mCOFWearables->getSelectedItem());

    if(item)
    {
        showFilteredWearablesListView(item->getWearableType());
    }
}

void LLPanelOutfitEdit::onReplaceMenuItemClicked(LLUUID selected_item_id)
{
    LLViewerInventoryItem* item = gInventory.getLinkedItem(selected_item_id);

    if (item)
    {
        showFilteredWearablesListView(item->getWearableType());
    }
}

void LLPanelOutfitEdit::onShopButtonClicked()
{
    static LLShopURLDispatcher url_resolver;

    // will contain the resultant URL
    std::string url;

    if (isAgentAvatarValid())
    {
        // try to get wearable type from 'Add More' panel first (EXT-7639)
        selection_info_t selection_info = getAddMorePanelSelectionType();

        LLWearableType::EType type = selection_info.first;

        if (selection_info.second > 1)
        {
            // the second argument is not important in this case: generic market place will be opened
            url = url_resolver.resolveURL(LLWearableType::WT_NONE, SEX_FEMALE);
        }
        else
        {
        if (type == LLWearableType::WT_NONE)
        {
            type = getCOFWearablesSelectionType();
        }

        ESex sex = gAgentAvatarp->getSex();

        // WT_INVALID comes for attachments
        if (type != LLWearableType::WT_INVALID && type != LLWearableType::WT_NONE)
        {
            url = url_resolver.resolveURL(type, sex);
        }

        if (url.empty())
        {
                url = url_resolver.resolveURL(
                        mCOFWearables->getExpandedAccordionAssetType(), sex);
            }
        }
    }
    else
    {
        LL_WARNS() << "Agent avatar is invalid" << LL_ENDL;

        // the second argument is not important in this case: generic market place will be opened
        url = url_resolver.resolveURL(LLWearableType::WT_NONE, SEX_FEMALE);
    }

    LLWeb::loadURL(url);
}

LLWearableType::EType LLPanelOutfitEdit::getCOFWearablesSelectionType() const
{
    std::vector<LLPanel*> selected_items;
    LLWearableType::EType type = LLWearableType::WT_NONE;

    mCOFWearables->getSelectedItems(selected_items);

    if (selected_items.size() == 1)
    {
        LLPanel* item = selected_items.front();

        // LLPanelDummyClothingListItem is lower then LLPanelInventoryListItemBase in hierarchy tree
        if (LLPanelDummyClothingListItem* dummy_item = dynamic_cast<LLPanelDummyClothingListItem*>(item))
        {
            type = dummy_item->getWearableType();
        }
        else if (LLPanelInventoryListItemBase* real_item = dynamic_cast<LLPanelInventoryListItemBase*>(item))
        {
            type = real_item->getWearableType();
        }
    }

    return type;
}

LLPanelOutfitEdit::selection_info_t LLPanelOutfitEdit::getAddMorePanelSelectionType() const
{
    selection_info_t result = std::make_pair(LLWearableType::WT_NONE, 0);

    if (mAddWearablesPanel != NULL && mAddWearablesPanel->getVisible())
    {
        if (mInventoryItemsPanel != NULL && mInventoryItemsPanel->getVisible())
        {
            std::set<LLFolderViewItem*> selected_items =    mInventoryItemsPanel->getRootFolder()->getSelectionList();

            result.second = selected_items.size();

            if (result.second == 1)
            {
                result.first = getWearableTypeByItemUUID(static_cast<LLFolderViewModelItemInventory*>((*selected_items.begin())->getViewModelItem())->getUUID());
            }
        }
        else if (mWearableItemsList != NULL && mWearableItemsList->getVisible())
        {
            std::vector<LLUUID> selected_uuids;
            mWearableItemsList->getSelectedUUIDs(selected_uuids);

            result.second = selected_uuids.size();

            if (result.second == 1)
            {
                result.first = getWearableTypeByItemUUID(selected_uuids.front());
            }
        }
    }

    return result;
}

LLWearableType::EType LLPanelOutfitEdit::getWearableTypeByItemUUID(const LLUUID& item_uuid) const
{
    LLViewerInventoryItem* item = gInventory.getLinkedItem(item_uuid);
    return (item != NULL) ? item->getWearableType() : LLWearableType::WT_NONE;
}

void LLPanelOutfitEdit::onRemoveFromOutfitClicked(void)
{
    LLUUID id_to_remove = mCOFWearables->getSelectedUUID();
    LLWearableType::EType type = getWearableTypeByItemUUID(id_to_remove);

    LLAppearanceMgr::getInstance()->removeItemFromAvatar(id_to_remove);

    if (!mCOFWearables->getSelectedItem())
    {
        mCOFWearables->selectClothing(type);
    }
}


void LLPanelOutfitEdit::onEditWearableClicked(void)
{
    LLUUID selected_item_id = mCOFWearables->getSelectedUUID();
    if (selected_item_id.notNull())
    {
        gAgentWearables.editWearable(selected_item_id);
    }
}

void LLPanelOutfitEdit::updatePlusButton()
{
    uuid_vec_t selected_items;
    getSelectedItemsUUID(selected_items);
    if (selected_items.empty())
    {
        mPlusBtn->setEnabled(false);
        return;
    }

    // If any of the selected items are not wearable (due to already being worn OR being of the wrong type), disable the add button.
    uuid_vec_t::iterator unwearable_item = std::find_if(selected_items.begin(), selected_items.end(), !boost::bind(&get_can_item_be_worn, _1));
    bool can_add = ( unwearable_item == selected_items.end() );

    mPlusBtn->setEnabled(can_add);

    LLViewerInventoryItem* first_item(gInventory.getItem(selected_items.front()));

    if (can_add &&
        first_item &&
        selected_items.size() == 1 &&
        first_item->getType() == LLAssetType::AT_BODYPART)
    {
        mPlusBtn->setToolTip(getString("replace_body_part"));
    }
    else
    {
        mPlusBtn->setToolTip(LLStringUtil::null);
    }

    /* Removing add to look inline button (not part of mvp for viewer 2)
    LLRect btn_rect(current_item->getLocalRect().mRight - 50,
                    current_item->getLocalRect().mTop,
                    current_item->getLocalRect().mRight - 30,
                    current_item->getLocalRect().mBottom);

    mAddToLookBtn->setRect(btn_rect);
    mAddToLookBtn->setEnabled(true);
    if (!mAddToLookBtn->getVisible())
    {
        mAddToLookBtn->setVisible(true);
    }

    current_item->addChild(mAddToLookBtn); */
}


void LLPanelOutfitEdit::applyFolderViewFilter(EFolderViewItemType type)
{
    mFolderViewFilterCmbBox->setCurrentByIndex(type);
    mFolderViewFilterCmbBox->onCommit();
}

void LLPanelOutfitEdit::applyListViewFilter(EListViewItemType type)
{
    mListViewFilterCmbBox->setCurrentByIndex(type);
    mListViewFilterCmbBox->onCommit();
}

void LLPanelOutfitEdit::filterWearablesBySelectedItem(void)
{
    if (!mAddWearablesPanel->getVisible()) return;

    uuid_vec_t ids;
    mCOFWearables->getSelectedUUIDs(ids);

    bool nothing_selected = ids.empty();
    bool one_selected = ids.size() == 1;
    bool more_than_one_selected = ids.size() > 1;
    bool is_dummy_item = (ids.size() && dynamic_cast<LLPanelDummyClothingListItem*>(mCOFWearables->getSelectedItem()));

    // selected, expanded accordion tabs and selection in flat list view determine filtering when no item is selected in COF
    // selection in flat list view participates in determining filtering because of EXT-7963
    // So the priority of criterions in is:
    //                   1. Selected accordion tab            |  IF (any accordion selected)
    //                                                        |     filter_type = selected_accordion_type
    //                   2. Selected item in flat list view   |  ELSEIF (any item in flat list view selected)
    //                                                        |     filter_type = selected_item_type
    //                   3. Expanded accordion tab            |  ELSEIF (any accordion expanded)
    //                                                        |      filter_type = expanded accordion_type
    if (nothing_selected)
    {
        if (mInventoryItemsPanel->getVisible())
        {
            return;
        }
        showWearablesListView();

        //selected accordion tab is more priority than expanded tab
        //and selected item in flat list view of 'Add more' panel when
        //determining filtering
        LLAssetType::EType type = mCOFWearables->getSelectedAccordionAssetType();
        if (type == LLAssetType::AT_NONE)
        { //no accordion selected

            // when no accordion selected then selected item from flat list view
            // has more priority than expanded when determining filtering
            LLUUID selected_item_id = mWearableItemsList->getSelectedUUID();
            LLViewerInventoryItem* item = gInventory.getLinkedItem(selected_item_id);
            if(item)
        {
                showFilteredWearablesListView(item->getWearableType());
                return;
            }

            // when no accordion selected and no selected items in flat list view
            // determine filtering according to expanded accordion
            type = mCOFWearables->getExpandedAccordionAssetType();
        }

        switch (type)
        {
        case LLAssetType::AT_OBJECT:
            applyListViewFilter(LVIT_ATTACHMENT);
            break;
        case LLAssetType::AT_BODYPART:
            applyListViewFilter(LVIT_BODYPART);
            break;
        case LLAssetType::AT_CLOTHING:
        default:
            applyListViewFilter(LVIT_CLOTHING);
            break;
        }

        return;
    }

    //resetting selection if more than one item is selected
    if (more_than_one_selected)
    {
        if (mInventoryItemsPanel->getVisible())
        {
            applyFolderViewFilter(FVIT_ALL);
            return;
        }

        showWearablesListView();
        applyListViewFilter(LVIT_ALL);
        return;
    }


    //filter wearables by a type represented by a dummy item
    if (one_selected && is_dummy_item)
    {
        if (mInventoryItemsPanel->getVisible())
        {
            applyFolderViewFilter(FVIT_WEARABLE);
            return;
        }

        onAddWearableClicked();
        return;
    }

    LLViewerInventoryItem* item = gInventory.getItem(ids[0]);
    if (!item && ids[0].notNull())
    {
        if (mInventoryItemsPanel->getVisible())
        {
            applyFolderViewFilter(FVIT_ALL);
            return;
        }
        //Inventory misses an item with non-zero id
        showWearablesListView();
        applyListViewFilter(LVIT_ALL);
        return;
    }

    if (item && one_selected && !is_dummy_item)
    {
        if (item->isWearableType())
        {
            if (mInventoryItemsPanel->getVisible())
            {
                applyFolderViewFilter(FVIT_WEARABLE);
                return;
            }
            //single clothing or bodypart item is selected
            showFilteredWearablesListView(item->getWearableType());
            return;
        }
        else
        {
            if (mInventoryItemsPanel->getVisible())
            {
                applyFolderViewFilter(FVIT_ATTACHMENT);
                return;
            }
            //attachment is selected
            showWearablesListView();
            applyListViewFilter(LVIT_ATTACHMENT);
            return;
        }
    }

}



void LLPanelOutfitEdit::update()
{
    mCOFWearables->refresh();

    updateVerbs();
}

bool LLPanelOutfitEdit::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop,
                                          EDragAndDropType cargo_type,
                                          void* cargo_data,
                                          EAcceptance* accept,
                                          std::string& tooltip_msg)
{
    if (cargo_data == NULL)
    {
        LL_WARNS() << "cargo_data is NULL" << LL_ENDL;
        return true;
    }

    switch (cargo_type)
    {
    case DAD_BODYPART:
    case DAD_CLOTHING:
    case DAD_OBJECT:
    case DAD_LINK:
        *accept = ACCEPT_YES_MULTI;
        break;
    default:
        *accept = ACCEPT_NO;
    }

    if (drop)
    {
        LLInventoryItem* item = static_cast<LLInventoryItem*>(cargo_data);

        if (LLAssetType::lookupIsAssetIDKnowable(item->getType()))
        {
            mCOFDragAndDropObserver->watchAsset(item->getAssetUUID());

            /*
             * Adding request to wear item. If the item is a link, then getLinkedUUID() will
             * return the ID of the linked item. Otherwise it will return the item's ID. The
             * second argument is used to delay the appearance update until all dragged items
             * are added to optimize user experience.
             */
            LLAppearanceMgr::instance().addCOFItemLink(item->getLinkedUUID());
        }
        else
        {
            // if asset id is not available for the item we must wear it immediately (attachments only)
            LLAppearanceMgr::instance().addCOFItemLink(item->getLinkedUUID(), new LLUpdateAppearanceAndEditWearableOnDestroy(item->getUUID()));
        }
    }

    return true;
}

void LLPanelOutfitEdit::displayCurrentOutfit()
{
    if (!getVisible())
    {
        setVisible(true);
    }

    updateCurrentOutfitName();

    update();
}

void LLPanelOutfitEdit::updateCurrentOutfitName()
{
    std::string current_outfit_name;
    if (LLAppearanceMgr::getInstance()->getBaseOutfitName(current_outfit_name))
    {
        mCurrentOutfitName->setText(current_outfit_name);
    }
    else
    {
        mCurrentOutfitName->setText(getString("No Outfit"));
    }
}

//private
void LLPanelOutfitEdit::updateVerbs()
{
    bool outfit_is_dirty = LLAppearanceMgr::getInstance()->isOutfitDirty();
    bool outfit_locked = LLAppearanceMgr::getInstance()->isOutfitLocked();
    bool has_baseoutfit = LLAppearanceMgr::getInstance()->getBaseOutfitUUID().notNull();

    getChildView(SAVE_BTN)->setEnabled(!outfit_locked && outfit_is_dirty);
    getChildView(REVERT_BTN)->setEnabled(outfit_is_dirty && has_baseoutfit);

    mStatus->setText(outfit_is_dirty ? getString("unsaved_changes") : getString("now_editing"));

    updateCurrentOutfitName();

    //updating state of "Wear Item" button previously known as "Plus" button
    updatePlusButton();
}

bool LLPanelOutfitEdit::switchPanels(LLPanel* switch_from_panel, LLPanel* switch_to_panel)
{
    if(switch_from_panel && switch_to_panel && !switch_to_panel->getVisible())
    {
        switch_from_panel->setVisible(false);
        switch_to_panel->setVisible(true);
        return true;
    }
    return false;
}

void LLPanelOutfitEdit::resetAccordionState()
{
    if (mCOFWearables != NULL)
    {
        mCOFWearables->expandDefaultAccordionTab();
    }
    else
    {
        LL_WARNS() << "mCOFWearables is NULL" << LL_ENDL;
    }
}

void LLPanelOutfitEdit::onAddMoreButtonClicked()
{
    toggleAddWearablesPanel();
    filterWearablesBySelectedItem();
}

void LLPanelOutfitEdit::showFilteredWearablesListView(LLWearableType::EType type)
{
    showAddWearablesPanel(true);
    showWearablesListView();

    //e_list_view_item_type implicitly contains LLWearableType::EType starting from LVIT_SHAPE
    applyListViewFilter(static_cast<EListViewItemType>(LVIT_SHAPE + type));
    mWearableItemsList->setMenuWearableType(type);
}

static void update_status_widget_rect(LLView * widget, S32 right_border)
{
    LLRect rect = widget->getRect();
    rect.mRight = right_border;

    widget->setShape(rect);
}

void LLPanelOutfitEdit::onOutfitChanging(bool started)
{
    static LLLoadingIndicator* indicator = getChild<LLLoadingIndicator>("edit_outfit_loading_indicator");
    static LLView* status_panel = getChild<LLView>("outfit_name_and_status");
    static S32 indicator_delta = status_panel->getRect().getWidth() - indicator->getRect().mLeft;

    S32 delta = started ? indicator_delta : 0;
    S32 right_border = status_panel->getRect().getWidth() - delta;

    if (mCurrentOutfitName)
        update_status_widget_rect(mCurrentOutfitName, right_border);
    if (mStatus)
        update_status_widget_rect(mStatus, right_border);

    indicator->setVisible(started);
}

void LLPanelOutfitEdit::getCurrentItemUUID(LLUUID& selected_id)
{
    if (mInventoryItemsPanel->getVisible())
    {
        LLFolderViewItem* curr_item = mInventoryItemsPanel->getRootFolder()->getCurSelectedItem();
        if (!curr_item) return;

        LLFolderViewModelItemInventory* listenerp  = static_cast<LLFolderViewModelItemInventory*>(curr_item->getViewModelItem());
        if (!listenerp) return;

        selected_id = listenerp->getUUID();
    }
    else if (mWearablesListViewPanel->getVisible())
    {
        selected_id = mWearableItemsList->getSelectedUUID();
    }
}


void LLPanelOutfitEdit::getSelectedItemsUUID(uuid_vec_t& uuid_list)
{
    void (uuid_vec_t::* tmp)(LLUUID const &) = &uuid_vec_t::push_back;
    if (mInventoryItemsPanel->getVisible())
    {
        std::set<LLFolderViewItem*> item_set =    mInventoryItemsPanel->getRootFolder()->getSelectionList();
        for (std::set<LLFolderViewItem*>::iterator it = item_set.begin(),    end_it = item_set.end();
            it != end_it;
            ++it)
        {
            uuid_list.push_back(static_cast<LLFolderViewModelItemInventory*>((*it)->getViewModelItem())->getUUID());
        }
    }
    else if (mWearablesListViewPanel->getVisible())
    {
        std::vector<LLSD> item_set;
        mWearableItemsList->getSelectedValues(item_set);

        std::for_each(item_set.begin(), item_set.end(), boost::bind( tmp, &uuid_list, boost::bind(&LLSD::asUUID, _1 )));
    }

//  return selected_id;
}

void LLPanelOutfitEdit::onCOFChanged()
{
    //the panel is only updated when is visible to a user

    // BAP - this check has to be removed because otherwise item name
    // changes made when the panel is not visible will not be
    // propagated to the panel.
    // if (!isInVisibleChain()) return;

    update();
}

void LLPanelOutfitEdit::updateWearablesPanelVerbButtons()
{
    if(mWearablesListViewPanel->getVisible())
    {
        mFolderViewBtn->setToggleState(false);
        mFolderViewBtn->setImageOverlay(getString("folder_view_off"), mFolderViewBtn->getImageOverlayHAlign());
        mListViewBtn->setImageOverlay(getString("list_view_on"), mListViewBtn->getImageOverlayHAlign());
    }
    else if(mInventoryItemsPanel->getVisible())
    {
        mListViewBtn->setToggleState(false);
        mListViewBtn->setImageOverlay(getString("list_view_off"), mListViewBtn->getImageOverlayHAlign());
        mFolderViewBtn->setImageOverlay(getString("folder_view_on"), mFolderViewBtn->getImageOverlayHAlign());
    }
}

void LLPanelOutfitEdit::saveListSelection()
{
    if(mWearablesListViewPanel->getVisible())
    {
        std::set<LLFolderViewItem*> selected_ids =    mInventoryItemsPanel->getRootFolder()->getSelectionList();

        if(!selected_ids.size()) return;

        for (std::set<LLFolderViewItem*>::const_iterator item_id =    selected_ids.begin(); item_id != selected_ids.end(); ++item_id)
        {
            mWearableItemsList->selectItemByUUID(static_cast<LLFolderViewModelItemInventory*>((*item_id)->getViewModelItem())->getUUID(),    true);
        }
        mWearableItemsList->scrollToShowFirstSelectedItem();
    }
    else if(mInventoryItemsPanel->getVisible())
    {
        std::vector<LLUUID> selected_ids;
        mWearableItemsList->getSelectedUUIDs(selected_ids);

        if(!selected_ids.size()) return;

        mInventoryItemsPanel->clearSelection();
        LLFolderView* root = mInventoryItemsPanel->getRootFolder();

        if(!root) return;

        for(std::vector<LLUUID>::const_iterator item_id = selected_ids.begin(); item_id != selected_ids.end(); ++item_id)
        {
            LLFolderViewItem* item = mInventoryItemsPanel->getItemByID(*item_id);
            if (!item) continue;

            LLFolderViewFolder* parent = item->getParentFolder();
            if(parent)
            {
                parent->setOpenArrangeRecursively(true, LLFolderViewFolder::RECURSE_UP);
            }
            mInventoryItemsPanel->getRootFolder()->changeSelection(item, true);
        }
        mInventoryItemsPanel->getRootFolder()->scrollToShowSelection();
    }
}

void LLPanelOutfitEdit::saveOutfit(bool as_new)
{
    LLPanelOutfitsInventory* panel_outfits_inventory = LLPanelOutfitsInventory::findInstance();
    if (panel_outfits_inventory)
    {
        panel_outfits_inventory->saveOutfit(as_new);
    }
}

// EOF