//
// Created by Ht on 2020/1/7.
//

#ifndef LIBGOING_QPOBJECTHOLDER_H
#define LIBGOING_QPOBJECTHOLDER_H

#include <unordered_map>
#include <functional>
#include <mutex>
#include "jebase/config.h"
#include "qlibc/QCommonUtil.h"
#include "StringUtils.hpp"
#include "QLogger.h"

namespace qlibc {

#define HOLDER_LOCK(m) \
    std::unique_lock<std::recursive_mutex>  __locker(*m)

    template<typename T>
    class QPObjectHolder {
    protected:
        std::unordered_map<std::string, T*> mHolderMap;
        std::recursive_mutex* m_;
        bool   mReleaseOnRemove;

    public:
        inline explicit QPObjectHolder(bool autoRelease = true) :
                m_(new std::recursive_mutex()), mReleaseOnRemove(autoRelease) {
        }

        inline ~QPObjectHolder() {
            releaseAll();
            delete m_;
            m_=nullptr;
        }

        inline void releaseAll() {
            HOLDER_LOCK(m_);
            if(mReleaseOnRemove) {
                for(auto& _it : mHolderMap)
                    delete  _it.second;
            }
            mHolderMap.clear();
        };

        inline void appendNew(const std::string &key, T *obj) {
            HOLDER_LOCK(m_);
            auto _it = mHolderMap.find((key));
            if(_it!=mHolderMap.end()) { //存在旧key
                auto oldObj = _it->second;
                if(oldObj==obj) {   //二者相同，且可能均为空
                    return;
                } else {  //二者不同，需要清空原有指针
                    if(mReleaseOnRemove)
                        delete (oldObj);
                    mHolderMap.erase((key));
                }
            }
            mHolderMap.insert(std::pair<const std::string, T*>(key,obj));
        }

        inline T *findObject(const std::string &key) const {
            HOLDER_LOCK(m_);
            auto  _it = mHolderMap.find((key));
            auto ret = ((  _it == mHolderMap.end())?nullptr: _it->second);
            return ret;
        }

        inline const T *findObjectConst(const std::string &key) const {
            HOLDER_LOCK(m_);
            auto  _it = mHolderMap.find((key));
            auto ret = ((  _it == mHolderMap.end())?nullptr: _it->second);
            return ret;
        }

        inline bool existObject(const std::string& key) const {
            HOLDER_LOCK(m_);
            return ((mHolderMap).find(key) != (mHolderMap).end());
        }

        inline void eraseObject(const std::string &key) {
            HOLDER_LOCK(m_);
            auto _it = mHolderMap.find((key));
            if (_it != mHolderMap.end()) {
                if(mReleaseOnRemove) {
                    if (_it->second != nullptr)
                        delete (_it->second);
                }
                mHolderMap.erase((key));
            }
        }

        inline T *removeObject(const std::string &key) {
            HOLDER_LOCK(m_);
            auto  _it = mHolderMap.find((key));
            auto lack = ( _it == mHolderMap.end());
            auto ret = lack?nullptr: _it->second;
            if (!lack)
                mHolderMap.erase(key);
            return ret;
        }

        using ObjAccessor = std::function<bool(const std::string&, T*)>;
        inline void invokeOnAllObject(const ObjAccessor& func, bool sortKey = false) {
            HOLDER_LOCK(m_);
            if(sortKey) {
                QStringList  keyLists;
                for(auto &it : mHolderMap)
                    keyLists.emplace_back(it.first);
                if(sortKey)
                    StringUtils::sortStringList(keyLists);
                for(auto k: keyLists) {
                    try{
                        if(!func(k, findObject(k)))
                            break;
                    } catch (std::bad_function_call& e) {
                        QErr("Fail to call on Objects map : %s", e.what());
                    }
                }
            } else {
                for (auto &it: mHolderMap) {
                    try{
                        if (!func(it.first, it.second))
                            break;
                    } catch (std::bad_function_call& e) {
                        QErr("Fail to call on Objects map : %s", e.what());
                    }
                }
            }
        };

        using ObjAccessorConst = std::function<bool(const std::string&, const T*)>;
        inline void invokeOnAllObjectConst(const ObjAccessorConst& func, bool sortKey = false ) const {
            HOLDER_LOCK(m_);
            if(sortKey) {
                QStringList  keyLists;
                for(auto &it : mHolderMap)
                    keyLists.emplace_back(it.first);
                if(sortKey)
                    StringUtils::sortStringList(keyLists);
                for(auto k: keyLists) {
                    try{
                        if(!func(k, findObjectConst(k)))
                            break;
                    } catch (std::bad_function_call& e) {
                        QErr("Fail to call on Objects map : %s", e.what());
                    }
                }
            } else {
                for (auto &it: mHolderMap) {
                    try{
                        if (!func(it.first, it.second))
                            break;
                    } catch (std::bad_function_call& e) {
                        QErr("Fail to call on Objects map : %s", e.what());
                    }
                }
            }
        };

        //危险用法
        inline std::unordered_map<std::string, T *>& getObjectMap(){
            return mHolderMap;
        }

        inline QStringList& getKeySets(QStringList& keys, bool sortKey = false){
            HOLDER_LOCK(m_);
            for(auto &it : mHolderMap){
                keys.emplace_back(it.first);
            }
            if(sortKey)
                StringUtils::sortStringList(keys);
            return keys;
        }

        void setAutoRelease(bool autoRelease) {
            mReleaseOnRemove = autoRelease;
        }

        bool isAutoRelease() const {
            return mReleaseOnRemove;
        }

        bool isEmpty() const {
            HOLDER_LOCK(m_);
            return mHolderMap.empty();
        }

        int size() const {
            HOLDER_LOCK(m_);
            return mHolderMap.size();
        }
    };

    template<typename T>
    class QSharedObjectHolder {
    protected:
        std::unordered_map<std::string, std::shared_ptr<T>> mHolderMap;
        std::recursive_mutex* m_;
        std::shared_ptr<T> empty_;

    public:
        using SharedObjAccessor = std::function<bool(const std::string&, std::shared_ptr<T>&)>;

        inline explicit QSharedObjectHolder() : //添加一个默认的空对象，用于返回空对象的指针
                m_(new std::recursive_mutex()), empty_(nullptr) {}

        inline ~QSharedObjectHolder() {
            releaseAll();
            delete m_;
            m_= nullptr;
        }

        inline void releaseAll() {
            if(m_==nullptr) return;
            HOLDER_LOCK(m_);
            mHolderMap.clear();
        };

        //防止同时压入两个相同的指针，注意、不是SharedPtr
        inline const std::shared_ptr<T> appendNew(const std::string &key, T *obj) {
            HOLDER_LOCK(m_);
            auto _it = mHolderMap.find((key));
            auto ne = _it == mHolderMap.end();
            if(!ne) {    //规避相同指针入栈,nullptr空指针除外
                std::shared_ptr<T>& oldObj = _it->second;
                if(oldObj.get()==obj)
                    return oldObj;  //old指针已存在、且与obj相同， 直接返回。不需要删除obj
                else
                    mHolderMap.erase(key);  //删除old指针
            }
            //创建一个智能指针入栈
            auto pair = std::pair<const std::string, std::shared_ptr<T>>(key,std::shared_ptr<T>(obj));
            mHolderMap.insert(pair);
            return (mHolderMap.find(key)->second);
        }

        //防止同时压入两个相同的指针，注意、不是SharedPtr
        inline const std::shared_ptr<T> appendNewRefCopy(const std::string &key, const std::shared_ptr<T> &obj) {
            HOLDER_LOCK(m_);
            auto _it = mHolderMap.find((key));
            auto ne = _it == mHolderMap.end();
            std::shared_ptr<T>& oldObj = (ne?empty_: _it->second);
            if(!ne) {    //规避相同指针入栈,nullptr空指针除外
                if(oldObj.get()==obj.get()) //不再添加
                    return oldObj;
                else
                    mHolderMap.erase(key);
            }
            //创建一个智能指针入栈
            mHolderMap.insert(std::pair<const std::string, std::shared_ptr<T>>(key,obj));
            return (mHolderMap.find(key)->second); //返回新创建的指针拷贝引用
        }

        inline const std::string& keyRef(const std::string& key) {
            HOLDER_LOCK(m_);
            auto _it = mHolderMap.find((key));
            auto ne = _it == mHolderMap.end();
            return ne?_it->first:key;
        }

        inline const std::shared_ptr<T> findObject(const std::string &key) const {
            HOLDER_LOCK(m_);
            auto  _it = mHolderMap.find((key));
            return ((_it == mHolderMap.end()) ? empty_ : _it->second);
        }

        inline bool existObject(const std::string& key) const {
//            if(m_==nullptr) return false;
            HOLDER_LOCK(m_);
            return ((mHolderMap).find(key) != (mHolderMap).end());
        }

        inline std::shared_ptr<T> &empty() {
            return empty_;
        }

        inline void eraseObject(const std::string &key) {
            HOLDER_LOCK(m_);
            auto _it = mHolderMap.find((key));
            if (_it != mHolderMap.end()) {
                mHolderMap.erase((key));
            }
        }

        //这是返回值、拷贝指针，而不是引用
        inline std::shared_ptr<T> removeObject(const std::string &key) {
            HOLDER_LOCK(m_);
            auto  _it = mHolderMap.find((key));
            auto lack = ( _it == mHolderMap.end());
            if (!lack) {
                auto c = _it->second;
                mHolderMap.erase(key);      //删除原有值
                return c;
            }
            return empty_;
        }

        inline void invokeOnAllObject(const SharedObjAccessor& func, bool sortKey = false) {
            HOLDER_LOCK(m_);
            if(sortKey) {
                QStringList  keyLists;
                for(auto &it : mHolderMap)
                    keyLists.emplace_back(it.first);
                if(sortKey)
                    StringUtils::sortStringList(keyLists);
                for(auto& k: keyLists) {
                    auto  _it = mHolderMap.find((k));
                    try{
                        if(!func(k, _it->second))
                            break;
                    } catch (std::bad_function_call& e) {
                        QErr("Fail to call on Objects map : %s", e.what());
                    }
                }
            } else {
                for (auto &it: mHolderMap) {
                    try{
                        if (!func(it.first, it.second))
                            break;
                    } catch (std::bad_function_call& e) {
                        QErr("Fail to call on Objects map : %s", e.what());
                    }
                }
            }
        };

        using SharedObjAccessorConst = std::function<bool(const std::string&, const std::shared_ptr<T>&)>;
        inline void invokeOnAllObjectConst(const SharedObjAccessorConst &func, bool sortKey = false ) const {
            HOLDER_LOCK(m_);
            if(sortKey) {
                QStringList  keyLists;
                for(auto &it : mHolderMap)
                    keyLists.emplace_back(it.first);
                if(sortKey)
                    StringUtils::sortStringList(keyLists);
                for(const auto& k: keyLists) {
                    auto  _it = mHolderMap.find((k));
                    try{
                        if(!func(k, _it->second))
                            break;
                    } catch (std::bad_function_call& e) {
                        QErr("Fail to call on Objects map : %s", e.what());
                    }
                }
            } else {
                for (auto &it: mHolderMap) {
                    try{
                        if (!func(it.first, it.second))
                            break;
                    } catch (std::bad_function_call& e) {
                        QErr("Fail to call on Objects map : %s", e.what());
                    }
                }
            }
        };

        //危险用法
        inline std::unordered_map<std::string, std::shared_ptr<T>>& getObjectMap(){
            return mHolderMap;
        }

        inline QStringList& getKeySets(QStringList& keys, bool sortKey = false){
            HOLDER_LOCK(m_);
            for(auto &it : mHolderMap){
                keys.emplace_back(it.first);
            }
            if(sortKey)
                StringUtils::sortStringList(keys);
            return keys;
        }

        bool isEmpty() const {
            HOLDER_LOCK(m_);
            return mHolderMap.empty();
        }

        int size() const {
            HOLDER_LOCK(m_);
            return mHolderMap.size();
        }
    };

#define HolderAccessor(k,o,type) (const std::string& k, type* o) -> bool
#define HolderAccessorConst(k,o,type) (const std::string& k, const type* o) -> bool

#define SharedHolderAccessor(k,o,type) (const std::string& k, std::shared_ptr<type>& o) -> bool
#define SharedHolderAccessorConst(k,o,type) (const std::string& k, const std::shared_ptr<type>& o) -> bool

}
#endif //LIBGOING_QPOBJECTHOLDER_H
