/*
 * Copyright (c) Contributors to the Open 3D Engine Project.
 * For complete copyright and license terms please see the LICENSE at the root of this distribution.
 *
 * SPDX-License-Identifier: Apache-2.0 OR MIT
 *
 */



#include "EditorDefs.h"

#include "BaseLibraryManager.h"

// Editor
#include "BaseLibraryItem.h"
#include "ErrorReport.h"
#include "Undo/IUndoObject.h"


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Undo functionality for Managers, including add library, remove library, and rename library -- Vera, Confetti
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

class CUndoBaseLibraryManager
    : public IUndoObject
{
public:
    CUndoBaseLibraryManager(CBaseLibraryManager* pMngr, const QString& description, const QString& modifiedManager = nullptr)
        : m_pMngr(pMngr)
        , m_description(description)
        , m_editorObject(modifiedManager)
    {
        assert(m_pMngr);
        SerializeTo(m_undos);
    }

    QString GetEditorObjectName() override
    {
        return m_editorObject;
    }

protected:
    int GetSize() override { return sizeof(CUndoBaseLibraryManager); }
    QString GetDescription() override { return m_description; };

    void Undo(bool bUndo) override
    {
        if (bUndo)
        {
            SerializeTo(m_redos);
        }
        m_pMngr->ClearAll();
        UnserializeFrom(m_undos);
        GetIEditor()->Notify(eNotify_OnDataBaseUpdate);
    }

    void Redo() override
    {
        m_pMngr->ClearAll();
        UnserializeFrom(m_redos);
        GetIEditor()->Notify(eNotify_OnDataBaseUpdate);
    }

private:
    struct LibUndoNode
        : public _i_reference_target_t
    {
        LibUndoNode()
        {
            node = nullptr;
            fileName = "";
        }
        XmlNodeRef node;
        QString fileName;
    };

    static const char* const LIBRARY_TAG;
    static const char* const LEVEL_LIBRARY_TAG;

    void SerializeTo(std::vector<_smart_ptr<LibUndoNode> >& undos) // Save Library Undo
    {
        undos.clear();
        for (int i = 0; i < m_pMngr->GetLibraryCount(); i++)
        {
            IDataBaseLibrary* library = m_pMngr->GetLibrary(i);

            const char* tag = library->IsLevelLibrary() ? LEVEL_LIBRARY_TAG : LIBRARY_TAG;
            XmlNodeRef node = GetIEditor()->GetSystem()->CreateXmlNode(tag);
            QString file = library->GetFilename().isEmpty() ? library->GetFilename() : library->GetName();
            library->Serialize(node, false);
            if (node && !file.isEmpty())
            {
                _smart_ptr<LibUndoNode> undo = new LibUndoNode();
                undo->fileName = file;
                undo->node = node;
                undos.push_back(undo);
            }
        }
    }

    void UnserializeFrom(std::vector<_smart_ptr<LibUndoNode> >& undos) // Load Library Undo
    {
        for (int i = 0; i < undos.size(); i++)
        {
            _smart_ptr<LibUndoNode> undo = undos[i];
            if (undo->node && !undo->fileName.isEmpty())
            {
                //AddLibrary adds a .xml to the end of the library path, this will remove the extra for compatibility
                undo->fileName.replace(m_pMngr->GetLibsPath().toLower(), "");
                undo->fileName.replace(".xml", "");

                const bool isLevelLibrary = (strcmp(undo->node->getTag(), LEVEL_LIBRARY_TAG) == 0);

                IDataBaseLibrary* library = m_pMngr->AddLibrary(undo->fileName, isLevelLibrary);
                library->Serialize(undo->node, true);
            }
        }
    }


    QString m_description;
    QString m_editorObject;
    CBaseLibraryManager* m_pMngr;
    std::vector<_smart_ptr<LibUndoNode> > m_undos;
    std::vector<_smart_ptr<LibUndoNode> > m_redos;
};

const char* const CUndoBaseLibraryManager::LIBRARY_TAG = "UndoLibrary";
const char* const CUndoBaseLibraryManager::LEVEL_LIBRARY_TAG = "UndoLevelLibrary";



//////////////////////////////////////////////////////////////////////////
// CBaseLibraryManager implementation.
//////////////////////////////////////////////////////////////////////////
CBaseLibraryManager::CBaseLibraryManager()
{
    m_bUniqNameMap = false;
    m_bUniqGuidMap = true;
    GetIEditor()->RegisterNotifyListener(this);
}

//////////////////////////////////////////////////////////////////////////
CBaseLibraryManager::~CBaseLibraryManager()
{
    ClearAll();
    GetIEditor()->UnregisterNotifyListener(this);
}

//////////////////////////////////////////////////////////////////////////
void CBaseLibraryManager::ClearAll()
{
    // Delete all items from all libraries.
    for (int i = 0; i < m_libs.size(); i++)
    {
        m_libs[i]->RemoveAllItems();
    }

    // if we will not copy maps locally then destructors of the elements of
    // the map will operate on the already invalid map object
    // see:
    //  CBaseLibraryManager::UnregisterItem()
    //  CBaseLibraryManager::DeleteItem()
    //  CMaterial::~CMaterial()

    ItemsGUIDMap itemsGuidMap;
    ItemsNameMap itemsNameMap;

    {
        AZStd::lock_guard<AZStd::mutex> lock(m_itemsNameMapMutex);
        std::swap(itemsGuidMap, m_itemsGuidMap);
        std::swap(itemsNameMap, m_itemsNameMap);

        m_libs.clear();
    }
}

//////////////////////////////////////////////////////////////////////////
IDataBaseLibrary* CBaseLibraryManager::FindLibrary(const QString& library)
{
    const int index = FindLibraryIndex(library);
    return index == -1 ? nullptr : m_libs[index];
}

//////////////////////////////////////////////////////////////////////////
int CBaseLibraryManager::FindLibraryIndex(const QString& library)
{
    QString lib = library;
    lib.replace('\\', '/');
    for (int i = 0; i < m_libs.size(); i++)
    {
        QString _lib = m_libs[i]->GetFilename();
        _lib.replace('\\', '/');
        if (QString::compare(lib, m_libs[i]->GetName(), Qt::CaseInsensitive) == 0 || QString::compare(lib, _lib, Qt::CaseInsensitive) == 0)
        {
            return i;
        }
    }
    return -1;
}

//////////////////////////////////////////////////////////////////////////
IDataBaseItem* CBaseLibraryManager::FindItem(REFGUID guid) const
{
    CBaseLibraryItem* pMtl = stl::find_in_map(m_itemsGuidMap, guid, nullptr);
    return pMtl;
}

//////////////////////////////////////////////////////////////////////////
void CBaseLibraryManager::SplitFullItemName(const QString& fullItemName, QString& libraryName, QString& itemName)
{
    int p;
    p = fullItemName.indexOf('.');
    if (p < 0 || !QString::compare(fullItemName.mid(p + 1), "mtl", Qt::CaseInsensitive))
    {
        libraryName = "";
        itemName = fullItemName;
        return;
    }
    libraryName = fullItemName.mid(0, p);
    itemName = fullItemName.mid(p + 1);
}

//////////////////////////////////////////////////////////////////////////
IDataBaseItem* CBaseLibraryManager::FindItemByName(const QString& fullItemName)
{
    AZStd::lock_guard<AZStd::mutex> lock(m_itemsNameMapMutex);
    return stl::find_in_map(m_itemsNameMap, fullItemName, nullptr);
}

//////////////////////////////////////////////////////////////////////////
IDataBaseItem* CBaseLibraryManager::LoadItemByName(const QString& fullItemName)
{
    QString libraryName, itemName;
    SplitFullItemName(fullItemName, libraryName, itemName);

    if (!FindLibrary(libraryName))
    {
        LoadLibrary(MakeFilename(libraryName));
    }

    return FindItemByName(fullItemName);
}

//////////////////////////////////////////////////////////////////////////
IDataBaseItem* CBaseLibraryManager::FindItemByName(const char* fullItemName)
{
    return FindItemByName(QString(fullItemName));
}

//////////////////////////////////////////////////////////////////////////
IDataBaseItem* CBaseLibraryManager::LoadItemByName(const char* fullItemName)
{
    return LoadItemByName(QString(fullItemName));
}

//////////////////////////////////////////////////////////////////////////
IDataBaseItem* CBaseLibraryManager::CreateItem(IDataBaseLibrary* pLibrary)
{
    assert(pLibrary);

    // Add item to this library.
    TSmartPtr<CBaseLibraryItem> pItem = MakeNewItem();
    pLibrary->AddItem(pItem);
    return pItem;
}

//////////////////////////////////////////////////////////////////////////
void CBaseLibraryManager::DeleteItem(IDataBaseItem* pItem)
{
    assert(pItem);

    UnregisterItem((CBaseLibraryItem*)pItem);
    if (pItem->GetLibrary())
    {
        pItem->GetLibrary()->RemoveItem(pItem);
    }
}

//////////////////////////////////////////////////////////////////////////
IDataBaseLibrary* CBaseLibraryManager::LoadLibrary(const QString& inFilename, [[maybe_unused]] bool bReload)
{
    if (auto lib = FindLibrary(inFilename))
    {
        return lib;
    }

    TSmartPtr<CBaseLibrary> pLib = MakeNewLibrary();
    if (!pLib->Load(MakeFilename(inFilename)))
    {
        Error(QObject::tr("Failed to Load Item Library: %1").arg(inFilename).toUtf8().data());
        return nullptr;
    }

    m_libs.push_back(pLib);
    return pLib;
}

//////////////////////////////////////////////////////////////////////////
int CBaseLibraryManager::GetModifiedLibraryCount() const
{
    int count = 0;
    for (int i = 0; i < m_libs.size(); i++)
    {
        if (m_libs[i]->IsModified())
        {
            count++;
        }
    }
    return count;
}

//////////////////////////////////////////////////////////////////////////
IDataBaseLibrary* CBaseLibraryManager::AddLibrary(const QString& library, bool bIsLevelLibrary, bool bIsLoading)
{
    // Make a filename from name of library.
    QString filename = library;

    if (filename.indexOf(".xml") == -1) // if its already a filename, we don't do anything
    {
        filename.replace(' ', '_');
        if (!bIsLevelLibrary)
        {
            filename = MakeFilename(library);
        }
        else
        {
            // if its the level library it gets saved in the level and should not be concatenated with any other file name
            filename = filename + ".xml";
        }
    }

    IDataBaseLibrary* pBaseLib = FindLibrary(library);  //library name
    if (!pBaseLib)
    {
        pBaseLib = FindLibrary(filename); //library file name
    }
    if (pBaseLib)
    {
        return pBaseLib;
    }

    CBaseLibrary* lib = MakeNewLibrary();
    lib->SetName(library);
    lib->SetLevelLibrary(bIsLevelLibrary);
    lib->SetFilename(filename, !bIsLoading);
    // set modified to true, so even empty particle libraries get saved
    lib->SetModified(true);

    m_libs.push_back(lib);
    return lib;
}

//////////////////////////////////////////////////////////////////////////
QString CBaseLibraryManager::MakeFilename(const QString& library)
{
    QString filename = library;
    filename.replace(' ', '_');
    filename.replace(".xml", "");

    // make it contain the canonical libs path:
    Path::ConvertBackSlashToSlash(filename);

    QString LibsPath(GetLibsPath());
    Path::ConvertBackSlashToSlash(LibsPath);

    if (filename.left(LibsPath.length()).compare(LibsPath, Qt::CaseInsensitive) == 0)
    {
        filename = filename.mid(LibsPath.length());
    }

    return LibsPath + filename + ".xml";
}

//////////////////////////////////////////////////////////////////////////
bool CBaseLibraryManager::IsUniqueFilename(const QString& library)
{
    QString resultPath = MakeFilename(library);
    CCryFile xmlFile;
    // If we can find a file for the path
    return !xmlFile.Open(resultPath.toUtf8().data(), "rb");
}

//////////////////////////////////////////////////////////////////////////
void CBaseLibraryManager::DeleteLibrary(const QString& library, bool forceDeleteLevel)
{
    for (int i = 0; i < m_libs.size(); i++)
    {
        if (QString::compare(library, m_libs[i]->GetName(), Qt::CaseInsensitive) == 0)
        {
            CBaseLibrary* pLibrary = m_libs[i];
            // Check if not level library, they cannot be deleted.
            if (!pLibrary->IsLevelLibrary() || forceDeleteLevel)
            {
                for (int j = 0; j < pLibrary->GetItemCount(); j++)
                {
                    UnregisterItem((CBaseLibraryItem*)pLibrary->GetItem(j));
                }
                pLibrary->RemoveAllItems();

                if (pLibrary->IsLevelLibrary())
                {
                    m_pLevelLibrary = nullptr;
                }
                m_libs.erase(m_libs.begin() + i);
            }
            break;
        }
    }
}

//////////////////////////////////////////////////////////////////////////
IDataBaseLibrary* CBaseLibraryManager::GetLibrary(int index) const
{
    assert(index >= 0 && index < m_libs.size());
    return m_libs[index];
};

//////////////////////////////////////////////////////////////////////////
IDataBaseLibrary* CBaseLibraryManager::GetLevelLibrary() const
{
    IDataBaseLibrary* pLevelLib = nullptr;

    for (int i = 0; i < GetLibraryCount(); i++)
    {
        if (GetLibrary(i)->IsLevelLibrary())
        {
            pLevelLib = GetLibrary(i);
            break;
        }
    }


    return pLevelLib;
}

//////////////////////////////////////////////////////////////////////////
void CBaseLibraryManager::SaveAllLibs()
{
    for (int i = 0; i < GetLibraryCount(); i++)
    {
        // Check if library is modified.
        IDataBaseLibrary* pLibrary = GetLibrary(i);

        //Level library is saved when the level is saved
        if (pLibrary->IsLevelLibrary())
        {
            continue;
        }
        if (pLibrary->IsModified())
        {
            if (pLibrary->Save())
            {
                pLibrary->SetModified(false);
            }
        }
    }
}

//////////////////////////////////////////////////////////////////////////
void CBaseLibraryManager::Serialize(XmlNodeRef& node, bool bLoading)
{
    static const char* const LEVEL_LIBRARY_TAG = "LevelLibrary";

    QString rootNodeName = GetRootNodeName();
    if (bLoading)
    {
        XmlNodeRef libs = node->findChild(rootNodeName.toUtf8().data());
        if (libs)
        {
            for (int i = 0; i < libs->getChildCount(); i++)
            {
                // Load only library name.
                XmlNodeRef libNode = libs->getChild(i);
                if (strcmp(libNode->getTag(), LEVEL_LIBRARY_TAG) == 0)
                {
                    if (!m_pLevelLibrary)
                    {
                        QString libName;
                        libNode->getAttr("Name", libName);
                        m_pLevelLibrary = static_cast<CBaseLibrary*>(AddLibrary(libName, true));
                    }
                    m_pLevelLibrary->Serialize(libNode, bLoading);
                }
                else
                {
                    QString libName;
                    if (libNode->getAttr("Name", libName))
                    {
                        // Load this library.
                        if (!FindLibrary(libName))
                        {
                            LoadLibrary(MakeFilename(libName));
                        }
                    }
                }
            }
        }
    }
    else
    {
        // Save all libraries.
        XmlNodeRef libs = node->newChild(rootNodeName.toUtf8().data());
        for (int i = 0; i < GetLibraryCount(); i++)
        {
            IDataBaseLibrary* pLib = GetLibrary(i);
            if (pLib->IsLevelLibrary())
            {
                // Level libraries are saved in in level.
                XmlNodeRef libNode = libs->newChild(LEVEL_LIBRARY_TAG);
                pLib->Serialize(libNode, bLoading);
            }
            else
            {
                // Save only library name.
                XmlNodeRef libNode = libs->newChild("Library");
                libNode->setAttr("Name", pLib->GetName().toUtf8().data());
            }
        }
        SaveAllLibs();
    }
}

//////////////////////////////////////////////////////////////////////////
QString CBaseLibraryManager::MakeUniqueItemName(const QString& srcName, const QString& libName)
{
    // unlikely we'll ever encounter more than 16
    std::vector<AZStd::string> possibleDuplicates;
    possibleDuplicates.reserve(16);

    // search for strings in the database that might have a similar name (ignore case)
    IDataBaseItemEnumerator* pEnum = GetItemEnumerator();
    for (IDataBaseItem* pItem = pEnum->GetFirst(); pItem != nullptr; pItem = pEnum->GetNext())
    {
        //Check if the item is in the target library first.
        IDataBaseLibrary* itemLibrary = pItem->GetLibrary();
        QString itemLibraryName;
        if (itemLibrary)
        {
            itemLibraryName = itemLibrary->GetName();
        }

        // Item is not in the library so there cannot be a naming conflict.
        if (!libName.isEmpty() && !itemLibraryName.isEmpty() && itemLibraryName != libName)
        {
            continue;
        }

        const QString& name = pItem->GetName();
        if (name.startsWith(srcName, Qt::CaseInsensitive))
        {
            possibleDuplicates.push_back(AZStd::string(name.toUtf8().data()));
        }
    }
    pEnum->Release();

    if (possibleDuplicates.empty())
    {
        return srcName;
    }

    std::sort(possibleDuplicates.begin(), possibleDuplicates.end(), [](const AZStd::string& strOne, const AZStd::string& strTwo)
        {
            // I can assume size sorting since if the length is different, either one of the two strings doesn't
            // closely match the string we are trying to duplicate, or it's a bigger number (X1 vs X10)
            if (strOne.size() != strTwo.size())
            {
                return strOne.size() < strTwo.size();
            }
            else
            {
                return azstricmp(strOne.c_str(), strTwo.c_str()) < 0;
            }
        }
        );

    int num = 0;
    QString returnValue = srcName;
    while (num < possibleDuplicates.size() && QString::compare(possibleDuplicates[num].c_str(), returnValue, Qt::CaseInsensitive) == 0)
    {
        returnValue = QStringLiteral("%1%2%3").arg(srcName).arg("_").arg(num);
        ++num;
    }

    return returnValue;
}

//////////////////////////////////////////////////////////////////////////
void CBaseLibraryManager::Validate()
{
    IDataBaseItemEnumerator* pEnum = GetItemEnumerator();
    for (IDataBaseItem* pItem = pEnum->GetFirst(); pItem != nullptr; pItem = pEnum->GetNext())
    {
        pItem->Validate();
    }
    pEnum->Release();
}

//////////////////////////////////////////////////////////////////////////
void CBaseLibraryManager::RegisterItem(CBaseLibraryItem* pItem, REFGUID newGuid)
{
    assert(pItem);

    bool bNotify = false;

    if (m_bUniqGuidMap)
    {
        bool bNewItem = true;
        REFGUID oldGuid = pItem->GetGUID();
        if (!GuidUtil::IsEmpty(oldGuid))
        {
            bNewItem = false;
            m_itemsGuidMap.erase(oldGuid);
        }
        if (GuidUtil::IsEmpty(newGuid))
        {
            return;
        }
        CBaseLibraryItem* pOldItem = stl::find_in_map(m_itemsGuidMap, newGuid, nullptr);
        if (!pOldItem)
        {
            pItem->m_guid = newGuid;
            m_itemsGuidMap[newGuid] = pItem;
            pItem->m_bRegistered = true;
            bNotify = true;
        }
        else
        {
            if (pOldItem != pItem)
            {
                ReportDuplicateItem(pItem, pOldItem);
            }
        }
    }

    if (m_bUniqNameMap)
    {
        QString fullName = pItem->GetFullName();
        if (!pItem->GetName().isEmpty())
        {
            CBaseLibraryItem* pOldItem = static_cast<CBaseLibraryItem*>(FindItemByName(fullName));
            if (!pOldItem)
            {
                AZStd::lock_guard<AZStd::mutex> lock(m_itemsNameMapMutex);
                m_itemsNameMap[fullName] = pItem;
                pItem->m_bRegistered = true;
                bNotify = true;
            }
            else
            {
                if (pOldItem != pItem)
                {
                    ReportDuplicateItem(pItem, pOldItem);
                }
            }
        }
    }

    // Notify listeners.
    if (bNotify)
    {
        NotifyItemEvent(pItem, EDB_ITEM_EVENT_ADD);
    }
}

//////////////////////////////////////////////////////////////////////////
void CBaseLibraryManager::RegisterItem(CBaseLibraryItem* pItem)
{
    assert(pItem);

    bool bNotify = false;

    if (m_bUniqGuidMap)
    {
        if (GuidUtil::IsEmpty(pItem->GetGUID()))
        {
            return;
        }
        CBaseLibraryItem* pOldItem = stl::find_in_map(m_itemsGuidMap, pItem->GetGUID(), nullptr);
        if (!pOldItem)
        {
            m_itemsGuidMap[pItem->GetGUID()] = pItem;
            pItem->m_bRegistered = true;
            bNotify = true;
        }
        else
        {
            if (pOldItem != pItem)
            {
                ReportDuplicateItem(pItem, pOldItem);
            }
        }
    }

    if (m_bUniqNameMap)
    {
        QString fullName = pItem->GetFullName();
        if (!fullName.isEmpty())
        {
            CBaseLibraryItem* pOldItem = static_cast<CBaseLibraryItem*>(FindItemByName(fullName));
            if (!pOldItem)
            {
                AZStd::lock_guard<AZStd::mutex> lock(m_itemsNameMapMutex);
                m_itemsNameMap[fullName] = pItem;
                pItem->m_bRegistered = true;
                bNotify = true;
            }
            else
            {
                if (pOldItem != pItem)
                {
                    ReportDuplicateItem(pItem, pOldItem);
                }
            }
        }
    }

    // Notify listeners.
    if (bNotify)
    {
        NotifyItemEvent(pItem, EDB_ITEM_EVENT_ADD);
    }
}

//////////////////////////////////////////////////////////////////////////
void CBaseLibraryManager::SetRegisteredFlag(CBaseLibraryItem* pItem, bool bFlag)
{
    pItem->m_bRegistered = bFlag;
}

//////////////////////////////////////////////////////////////////////////
void CBaseLibraryManager::ReportDuplicateItem(CBaseLibraryItem* pItem, CBaseLibraryItem* pOldItem)
{
    QString sLibName;
    if (pOldItem->GetLibrary())
    {
        sLibName = pOldItem->GetLibrary()->GetName();
    }
    CErrorRecord err;
    err.pItem = pItem;
    err.error = QStringLiteral("Item %1 with duplicate GUID to loaded item %2 ignored").arg(pItem->GetFullName(), pOldItem->GetFullName());
    GetIEditor()->GetErrorReport()->ReportError(err);
}

//////////////////////////////////////////////////////////////////////////
void CBaseLibraryManager::UnregisterItem(CBaseLibraryItem* pItem)
{
    // Notify listeners.
    NotifyItemEvent(pItem, EDB_ITEM_EVENT_DELETE);

    if (!pItem)
    {
        return;
    }

    if (m_bUniqGuidMap)
    {
        m_itemsGuidMap.erase(pItem->GetGUID());
    }
    if (m_bUniqNameMap && !pItem->GetFullName().isEmpty())
    {
        AZStd::lock_guard<AZStd::mutex> lock(m_itemsNameMapMutex);
        auto findIter = m_itemsNameMap.find(pItem->GetFullName());
        if (findIter != m_itemsNameMap.end())
        {
            _smart_ptr<CBaseLibraryItem> item = findIter->second;
            m_itemsNameMap.erase(findIter);
        }
    }

    pItem->m_bRegistered = false;
}

//////////////////////////////////////////////////////////////////////////
QString CBaseLibraryManager::MakeFullItemName(IDataBaseLibrary* pLibrary, const QString& group, const QString& itemName)
{
    assert(pLibrary);
    QString name = pLibrary->GetName() + ".";
    if (!group.isEmpty())
    {
        name += group + ".";
    }
    name += itemName;
    return name;
}

//////////////////////////////////////////////////////////////////////////
void CBaseLibraryManager::GatherUsedResources(CUsedResources& resources)
{
    IDataBaseItemEnumerator* pEnum = GetItemEnumerator();
    for (IDataBaseItem* pItem = pEnum->GetFirst(); pItem != nullptr; pItem = pEnum->GetNext())
    {
        pItem->GatherUsedResources(resources);
    }
    pEnum->Release();
}

//////////////////////////////////////////////////////////////////////////
IDataBaseItemEnumerator* CBaseLibraryManager::GetItemEnumerator()
{
    if (m_bUniqNameMap)
    {
        return new CDataBaseItemEnumerator<ItemsNameMap>(&m_itemsNameMap);
    }
    else
    {
        return new CDataBaseItemEnumerator<ItemsGUIDMap>(&m_itemsGuidMap);
    }
}

//////////////////////////////////////////////////////////////////////////
void CBaseLibraryManager::OnEditorNotifyEvent(EEditorNotifyEvent event)
{
    switch (event)
    {
    case eNotify_OnBeginNewScene:
        SetSelectedItem(nullptr);
        ClearAll();
        break;
    case eNotify_OnBeginSceneOpen:
        SetSelectedItem(nullptr);
        ClearAll();
        break;
    case eNotify_OnCloseScene:
        SetSelectedItem(nullptr);
        ClearAll();
        break;
    }
}

//////////////////////////////////////////////////////////////////////////
void CBaseLibraryManager::OnRenameItem(CBaseLibraryItem* pItem, const QString& oldName)
{
    m_itemsNameMapMutex.lock();
    if (!oldName.isEmpty())
    {
        m_itemsNameMap.erase(oldName);
    }
    if (!pItem->GetFullName().isEmpty())
    {
        m_itemsNameMap[pItem->GetFullName()] = pItem;
    }
    m_itemsNameMapMutex.unlock();

    OnItemChanged(pItem);
}

//////////////////////////////////////////////////////////////////////////
void CBaseLibraryManager::AddListener(IDataBaseManagerListener* pListener)
{
    stl::push_back_unique(m_listeners, pListener);
}

//////////////////////////////////////////////////////////////////////////
void CBaseLibraryManager::RemoveListener(IDataBaseManagerListener* pListener)
{
    stl::find_and_erase(m_listeners, pListener);
}

//////////////////////////////////////////////////////////////////////////
void CBaseLibraryManager::NotifyItemEvent(IDataBaseItem* pItem, EDataBaseItemEvent event)
{
    // Notify listeners.
    if (!m_listeners.empty())
    {
        for (int i = 0; i < m_listeners.size(); i++)
        {
            m_listeners[i]->OnDataBaseItemEvent(pItem, event);
        }
    }
}

//////////////////////////////////////////////////////////////////////////
void CBaseLibraryManager::OnItemChanged(IDataBaseItem* pItem)
{
    NotifyItemEvent(pItem, EDB_ITEM_EVENT_CHANGED);
}

//////////////////////////////////////////////////////////////////////////
void CBaseLibraryManager::OnUpdateProperties(IDataBaseItem* pItem, bool bRefresh)
{
    NotifyItemEvent(pItem, bRefresh ? EDB_ITEM_EVENT_UPDATE_PROPERTIES
        : EDB_ITEM_EVENT_UPDATE_PROPERTIES_NO_EDITOR_REFRESH);
}

//////////////////////////////////////////////////////////////////////////
void CBaseLibraryManager::SetSelectedItem(IDataBaseItem* pItem)
{
    if (m_pSelectedItem == pItem)
    {
        return;
    }
    m_pSelectedItem = (CBaseLibraryItem*)pItem;
    NotifyItemEvent(m_pSelectedItem, EDB_ITEM_EVENT_SELECTED);
}

//////////////////////////////////////////////////////////////////////////
IDataBaseItem* CBaseLibraryManager::GetSelectedItem() const
{
    return m_pSelectedItem;
}

//////////////////////////////////////////////////////////////////////////
IDataBaseItem* CBaseLibraryManager::GetSelectedParentItem() const
{
    return m_pSelectedParent;
}

void CBaseLibraryManager::ChangeLibraryOrder(IDataBaseLibrary* lib, unsigned int newLocation)
{
    if (!lib || newLocation >= m_libs.size() || lib == m_libs[newLocation])
    {
        return;
    }

    for (int i = 0; i < m_libs.size(); i++)
    {
        if (lib == m_libs[i])
        {
            _smart_ptr<CBaseLibrary> curLib = m_libs[i];
            m_libs.erase(m_libs.begin() + i);
            m_libs.insert(m_libs.begin() + newLocation, curLib);
            return;
        }
    }
}

bool CBaseLibraryManager::SetLibraryName(CBaseLibrary* lib, const QString& name)
{
    // SetFilename will validate if the name is duplicate with exist libraries.
    if (lib->SetFilename(MakeFilename(name)))
    {
        lib->SetName(name);
        return true;
    }
    return false;
}
