//
// Created by Ht on 2021/11/21.
//

#ifndef INC_0_LIBDEVCPP_QMEMOBJECT_H
#define INC_0_LIBDEVCPP_QMEMOBJECT_H

#include <jebase/config.h>
#include <cross/SystemUtil.h>
#include <condition_variable>
#include <utility>
#include "QLogger.h"

namespace qlibc {

    class JE_API QMemObject {
    protected:
        bool           _r = false;      //是否已经在释放过程中。已经在释放过程中的对象不能被锁定
        unsigned short _ref = 0;
        std::mutex     _m;
        std::condition_variable _w;

        //提前调用
        void checkUnrefs(int bc = 0);

    public:
        explicit QMemObject() = default;

        virtual ~QMemObject();

        auto obref(bool byForce = false) -> bool;

        void obunref(bool byForce = false);

        virtual const std::string& objId() = 0;

        inline auto getRefCount() const -> unsigned short {
            return _ref;
        }

        virtual auto available() -> bool;

        void showRefCount();
    };


    //只引用、不持有，只是针对计数器进行操作
    template <typename T>
    class JE_API QShareRef {
    protected:
        T* self_;
    public:
        explicit QShareRef(T* t) : self_(t){
            auto* b = dynamic_cast<QMemObject*>(self_);
            if(b!= nullptr) {
                b->obref();
//                QLog("Creating ptr, left : %02d ", b->getRefCount());
            }
        }

        explicit QShareRef() : self_(nullptr){}

        QShareRef(const QShareRef& r) : self_(r.self_) {
            ref(true);
        }

        virtual ~QShareRef() {
            unref(true, true);
        }

        auto get() const -> T*  {
            return self_;
        }

        auto use_count() const -> int  {
            if(self_!= nullptr) {
                auto *b = dynamic_cast<QMemObject *>(self_);
                if (b != nullptr) {
                    return b->getRefCount();
                }
            }
            return 0;
        }

        void unref(bool byForce = true, bool silent = false) {
            if(self_!= nullptr) {
                auto *b = dynamic_cast<QMemObject *>(self_);
                if (b != nullptr) {
                    b->obunref(byForce);
//                    if(!silent && !b->objId().empty()) {
//                        QDLog("unref %s, left : %02d ", b->objId().c_str(), b->getRefCount());
//                    }
                }
            }
        }

        void ref(bool byForce = false) {
            if(self_!= nullptr) {
                auto *b = dynamic_cast<QMemObject *>(self_);
                if (b != nullptr) {
                    if(!b->obref(byForce)) {
                        QWarn("ref on released object.");
                    }
//                    if(!b->objId().empty()) {
//                        QDLog("ref ptr %s, left : %02d ", b->objId().c_str(), b->getRefCount());
//                    }
                }
            }
        }

        auto operator->() const -> T* {
            return self_;
        }

        auto operator=(const QShareRef& r) -> QShareRef& {
            if(&r!=this) {
                unref();
                self_ = r.self_;
                ref();
            }
            return *this;
        }

        auto operator=(T* p) -> QShareRef& {
            if(self_!=p) {
                unref(true, true);
                self_ = p;
                ref();
            }
            return *this;
        }

        auto available() const -> bool {
            if(self_!= nullptr) {
                auto *b = dynamic_cast<QMemObject *>(self_);
                if (b != nullptr) {
                    return b->available();
                }
            }
            return false;
        }
    };

    //注意，与智能指针不同，QSelfRefObject只是引用、不持有对象、不释放对象
    class JE_API QSelfRefObject :
            public  qlibc::QMemObject {
    protected:
        qlibc::QShareRef<QSelfRefObject> selfRef;

    public:
        explicit QSelfRefObject(): selfRef(this) { }

        void waitForShareAccessReleased() {
#if defined(SHOW_MEM_LOG)
            QDLog("Waiting for self-ref object [%d] releasing ... ", selfRef.use_count());
#endif
            //等待别人唤醒到1再删除
            checkUnrefs(1);
            selfRef = nullptr;      //移除对自身的引用
        }

        ~QSelfRefObject() override {
            waitForShareAccessReleased();
        };

        //重点注意：在拷贝自身引用时，需要重点注意在释放本身内存之前，需要清理所有被设置过的持久化的lamda引用。
        //否则容易引起自锁
        auto getSelfRef() -> qlibc::QShareRef<QSelfRefObject> {
            return selfRef;
        }
    };

    class JE_API QSelfRefControl :
            public QSelfRefObject {
    protected:
        std::string name_;
    public:
        explicit QSelfRefControl(std::string  name) : name_(std::move(name)) {}
        const std::string &objId() override;
    };

#define AssertMemObjectVal(obj, val) \
    if(dynamic_cast<qlibc::QSelfRefObject*>(obj) == nullptr) return val

#define AssertAvailableVoid \
    if(!available()) return

#define AssertAvailableValue(_val)\
    if(!available()) return _val

#define AssertMemObject(obj) \
    if(dynamic_cast<qlibc::QSelfRefObject*>(obj) == nullptr) return

#define AssertAvailableVoidObject(_obj) \
    auto* __##_obj = dynamic_cast<qlibc::QSelfRefObject*>(_obj); \
    if(__##_obj!=nullptr && !__##_obj->available()) return

#define copySelfRef(obj,var, rval) \
    auto* __m_##obj_##_var=dynamic_cast<qlibc::QSelfRefObject*>(obj); \
    if(__m_##obj_##_var==nullptr) return rval;                        \
    auto var=__m_##obj_##_var->getSelfRef()


}
#endif //INC_0_LIBDEVCPP_QMEMOBJECT_H
