/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/

#pragma once
#ifndef CRYINCLUDE_EDITORUI_QT_LIBRARY_TREEVIEW_H
#define CRYINCLUDE_EDITORUI_QT_LIBRARY_TREEVIEW_H

#include "api.h"
#include "Utils.h"

#include <AzQtComponents/Components/Style.h>

#include <QTreeWidget>
#include <functional>
#include <QPen>
#include <QPainter>
#include <QMap>
#include <QProxyStyle>

//TreeView Drop Indicator Color
#define CTreeViewDropIndicatorColor  0x1b75cf

#ifndef VALID_LIBRARY_ITEM_CHARACTERS
#define VALID_LIBRARY_ITEM_CHARACTERS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890_."
#endif

#define VALID_LIBRARY_CHARACTERS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890_"

#define LIBRARY_TREEVIEW_NAME_COLUMN 0
#define LIBRARY_TREEVIEW_INDICATOR_COLUMN 1

struct IDataBaseLibrary;
class CBaseLibraryItem;
class CLibraryTreeViewItem;
class ContextMenu;

class CLibraryTreeViewExpandState;
class EDITOR_QT_UI_API CLibraryTreeView
    : public QTreeWidget
{
    Q_OBJECT
public:
    CLibraryTreeView(QWidget* parent, IDataBaseLibrary* library);
    virtual ~CLibraryTreeView();

    QString GetLibName();

    void enableReordering();
    void fillFromLibrary();
    void refreshActiveState();
    QString storeExpandState();
    void restoreExpandState(const QString& data);
    void StartRename(CLibraryTreeViewItem* item, const QString& forceNameSelected = "", const bool overrideSafety = false);
    void EndRename();
    QString getItemPath(QTreeWidgetItem* item, bool includeLibrary = false);

    void ExpandItem(CLibraryTreeViewItem* item);
    void CollapseItem(CLibraryTreeViewItem* item);
    QModelIndex getIndexFromItem(QTreeWidgetItem* item);
    CLibraryTreeViewItem* getItemFromIndex(const QModelIndex& item);
    CLibraryTreeViewItem* getItemFromName(const QString& name);
    QVector<CLibraryTreeViewItem*> GetSelectedItems();
    QVector<CBaseLibraryItem*> GetChildrenOfItem(const QString& path);
    QVector<CBaseLibraryItem*> GetSelectedItemsWithChildren();
    QVector<CLibraryTreeViewItem*> GetChildrenOfTreeItem(const QString& path);
    QVector<CLibraryTreeViewItem*> GetSelectedTreeItemsWithChildren();
    CLibraryTreeViewItem* GetFirstSelectedItem();
    //pass "" to clear selection
    void SelectItem(QString pathWithoutLibrary, bool forceSelection = false);
    void UpdateColors(const QColor& enabledColor, const QColor& disabledColor);
    void setReloading(bool val);
    void SetLibrary(IDataBaseLibrary* lib);

    /// Returns true if the given library item name and item full-name are valid.
    /// \param itemName The name of the single item. Corresponds to a single folder's name or single leaf item's name. Ex: "myItem".
    /// \param itemPath The path of the item, including any prefixes for parent folders and the item name itself (but not including the library name). Ex: "folderA.folderB.myItem".
    /// \param item     The item being renamed.
    bool ValidateItemName(const QString& itemName, const QString& itemPath, const CBaseLibraryItem* item);

    //tree view inherits from scroll area so it's size calculations are hard coded and ignores content size
    //this function will calculate the size of the contents
    QSize GetSizeOfContents();
    bool IsLibraryModified();

    bool ApplyFilter(const QString& filter);
    void SetAllItemsEnabled(const bool& enabled = true);

    void StoreSelectionData();
    void RestoreSelectionData();
    //made public to allow dockablelibrarytreeview to request the context menu
    void OnMenuRequested(const QPoint& pos);
    void DropMetaData(QDropEvent* event, bool dropToLocation = true);
    virtual void dropEvent(QDropEvent* event);
    void UpdateItemStyle(CLibraryTreeViewItem* item);
    // This function could only used to reorder item with the same parent. Hierarchy issue must
    // be taken care before this function get called.
    void ReorderItem(CBaseLibraryItem* neighbor, CBaseLibraryItem* item, DropIndicatorPosition position);

signals:
    void SignalTreeViewEmpty();
    void SignalItemSelected(CBaseLibraryItem* selectedItem);
    void SignalItemReselected(CBaseLibraryItem* selectedItem);
    void SignalItemAboutToBeRenamed(CBaseLibraryItem* item, const QString& currentName, const QString& nextName, bool& proceed);
    void SignalItemRenamed(CBaseLibraryItem* item, const QString& oldName, const QString& currentName, const QString newLib = "");
    void SignalItemAboutToBeDragged(CLibraryTreeViewItem* item);
    void SignalDragOperationFinished();
    void SignalTreeFilledFromLibrary(IDataBaseLibrary* lib, CLibraryTreeView* view);
    void SignalPopulateItemContextMenu(CLibraryTreeViewItem* focusedItem, ContextMenu* toAddTo);
    void SignalItemEnableStateChanged(CBaseLibraryItem* item, const bool& state);
    void SignalPopulateSelectionList(QVector<CLibraryTreeViewItem*>& listOut);
    void SignalStartDrag(QDrag* drag, Qt::DropActions supportedActions);
    void SignalCheckLod(CBaseLibraryItem* item, bool& hasLod);
    void SignalMoveItem(const QString& destPath, QString selections, CBaseLibraryItem*& newItem);
    void SignalCopyItem(IDataBaseItem*);
    void SignalPasteSelected(IDataBaseItem* item, bool overrideSelection);
    void SignalUpdateTabName(const QString& fullOriginalName, const QString& fullNewName);

protected:
    virtual bool event(QEvent* e);
    virtual void startDrag(Qt::DropActions supportedActions);
    virtual bool RenameItem(const QString& oldName, const QString& nextName);

private:
    void OnCurrentItemChanged(QTreeWidgetItem* item, QTreeWidgetItem* previous);
    void OnSelectionChanged(); //keeps the active item cache up to date
    void OnRowInserted(const QModelIndex& parentIndex, int first, int last);
    void OnItemChanged(QTreeWidgetItem* item, int column);

    QString GetPathFromItemFullName(const QString& name);

    //Help function to grab final item name for drag/drop
    void GetFinalItemName(QString& finalItemName, CBaseLibraryItem* libItem, CBaseLibraryItem* dropLibItem, const QVector<QString>& parents);

    virtual void mousePressEvent(QMouseEvent* event) override;
    virtual void mouseMoveEvent(QMouseEvent* event) override;
    virtual void mouseReleaseEvent(QMouseEvent* event) override;

    virtual void leaveEvent(QEvent* event) override;
    QString GetReverseMatchedStrings(const QString& a, const QString& b);

    virtual QStringList mimeTypes() const override;

    virtual void dragEnterEvent(QDragEnterEvent* e) override;
    
    //After drag/drop operation. The order of particle item in this view may change and same as the items in particle's baseDataLibrary.
    //But the order of children in IParticleEffect won't match the new order. We are using this patch function to sync the order in IParticleEffect with particle library.
    void FixParticleEffectOrder(IDataBaseLibrary* lib);

public: 
    // The supported MIME type name of a drag & drop object. Used for filtering.
    const QString MIMEType = "LibraryTreeViewItem";
private:
    AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
    QMap<QString, QTreeWidgetItem*> m_nameToNode;
    IDataBaseLibrary* m_baseLibrary;
    QIcon* m_iconFolderClosed;
    QIcon* m_iconFolderOpen;
    QIcon* m_iconShowLOD;
    QIcon* m_iconIsGroup;
    QIcon* m_iconGroupWithLOD;
    QIcon* m_iconEmpty;
    //item selection doesn't always survive changes
    //added this list to track what is selected when a change happens
    QVector<CLibraryTreeViewItem*> m_activeItems;
    AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
    //used during rename and reloading to prevent problematic code
    bool m_allowSelection;
    bool m_isInDrop;
    //used for the "draggable" checkbox
    Qt::CheckState m_lastCheckboxState;
    bool m_amSettingCheckboxStates;
    QColor m_enabledItemTextColor;
    QColor m_disabledItemTextColor;
    QString m_filter;
    QString m_cachedSelectonData; //used for storeselectiondata/restoreselectiondata
};

class dropIndicatorStyle
    : public QProxyStyle
{
public:
    explicit dropIndicatorStyle(QStyle* baseStyle = nullptr)
        : QProxyStyle()
    {
        if (baseStyle)
        {
            AzQtComponents::Style::fixProxyStyle(this, baseStyle);
        }
    }

    void drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const
    {
        // FIXME: Move to AzQtComponents Style.cpp
        if (element == QStyle::PE_IndicatorItemViewItemDrop)
        {
            painter->setPen(QPen(QColor(CTreeViewDropIndicatorColor), 2));
        }
        QProxyStyle::drawPrimitive(element, option, painter, widget);
    }
};
#endif // CRYINCLUDE_EDITORUI_QT_LIBRARY_TREEVIEW_H