/**
 * @file llinventorymodel.h
 * @brief LLInventoryModel class header file
 *
 * $LicenseInfo:firstyear=2002&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$
 */

#ifndef LL_LLINVENTORYMODEL_H
#define LL_LLINVENTORYMODEL_H

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

#include "llassettype.h"
#include "llfoldertype.h"
#include "llframetimer.h"
#include "lluuid.h"
#include "llpermissionsflags.h"
#include "llviewerinventory.h"
#include "llstring.h"
#include "httpcommon.h"
#include "httprequest.h"
#include "httpoptions.h"
#include "httpheaders.h"
#include "httphandler.h"
#include "lleventcoro.h"
#include "llcoros.h"

class LLInventoryObserver;
class LLInventoryObject;
class LLInventoryItem;
class LLInventoryCategory;
class LLMessageSystem;
class LLInventoryCollectFunctor;

///----------------------------------------------------------------------------
/// LLInventoryValidationInfo
///----------------------------------------------------------------------------
class LLInventoryValidationInfo: public LLRefCount
{
public:
    LLInventoryValidationInfo();
    void toOstream(std::ostream& os) const;
    void asLLSD(LLSD& sd) const;

    bool mInitialized{false};
    S32 mWarningCount{0};
    std::map<std::string,U32> mWarnings;

    S32 mLoopCount{0}; // Presence of folders whose ancestors loop onto themselves
    S32 mOrphanedCount{0}; // Missing or orphaned items, links and folders

    S32 mFatalErrorCount{0};
    bool mFatalNoRootFolder{false};
    S32 mFatalSystemDuplicate{0};
    bool mFatalNoLibraryRootFolder{false};
    bool mFatalQADebugMode{false};

    std::set<LLFolderType::EType> mMissingRequiredSystemFolders;
    std::set<LLFolderType::EType> mDuplicateRequiredSystemFolders;
};
std::ostream& operator<<(std::ostream& s, const LLInventoryValidationInfo& v);

///----------------------------------------------------------------------------
// LLInventoryModel
//
// Represents a collection of inventory, and provides efficient ways to access
// that information.
//   NOTE: This class could in theory be used for any place where you need
//   inventory, though it optimizes for time efficiency - not space efficiency,
//   probably making it inappropriate for use on tasks.
///----------------------------------------------------------------------------
class LLInventoryModel
{
    LOG_CLASS(LLInventoryModel);

public:
    enum EHasChildren
    {
        CHILDREN_NO,
        CHILDREN_YES,
        CHILDREN_MAYBE
    };

    typedef std::vector<LLPointer<LLViewerInventoryCategory> > cat_array_t;
    typedef std::vector<LLPointer<LLViewerInventoryItem> > item_array_t;
    typedef std::set<LLUUID> changed_items_t;

    // Rider: This is using the old responder patter.  It should be refactored to
    // take advantage of coroutines.

    // HTTP handler for individual item requests (inventory or library).
    // Background item requests are derived from this in the background
    // inventory system.  All folder requests are also located there
    // but have their own handler derived from HttpHandler.
    class FetchItemHttpHandler : public LLCore::HttpHandler
    {
    public:
        LOG_CLASS(FetchItemHttpHandler);

        FetchItemHttpHandler(const LLSD & request_sd);
        virtual ~FetchItemHttpHandler();

    protected:
        FetchItemHttpHandler(const FetchItemHttpHandler &);             // Not defined
        void operator=(const FetchItemHttpHandler &);                   // Not defined

    public:
        virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response);

    private:
        void processData(LLSD & body, LLCore::HttpResponse * response);
        void processFailure(LLCore::HttpStatus status, LLCore::HttpResponse * response);
        void processFailure(const char * const reason, LLCore::HttpResponse * response);

    private:
        LLSD mRequestSD;
    };

/********************************************************************************
 **                                                                            **
 **                    INITIALIZATION/SETUP
 **/

    //--------------------------------------------------------------------
    // Constructors / Destructors
    //--------------------------------------------------------------------
public:
    LLInventoryModel();
    ~LLInventoryModel();
    void cleanupInventory();
protected:
    void empty(); // empty the entire contents

    //--------------------------------------------------------------------
    // Initialization
    //--------------------------------------------------------------------
public:
    // The inventory model usage is sensitive to the initial construction of the model
    bool isInventoryUsable() const;
private:
    bool mIsAgentInvUsable; // used to handle an invalid inventory state

    // One-time initialization of HTTP system.
    void initHttpRequest();

    //--------------------------------------------------------------------
    // Root Folders
    //--------------------------------------------------------------------
public:
    // The following are set during login with data from the server
    void setRootFolderID(const LLUUID& id);
    void setLibraryOwnerID(const LLUUID& id);
    void setLibraryRootFolderID(const LLUUID& id);

    const LLUUID &getRootFolderID() const;
    const LLUUID &getLibraryOwnerID() const;
    const LLUUID &getLibraryRootFolderID() const;
private:
    LLUUID mRootFolderID;
    LLUUID mLibraryRootFolderID;
    LLUUID mLibraryOwnerID;

    //--------------------------------------------------------------------
    // Structure
    //--------------------------------------------------------------------
public:
    // Methods to load up inventory skeleton & meat. These are used
    // during authentication. Returns true if everything parsed.
    bool loadSkeleton(const LLSD& options, const LLUUID& owner_id);
    void buildParentChildMap(); // brute force method to rebuild the entire parent-child relations
    void createCommonSystemCategories();

    static std::string getInvCacheAddres(const LLUUID& owner_id);

    // Call on logout to save a terse representation.
    void cache(const LLUUID& parent_folder_id, const LLUUID& agent_id);
private:
    // Information for tracking the actual inventory. We index this
    // information in a lot of different ways so we can access
    // the inventory using several different identifiers.
    // mInventory member data is the 'master' list of inventory, and
    // mCategoryMap and mItemMap store uuid->object mappings.
    typedef std::map<LLUUID, LLPointer<LLViewerInventoryCategory> > cat_map_t;
    typedef std::map<LLUUID, LLPointer<LLViewerInventoryItem> > item_map_t;
    cat_map_t mCategoryMap;
    item_map_t mItemMap;
    // This last set of indices is used to map parents to children.
    typedef std::map<LLUUID, cat_array_t*> parent_cat_map_t;
    typedef std::map<LLUUID, item_array_t*> parent_item_map_t;
    parent_cat_map_t mParentChildCategoryTree;
    parent_item_map_t mParentChildItemTree;

    // Track links to items and categories. We do not store item or
    // category pointers here, because broken links are also supported.
    typedef std::multimap<LLUUID, LLUUID> backlink_mmap_t;
    backlink_mmap_t mBacklinkMMap; // key = target_id: ID of item, values = link_ids: IDs of item or folder links referencing it.
    // For internal use only
    bool hasBacklinkInfo(const LLUUID& link_id, const LLUUID& target_id) const;
    void addBacklinkInfo(const LLUUID& link_id, const LLUUID& target_id);
    void removeBacklinkInfo(const LLUUID& link_id, const LLUUID& target_id);

    //--------------------------------------------------------------------
    // Login
    //--------------------------------------------------------------------
public:
    static bool getIsFirstTimeInViewer2();
    static bool  isSysFoldersReady() { return (sPendingSystemFolders == 0); }

private:
    static bool sFirstTimeInViewer2;
    const static S32 sCurrentInvCacheVersion; // expected inventory cache version

    static S32 sPendingSystemFolders;

/**                    Initialization/Setup
 **                                                                            **
 *******************************************************************************/

/********************************************************************************
 **                                                                            **
 **                    ACCESSORS
 **/

    //--------------------------------------------------------------------
    // Descendants
    //--------------------------------------------------------------------
public:
    // Make sure we have the descendants in the structure.  Returns true
    // if a fetch was performed.
    bool fetchDescendentsOf(const LLUUID& folder_id) const;

    // Return the direct descendants of the id provided.Set passed
    // in values to NULL if the call fails.
    //    NOTE: The array provided points straight into the guts of
    //    this object, and should only be used for read operations, since
    //    modifications may invalidate the internal state of the inventory.
    void getDirectDescendentsOf(const LLUUID& cat_id,
                                cat_array_t*& categories,
                                item_array_t*& items) const;
    void getDirectDescendentsOf(const LLUUID& cat_id, cat_array_t& categories, item_array_t& items, LLInventoryCollectFunctor& f) const;

    typedef LLUUID digest_t; // To clarify the actual usage of this "UUID"
    // Compute a hash of direct descendant names (for detecting child name changes)
    digest_t hashDirectDescendentNames(const LLUUID& cat_id) const;

    // Starting with the object specified, add its descendants to the
    // array provided, but do not add the inventory object specified
    // by id. There is no guaranteed order.
    //    NOTE: Neither array will be erased before adding objects to it.
    //    Do not store a copy of the pointers collected - use them, and
    //    collect them again later if you need to reference the same objects.
    enum {
        EXCLUDE_TRASH = false,
        INCLUDE_TRASH = true
    };
    // Simpler existence test if matches don't actually need to be collected.
    bool hasMatchingDirectDescendent(const LLUUID& cat_id,
                                     LLInventoryCollectFunctor& filter);
    void collectDescendents(const LLUUID& id,
                            cat_array_t& categories,
                            item_array_t& items,
                            bool include_trash);
    void collectDescendentsIf(const LLUUID& id,
                              cat_array_t& categories,
                              item_array_t& items,
                              bool include_trash,
                              LLInventoryCollectFunctor& add);

    // Collect all items in inventory that are linked to item_id.
    // Assumes item_id is itself not a linked item.
    item_array_t collectLinksTo(const LLUUID& item_id);

    // Check if one object has a parent chain up to the category specified by UUID.
    bool isObjectDescendentOf(const LLUUID& obj_id, const LLUUID& cat_id) const;

    enum EAncestorResult{
        ANCESTOR_OK = 0,
        ANCESTOR_MISSING = 1,
        ANCESTOR_LOOP = 2,
    };
    // Follow parent chain to the top.
    EAncestorResult getObjectTopmostAncestor(const LLUUID& object_id, LLUUID& result) const;

    //--------------------------------------------------------------------
    // Find
    //--------------------------------------------------------------------
public:

    // Checks if category exists ("My Inventory" only), if it does not, creates it
    void ensureCategoryForTypeExists(LLFolderType::EType preferred_type);

    const LLUUID findCategoryUUIDForTypeInRoot(
        LLFolderType::EType preferred_type,
        const LLUUID& root_id) const;

    // Returns the uuid of the category that specifies 'type' as what it
    // defaults to containing. The category is not necessarily only for that type.
    //    NOTE: If create_folder is true, this will create a new inventory category
    //    on the fly if one does not exist. *NOTE: if find_in_library is true it
    //    will search in the user's library folder instead of "My Inventory"
    const LLUUID findCategoryUUIDForType(LLFolderType::EType preferred_type) const;
    //    will search in the user's library folder instead of "My Inventory"
    const LLUUID findLibraryCategoryUUIDForType(LLFolderType::EType preferred_type) const;
    // Returns user specified category for uploads, returns default id if there are no
    // user specified one or it does not exist, creates default category if it is missing.
    const LLUUID findUserDefinedCategoryUUIDForType(LLFolderType::EType preferred_type) const;

    // Get whatever special folder this object is a child of, if any.
    const LLViewerInventoryCategory *getFirstNondefaultParent(const LLUUID& obj_id) const;

    // Get first descendant of the child object under the specified parent
    const LLViewerInventoryCategory *getFirstDescendantOf(const LLUUID& master_parent_id, const LLUUID& obj_id) const;

    // Get the object by id. Returns NULL if not found.
    //   NOTE: Use the pointer returned for read operations - do
    //   not modify the object values in place or you will break stuff.
    LLInventoryObject* getObject(const LLUUID& id) const;

    // Get the item by id. Returns NULL if not found.
    //    NOTE: Use the pointer for read operations - use the
    //    updateItem() method to actually modify values.
    LLViewerInventoryItem* getItem(const LLUUID& id) const;

    // Get the category by id. Returns NULL if not found.
    //    NOTE: Use the pointer for read operations - use the
    //    updateCategory() method to actually modify values.
    LLViewerInventoryCategory* getCategory(const LLUUID& id) const;

    // Get the inventoryID or item that this item points to, else just return object_id
    const LLUUID& getLinkedItemID(const LLUUID& object_id) const;
    LLViewerInventoryItem* getLinkedItem(const LLUUID& object_id) const;

    // Copy content of all folders of type "type" into folder "id" and delete/purge the empty folders
    // Note : This method has been designed for FT_OUTBOX (aka Merchant Outbox) but can be used for other categories
    void consolidateForType(const LLUUID& id, LLFolderType::EType type);

private:
    mutable LLPointer<LLViewerInventoryItem> mLastItem; // cache recent lookups

    //--------------------------------------------------------------------
    // Count
    //--------------------------------------------------------------------
public:
    // Return the number of items or categories
    S32 getItemCount() const;
    S32 getCategoryCount() const;

/**                    Accessors
 **                                                                            **
 *******************************************************************************/

/********************************************************************************
 **                                                                            **
 **                    MUTATORS
 **/

public:
    // Change an existing item with a matching item_id or add the item
    // to the current inventory. Returns the change mask generated by
    // the update. No notification will be sent to observers. This
    // method will only generate network traffic if the item had to be
    // reparented.
    //    NOTE: In usage, you will want to perform cache accounting
    //    operations in LLInventoryModel::accountForUpdate() or
    //    LLViewerInventoryItem::updateServer() before calling this method.
    U32 updateItem(const LLViewerInventoryItem* item, U32 mask = 0);

    // Change an existing item with the matching id or add
    // the category. No notification will be sent to observers. This
    // method will only generate network traffic if the item had to be
    // reparented.
    //    NOTE: In usage, you will want to perform cache accounting
    //    operations in accountForUpdate() or LLViewerInventoryCategory::
    //    updateServer() before calling this method.
    void updateCategory(const LLViewerInventoryCategory* cat, U32 mask = 0);

    // Move the specified object id to the specified category and
    // update the internal structures. No cache accounting,
    // observer notification, or server update is performed.
    void moveObject(const LLUUID& object_id, const LLUUID& cat_id);

    // Migrated from llinventoryfunctions
    void changeItemParent(LLViewerInventoryItem* item,
                          const LLUUID& new_parent_id,
                          bool restamp);

    // Migrated from llinventoryfunctions
    void changeCategoryParent(LLViewerInventoryCategory* cat,
                              const LLUUID& new_parent_id,
                              bool restamp);

    // Marks links from a "possibly" broken list for a rebuild
    // clears the list
    void rebuildBrockenLinks();
    bool hasPosiblyBrockenLinks() const { return mPossiblyBrockenLinks.size() > 0; }

    //--------------------------------------------------------------------
    // Delete
    //--------------------------------------------------------------------
public:

    // Update model after an item is confirmed as removed from
    // server. Works for categories or items.
    void onObjectDeletedFromServer(const LLUUID& item_id,
                                   bool fix_broken_links = true,
                                   bool update_parent_version = true,
                                   bool do_notify_observers = true);

    // Update model after all descendants removed from server.
    void onDescendentsPurgedFromServer(const LLUUID& object_id, bool fix_broken_links = true);

    // Update model after an existing item gets updated on server.
    void onItemUpdated(const LLUUID& item_id, const LLSD& updates, bool update_parent_version);

    // Update model after an existing category gets updated on server.
    void onCategoryUpdated(const LLUUID& cat_id, const LLSD& updates);

    // Delete a particular inventory object by ID. Will purge one
    // object from the internal data structures, maintaining a
    // consistent internal state. No cache accounting, observer
    // notification, or server update is performed.
    void deleteObject(const LLUUID& id, bool fix_broken_links = true, bool do_notify_observers = true);
    /// move Item item_id to Trash
    void removeItem(const LLUUID& item_id);
    /// move Category category_id to Trash
    void removeCategory(const LLUUID& category_id);
    /// removeItem() or removeCategory(), whichever is appropriate
    void removeObject(const LLUUID& object_id);

    // "TrashIsFull" when trash exceeds maximum capacity
    void checkTrashOverflow();

protected:
    void rebuildLinkItems(LLInventoryModel::item_array_t& items);

    //--------------------------------------------------------------------
    // Reorder
    //--------------------------------------------------------------------
public:
    // Changes items order by insertion of the item identified by src_item_id
    // before (or after) the item identified by dest_item_id. Both items must exist in items array.
    // Sorting is stored after method is finished. Only src_item_id is moved before (or after) dest_item_id.
    // The parameter "insert_before" controls on which side of dest_item_id src_item_id gets reinserted.
    static void updateItemsOrder(LLInventoryModel::item_array_t& items,
                                 const LLUUID& src_item_id,
                                 const LLUUID& dest_item_id,
                                 bool insert_before = true);
    // Gets an iterator on an item vector knowing only the item UUID.
    // Returns end() of the vector if not found.
    static LLInventoryModel::item_array_t::iterator findItemIterByUUID(LLInventoryModel::item_array_t& items, const LLUUID& id);


    // Rearranges Landmarks inside Favorites folder.
    // Moves source landmark before target one.
    void rearrangeFavoriteLandmarks(const LLUUID& source_item_id, const LLUUID& target_item_id);
    //void saveItemsOrder(const LLInventoryModel::item_array_t& items);

    //--------------------------------------------------------------------
    // Creation
    //--------------------------------------------------------------------
public:
    // Returns the UUID of the new category. If you want to use the default
    // name based on type, pass in a NULL to the 'name' parameter.
    void createNewCategory(const LLUUID& parent_id,
                             LLFolderType::EType preferred_type,
                             const std::string& name,
                             inventory_func_type callback = NULL,
                             const LLUUID& thumbnail_id = LLUUID::null);
protected:
    // Internal methods that add inventory and make sure that all of
    // the internal data structures are consistent. These methods
    // should be passed pointers of newly created objects, and the
    // instance will take over the memory management from there.
    void addCategory(LLViewerInventoryCategory* category);
    void addItem(LLViewerInventoryItem* item);

    void createNewCategoryCoro(std::string url, LLSD postData, inventory_func_type callback);

/**                    Mutators
 **                                                                            **
 *******************************************************************************/

/********************************************************************************
 **                                                                            **
 **                    CATEGORY ACCOUNTING
 **/

public:
    // Represents the number of items added or removed from a category.
    struct LLCategoryUpdate
    {
        LLCategoryUpdate() : mDescendentDelta(0), mChangeVersion(true) {}
        LLCategoryUpdate(const LLUUID& category_id, S32 delta, bool change_version = true) :
            mCategoryID(category_id),
            mDescendentDelta(delta),
            mChangeVersion(change_version) {}
        LLUUID mCategoryID;
        S32 mDescendentDelta;
        bool mChangeVersion;
    };
    typedef std::vector<LLCategoryUpdate> update_list_t;

    // This exists to make it easier to account for deltas in a map.
    struct LLInitializedS32
    {
        LLInitializedS32() : mValue(0) {}
        LLInitializedS32(S32 value) : mValue(value) {}
        S32 mValue;
        LLInitializedS32& operator++() { ++mValue; return *this; }
        LLInitializedS32& operator--() { --mValue; return *this; }
    };
    typedef std::map<LLUUID, LLInitializedS32> update_map_t;

    // Call when there are category updates.  Call them *before* the
    // actual update so the method can do descendent accounting correctly.
    void accountForUpdate(const LLCategoryUpdate& update) const;
    void accountForUpdate(const update_list_t& updates) const;
    void accountForUpdate(const update_map_t& updates) const;

    // Return (yes/no/maybe) child status of category children.
    EHasChildren categoryHasChildren(const LLUUID& cat_id) const;

    // Returns true if category version is known and theoretical
    // descendents == actual descendents.
    bool isCategoryComplete(const LLUUID& cat_id) const;

/**                    Category Accounting
 **                                                                            **
 *******************************************************************************/

/********************************************************************************
 **                                                                            **
 **                    NOTIFICATIONS
 **/

public:
    // Called by the idle loop.  Only updates if new state is detected.  Call
    // notifyObservers() manually to update regardless of whether state change
    // has been indicated.
    void idleNotifyObservers();

    // Call to explicitly update everyone on a new state.
    void notifyObservers();

    // Allows outsiders to tell the inventory if something has
    // been changed 'under the hood', but outside the control of the
    // inventory. The next notify will include that notification.
    void addChangedMask(U32 mask, const LLUUID& referent);

    const changed_items_t& getChangedIDs() const { return mChangedItemIDs; }
    const changed_items_t& getAddedIDs() const { return mAddedItemIDs; }
protected:
    // Updates all linked items pointing to this id.
    void addChangedMaskForLinks(const LLUUID& object_id, U32 mask);
private:
    // Flag set when notifyObservers is being called, to look for bugs
    // where it's called recursively.
    bool mIsNotifyObservers;
    // Variables used to track what has changed since the last notify.
    U32 mModifyMask;
    changed_items_t mChangedItemIDs;
    changed_items_t mAddedItemIDs;
    // Fallback when notifyObservers is in progress
    U32 mModifyMaskBacklog;
    changed_items_t mChangedItemIDsBacklog;
    changed_items_t mAddedItemIDsBacklog;
    typedef std::map<LLUUID , changed_items_t> broken_links_t;
    broken_links_t mPossiblyBrockenLinks; // there can be multiple links per item
    changed_items_t mLinksRebuildList;
    boost::signals2::connection mBulkFecthCallbackSlot;


    //--------------------------------------------------------------------
    // Observers
    //--------------------------------------------------------------------
public:
    // If the observer is destroyed, be sure to remove it.
    void addObserver(LLInventoryObserver* observer);
    void removeObserver(LLInventoryObserver* observer);
    bool containsObserver(LLInventoryObserver* observer) const;
private:
    typedef std::set<LLInventoryObserver*> observer_list_t;
    observer_list_t mObservers;

/**                    Notifications
 **                                                                            **
 *******************************************************************************/


/********************************************************************************
 **                                                                            **
 **                    HTTP Transport
 **/
public:
    // Invoke handler completion method (onCompleted) for all
    // requests that are ready.
    void handleResponses(bool foreground);

    // Request an inventory HTTP operation to either the
    // foreground or background processor.  These are actually
    // the same service queue but the background requests are
    // seviced more slowly effectively de-prioritizing new
    // requests.
    LLCore::HttpHandle requestPost(bool foreground,
                                   const std::string & url,
                                   const LLSD & body,
                                   const LLCore::HttpHandler::ptr_t &handler,
                                   const char * const message);

private:
    // Usual plumbing for LLCore:: HTTP operations.
    LLCore::HttpRequest *               mHttpRequestFG;
    LLCore::HttpRequest *               mHttpRequestBG;
    LLCore::HttpOptions::ptr_t          mHttpOptions;
    LLCore::HttpHeaders::ptr_t          mHttpHeaders;
    LLCore::HttpRequest::policy_t       mHttpPolicyClass;

/**                    HTTP Transport
 **                                                                            **
 *******************************************************************************/


/********************************************************************************
 **                                                                            **
 **                    MISCELLANEOUS
 **/

    //--------------------------------------------------------------------
    // Callbacks
    //--------------------------------------------------------------------
public:
    // Trigger a notification and empty the folder type (FT_TRASH or FT_LOST_AND_FOUND) if confirmed
    void emptyFolderType(const std::string notification, LLFolderType::EType folder_type);
    bool callbackEmptyFolderType(const LLSD& notification, const LLSD& response, LLFolderType::EType preferred_type);
    static void registerCallbacks(LLMessageSystem* msg);

    //--------------------------------------------------------------------
    // File I/O
    //--------------------------------------------------------------------
protected:
    static bool loadFromFile(const std::string& filename,
                             cat_array_t& categories,
                             item_array_t& items,
                             changed_items_t& cats_to_update,
                             bool& is_cache_obsolete);
    static bool saveToFile(const std::string& filename,
                           const cat_array_t& categories,
                           const item_array_t& items);

    //--------------------------------------------------------------------
    // Message handling functionality
    //--------------------------------------------------------------------
public:
    static void processUpdateCreateInventoryItem(LLMessageSystem* msg, void**);
    static void removeInventoryItem(LLUUID agent_id, LLMessageSystem* msg, const char* msg_label);
    static void processRemoveInventoryItem(LLMessageSystem* msg, void**);
    static void removeInventoryFolder(LLUUID agent_id, LLMessageSystem* msg);
    static void processRemoveInventoryFolder(LLMessageSystem* msg, void**);
    static void processRemoveInventoryObjects(LLMessageSystem* msg, void**);
    static void processSaveAssetIntoInventory(LLMessageSystem* msg, void**);
    static void processBulkUpdateInventory(LLMessageSystem* msg, void**);
    static void processMoveInventoryItem(LLMessageSystem* msg, void**);
protected:
    bool messageUpdateCore(LLMessageSystem* msg, bool do_accounting, U32 mask = 0x0);

    //--------------------------------------------------------------------
    // Locks
    //--------------------------------------------------------------------
public:
    void lockDirectDescendentArrays(const LLUUID& cat_id,
                                    cat_array_t*& categories,
                                    item_array_t*& items);
    void unlockDirectDescendentArrays(const LLUUID& cat_id);
protected:
    cat_array_t* getUnlockedCatArray(const LLUUID& id);
    item_array_t* getUnlockedItemArray(const LLUUID& id);
private:
    std::map<LLUUID, bool> mCategoryLock;
    std::map<LLUUID, bool> mItemLock;

    //--------------------------------------------------------------------
    // Debugging
    //--------------------------------------------------------------------
public:
    void dumpInventory() const;
    LLPointer<LLInventoryValidationInfo> validate() const;
    LLPointer<LLInventoryValidationInfo> mValidationInfo;
    std::string getFullPath(const LLInventoryObject *obj) const;

/**                    Miscellaneous
 **                                                                            **
 *******************************************************************************/
};

// a special inventory model for the agent
extern LLInventoryModel gInventory;

#endif // LL_LLINVENTORYMODEL_H