/**
 * @file llfolderviewmodel.h
 *
 * $LicenseInfo:firstyear=2001&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 LLFOLDERVIEWMODEL_H
#define LLFOLDERVIEWMODEL_H

#include "llfontgl.h"   // just for StyleFlags enum
#include "llfolderview.h"

// These are grouping of inventory types.
// Order matters when sorting system folders to the top.
enum EInventorySortGroup
{
    SG_SYSTEM_FOLDER,
    SG_TRASH_FOLDER,
    SG_NORMAL_FOLDER,
    SG_ITEM
};

class LLFontGL;
class LLInventoryModel;
class LLMenuGL;
class LLUIImage;
class LLUUID;
class LLFolderViewItem;
class LLFolderViewFolder;

class LLFolderViewFilter
{
public:
    enum EFilterModified
    {
        FILTER_NONE,                // nothing to do, already filtered
        FILTER_RESTART,             // restart filtering from scratch
        FILTER_LESS_RESTRICTIVE,    // existing filtered items will certainly pass this filter
        FILTER_MORE_RESTRICTIVE     // if you didn't pass the previous filter, you definitely won't pass this one
    };

public:

    LLFolderViewFilter() {}
    virtual ~LLFolderViewFilter() {}

    // +-------------------------------------------------------------------+
    // + Execution And Results
    // +-------------------------------------------------------------------+
    virtual bool                check(const LLFolderViewModelItem* item) = 0;
    virtual bool                checkFolder(const LLFolderViewModelItem* folder) const = 0;

    virtual void                setEmptyLookupMessage(const std::string& message) = 0;
    virtual std::string         getEmptyLookupMessage(bool is_empty_folder = false) const = 0;

    virtual bool                showAllResults() const = 0;

    virtual std::string::size_type getStringMatchOffset(LLFolderViewModelItem* item) const = 0;
    virtual std::string::size_type getFilterStringSize() const = 0;
    // +-------------------------------------------------------------------+
    // + Status
    // +-------------------------------------------------------------------+
    virtual bool                isActive() const = 0;
    virtual bool                isModified() const = 0;
    virtual void                clearModified() = 0;
    virtual const std::string&  getName() const = 0;
    virtual const std::string&  getFilterText() = 0;
    //RN: this is public to allow system to externally force a global refilter
    virtual void                setModified(EFilterModified behavior = FILTER_RESTART) = 0;

    // +-------------------------------------------------------------------+
    // + Time
    // +-------------------------------------------------------------------+
    virtual void                resetTime(S32 timeout) = 0;
    virtual bool                isTimedOut() = 0;

    // +-------------------------------------------------------------------+
    // + Default
    // +-------------------------------------------------------------------+
    virtual bool                isDefault() const = 0;
    virtual bool                isNotDefault() const = 0;
    virtual void                markDefault() = 0;
    virtual void                resetDefault() = 0;

    // +-------------------------------------------------------------------+
    // + Generation
    // +-------------------------------------------------------------------+
    virtual S32                 getCurrentGeneration() const = 0;
    virtual S32                 getFirstSuccessGeneration() const = 0;
    virtual S32                 getFirstRequiredGeneration() const = 0;
};

class LLFolderViewModelInterface
{
public:
    LLFolderViewModelInterface()
    {}

    virtual ~LLFolderViewModelInterface() {}
    virtual void requestSortAll() = 0;

    virtual void sort(class LLFolderViewFolder*) = 0;
    virtual void filter() = 0;

    virtual bool contentsReady() = 0;
    virtual bool isFolderComplete(class LLFolderViewFolder*) = 0;
    virtual void setFolderView(LLFolderView* folder_view) = 0;
    virtual LLFolderViewFilter& getFilter() = 0;
    virtual const LLFolderViewFilter& getFilter() const = 0;
    virtual std::string getStatusText(bool is_empty_folder = false) = 0;

    virtual bool startDrag(std::vector<LLFolderViewModelItem*>& items) = 0;
};

// This is an abstract base class that users of the folderview classes
// would use to bridge the folder view with the underlying data
class LLFolderViewModelItem : public LLRefCount
{
public:
    LLFolderViewModelItem()
    {}

    virtual ~LLFolderViewModelItem() { }

    virtual void update() {}    //called when drawing
    virtual const std::string& getName() const = 0;
    virtual const std::string& getDisplayName() const = 0;
    virtual const std::string& getSearchableName() const = 0;

    virtual std::string getSearchableDescription() const = 0;
    virtual std::string getSearchableCreatorName()const = 0;
    virtual std::string getSearchableUUIDString() const = 0;

    virtual LLPointer<LLUIImage> getIcon() const = 0;
    virtual LLPointer<LLUIImage> getIconOpen() const { return getIcon(); }
    virtual LLPointer<LLUIImage> getIconOverlay() const { return NULL; }

    virtual LLFontGL::StyleFlags getLabelStyle() const = 0;
    virtual std::string getLabelSuffix() const = 0;

    virtual void openItem( void ) = 0;
    virtual void closeItem( void ) = 0;
    virtual void selectItem(void) = 0;

    virtual void navigateToFolder(bool new_window = false, bool change_mode = false) = 0;

    virtual bool isItemWearable() const { return false; }

    virtual bool isItemRenameable() const = 0;
    virtual bool renameItem(const std::string& new_name) = 0;

    virtual bool isItemMovable( void ) const = 0;       // Can be moved to another folder
    virtual void move( LLFolderViewModelItem* parent_listener ) = 0;

    virtual bool isItemRemovable( bool check_worn = true ) const = 0;       // Can be destroyed
    virtual bool removeItem() = 0;
    virtual void removeBatch(std::vector<LLFolderViewModelItem*>& batch) = 0;

    virtual bool isItemCopyable(bool can_copy_as_link = true) const = 0;
    virtual bool copyToClipboard() const = 0;
    virtual bool cutToClipboard() = 0;
    virtual bool isCutToClipboard() { return false; };

    virtual bool isClipboardPasteable() const = 0;
    virtual void pasteFromClipboard() = 0;
    virtual void pasteLinkFromClipboard() = 0;

    virtual void buildContextMenu(LLMenuGL& menu, U32 flags) = 0;

    virtual bool potentiallyVisible() = 0; // is the item definitely visible or we haven't made up our minds yet?

    virtual bool filter( LLFolderViewFilter& filter) = 0;
    virtual bool passedFilter(S32 filter_generation = -1) = 0;
    virtual bool descendantsPassedFilter(S32 filter_generation = -1) = 0;
    virtual void setPassedFilter(bool passed, S32 filter_generation, std::string::size_type string_offset = std::string::npos, std::string::size_type string_size = 0) = 0;
    virtual void setPassedFolderFilter(bool passed, S32 filter_generation) = 0;
    virtual void dirtyFilter() = 0;
    virtual void dirtyDescendantsFilter() = 0;
    virtual bool hasFilterStringMatch() = 0;
    virtual std::string::size_type getFilterStringOffset() = 0;
    virtual std::string::size_type getFilterStringSize() = 0;

    virtual S32 getLastFilterGeneration() const = 0;
    virtual S32 getMarkedDirtyGeneration() const = 0;

    virtual bool hasChildren() const = 0;
    virtual void addChild(LLFolderViewModelItem* child) = 0;
    virtual void removeChild(LLFolderViewModelItem* child) = 0;
    virtual void clearChildren() = 0;

    // This method will be called to determine if a drop can be
    // performed, and will set drop to true if a drop is
    // requested. Returns true if a drop is possible/happened,
    // otherwise false.
    virtual bool dragOrDrop(MASK mask, bool drop,
                            EDragAndDropType cargo_type,
                            void* cargo_data,
                            std::string& tooltip_msg) = 0;

    virtual void requestSort() = 0;
    virtual S32 getSortVersion() = 0;
    virtual void setSortVersion(S32 version) = 0;
    virtual void setParent(LLFolderViewModelItem* parent) = 0;
    virtual bool hasParent() = 0;

protected:

    friend class LLFolderViewItem;
    virtual void setFolderViewItem(LLFolderViewItem* folder_view_item) = 0;

};


class LLFolderViewModelItemCommon : public LLFolderViewModelItem
{
public:
    LLFolderViewModelItemCommon(LLFolderViewModelInterface& root_view_model)
    :   mSortVersion(-1),
        mPassedFilter(true),
        mPassedFolderFilter(true),
        mStringMatchOffsetFilter(std::string::npos),
        mStringFilterSize(0),
        mFolderViewItem(NULL),
        mLastFilterGeneration(-1),
        mLastFolderFilterGeneration(-1),
        mMarkedDirtyGeneration(-1),
        mMostFilteredDescendantGeneration(-1),
        mParent(NULL),
        mRootViewModel(root_view_model)
    {
        mChildren.clear();
    }

    void requestSort() { mSortVersion = -1; }
    S32 getSortVersion() { return mSortVersion; }
    void setSortVersion(S32 version) { mSortVersion = version;}

    S32 getLastFilterGeneration() const { return mLastFilterGeneration; }
    S32 getLastFolderFilterGeneration() const { return mLastFolderFilterGeneration; }
    S32 getMarkedDirtyGeneration() const { return mMarkedDirtyGeneration; }
    void dirtyFilter()
    {
        if(mMarkedDirtyGeneration < 0)
        {
            mMarkedDirtyGeneration = mLastFilterGeneration;
        }
        mLastFilterGeneration = -1;
        mLastFolderFilterGeneration = -1;

        // bubble up dirty flag all the way to root
        if (mParent)
        {
            mParent->dirtyFilter();
        }
    }
    void dirtyDescendantsFilter()
    {
        mMostFilteredDescendantGeneration = -1;
        if (mParent)
        {
            mParent->dirtyDescendantsFilter();
        }
    }
    bool hasFilterStringMatch();
    std::string::size_type getFilterStringOffset();
    std::string::size_type getFilterStringSize();

    typedef std::list<LLFolderViewModelItem*> child_list_t;

    virtual void addChild(LLFolderViewModelItem* child)
    {
        mChildren.push_back(child);
        child->setParent(this);
        dirtyFilter();
        requestSort();
    }

    virtual void removeChild(LLFolderViewModelItem* child)
    {
        mChildren.remove(child);
        child->setParent(NULL);
        dirtyDescendantsFilter();
        dirtyFilter();
    }

    virtual void clearChildren()
    {
        // We are working with models that belong to views as LLPointers, clean the list, let poiters handle the rest
        std::for_each(mChildren.begin(), mChildren.end(), [](LLFolderViewModelItem* c) {c->setParent(NULL); });
        mChildren.clear();
        dirtyDescendantsFilter();
        dirtyFilter();
    }

    child_list_t::const_iterator getChildrenBegin() const { return mChildren.begin(); }
    child_list_t::const_iterator getChildrenEnd() const { return mChildren.end(); }
    child_list_t::size_type getChildrenCount() const { return mChildren.size(); }

    void setPassedFilter(bool passed, S32 filter_generation, std::string::size_type string_offset = std::string::npos, std::string::size_type string_size = 0)
    {
        mPassedFilter = passed;
        mLastFilterGeneration = filter_generation;
        mStringMatchOffsetFilter = string_offset;
        mStringFilterSize = string_size;
        mMarkedDirtyGeneration = -1;
    }

    void setPassedFolderFilter(bool passed, S32 filter_generation)
    {
        mPassedFolderFilter = passed;
        mLastFolderFilterGeneration = filter_generation;
    }

    virtual bool potentiallyVisible()
    {
        return passedFilter() // we've passed the filter
            || (getLastFilterGeneration() < mRootViewModel.getFilter().getFirstSuccessGeneration()) // or we don't know yet
            || descendantsPassedFilter();
    }

    virtual bool passedFilter(S32 filter_generation = -1)
    {
        if (filter_generation < 0)
        {
            filter_generation = mRootViewModel.getFilter().getFirstSuccessGeneration();
        }
        bool passed_folder_filter = mPassedFolderFilter && (mLastFolderFilterGeneration >= filter_generation);
        bool passed_filter = mPassedFilter && (mLastFilterGeneration >= filter_generation);
        return passed_folder_filter && (passed_filter || descendantsPassedFilter(filter_generation));
    }

    virtual bool descendantsPassedFilter(S32 filter_generation = -1)
    {
        if (filter_generation < 0)
        {
            filter_generation = mRootViewModel.getFilter().getFirstSuccessGeneration();
        }
        return mMostFilteredDescendantGeneration >= filter_generation;
    }


protected:
    virtual void setParent(LLFolderViewModelItem* parent) { mParent = parent; }
    virtual bool hasParent() { return mParent != NULL; }

    S32                         mSortVersion;
    bool                        mPassedFilter;
    bool                        mPassedFolderFilter;
    std::string::size_type      mStringMatchOffsetFilter;
    std::string::size_type      mStringFilterSize;

    S32                         mLastFilterGeneration,
                                mLastFolderFilterGeneration,
                                mMostFilteredDescendantGeneration,
                                mMarkedDirtyGeneration;

    child_list_t                mChildren;
    LLFolderViewModelItem*      mParent;
    LLFolderViewModelInterface& mRootViewModel;

    void setFolderViewItem(LLFolderViewItem* folder_view_item) { mFolderViewItem = folder_view_item;}
    LLFolderViewItem*       mFolderViewItem;
};



class LLFolderViewModelCommon : public LLFolderViewModelInterface
{
public:
    LLFolderViewModelCommon()
    :   mTargetSortVersion(0),
        mFolderView(NULL)
    {}

    virtual void requestSortAll()
    {
        // sort everything
        mTargetSortVersion++;
    }
    virtual std::string getStatusText(bool is_empty_folder = false);
    virtual void filter();

    void setFolderView(LLFolderView* folder_view) { mFolderView = folder_view;}

protected:
    bool needsSort(class LLFolderViewModelItem* item);

    S32 mTargetSortVersion;
    LLFolderView* mFolderView;

};

template <typename SORT_TYPE, typename ITEM_TYPE, typename FOLDER_TYPE, typename FILTER_TYPE>
class LLFolderViewModel : public LLFolderViewModelCommon
{
public:
    typedef SORT_TYPE       SortType;
    typedef ITEM_TYPE       ItemType;
    typedef FOLDER_TYPE     FolderType;
    typedef FILTER_TYPE     FilterType;

    LLFolderViewModel(SortType* sorter, FilterType* filter)
    :   mSorter(sorter),
        mFilter(filter)
    {}

    virtual ~LLFolderViewModel() {}

    virtual SortType& getSorter()                    { return *mSorter; }
    virtual const SortType& getSorter() const        { return *mSorter; }
    virtual void setSorter(const SortType& sorter)   { mSorter.reset(new SortType(sorter)); requestSortAll(); }

    virtual FilterType& getFilter()                  { return *mFilter; }
    virtual const FilterType& getFilter() const      { return *mFilter; }
    virtual void setFilter(const FilterType& filter) { mFilter.reset(new FilterType(filter)); }

    // By default, we assume the content is available. If a network fetch mechanism is implemented for the model,
    // this method needs to be overloaded and return the relevant fetch status.
    virtual bool contentsReady()                    { return true; }
    virtual bool isFolderComplete(LLFolderViewFolder* folder)                   { return true; }

    struct ViewModelCompare
    {
        ViewModelCompare(const SortType& sorter)
        :   mSorter(sorter)
        {}

        bool operator () (const LLFolderViewItem* a, const LLFolderViewItem* b) const
        {
            return mSorter(static_cast<const ItemType*>(a->getViewModelItem()), static_cast<const ItemType*>(b->getViewModelItem()));
        }

        bool operator () (const LLFolderViewFolder* a, const LLFolderViewFolder* b) const
        {
            return mSorter(static_cast<const ItemType*>(a->getViewModelItem()), static_cast<const ItemType*>(b->getViewModelItem()));
        }

        const SortType& mSorter;
    };

    void sort(LLFolderViewFolder* folder)
    {
        if (needsSort(folder->getViewModelItem()))
        {
            folder->sortFolders(ViewModelCompare(getSorter()));
            folder->sortItems(ViewModelCompare(getSorter()));
            folder->getViewModelItem()->setSortVersion(mTargetSortVersion);
            folder->requestArrange();
        }
    }

protected:
    std::unique_ptr<SortType>       mSorter;
    std::unique_ptr<FilterType>     mFilter;
};

#endif // LLFOLDERVIEWMODEL_H