//
// Created by Ht on 2022/10/2.
//

#ifndef INC_1_LIBDEVCPP_QGACONTROLLERUTIL_H
#define INC_1_LIBDEVCPP_QGACONTROLLERUTIL_H

#include <qlibc/qlibc.h>
#include <exlib/QJAServer.h>

namespace jedge {

    class JE_API QGaActionSyncer {
    protected:
        std::mutex              mutex_;
        std::condition_variable cond_;
        bool                    waked_;

    public:
        bool wait(int timeout) {
            SINGLE_LOCK(mutex_);
            waked_ = false;
            cond_.wait_for(LOCKER_NAME(mutex_), std::chrono::milliseconds(timeout), [this]{return waked_;} );   //超时
            return waked_;
        }

        void notify() {
            SINGLE_LOCK(mutex_);
            waked_ = true;
            cond_.notify_all();
        }
        void notifyOne() {
            SINGLE_LOCK(mutex_);
            waked_ = true;
            cond_.notify_one();
        }
    };

    using QGaActionSyncerR = std::shared_ptr<QGaActionSyncer>;

    class JE_API SyncerPool {
    protected:
        qlibc::QSharedObjectHolder<QGaActionSyncer> syncers_;
    public:
        void addSyncer(const std::string& key, const QGaActionSyncerR& syncer) {
            syncers_.appendNewRefCopy(key, syncer);
        }

        void notifySyncer(const std::string &syncerKey) {
            auto syncer = syncers_.findObject(syncerKey);
            if(syncer!=nullptr) {
                QDLog("Notify all Wait controllers by key : %s", syncerKey.c_str());
                syncer->notify();
            }
        }

        QGaActionSyncerR getSyncer(const std::string &syncerKey) {
            return syncers_.findObject(syncerKey);
        }

        void notifyOneSyncer(const std::string &syncerKey) {
            auto syncer = syncers_.findObject(syncerKey);
            if(syncer!=nullptr) {
                QDLog("Notify one Wait controller by key : %s",syncerKey.c_str());
                syncer->notifyOne();
            }
        }

        bool existSycner(const std::string &syncerKey) {
            return syncers_.existObject(syncerKey);
        }
    };

    class JE_API QGaDataActionBase {
    public:
        //用于处理不包含${}符号的独立变量Key
        virtual void parseParam(Json::Value& ret, const std::string& src, const qlibc::QData* msg) = 0;

        virtual bool runActionStopIfFail(qlibc::QData *req, qlibc::QData *resp) = 0;

        virtual bool runThisAction(qlibc::QData *req, qlibc::QData *resp) = 0;

        virtual bool checkCondition(qlibc::QData *req, qlibc::QData *resp) = 0;

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

        virtual SyncerPool &getSyncerPool() = 0;

        //用户处理嵌入变量的长字符串
        virtual bool handleActionVar(const std::string &src, std::string &dest, const qlibc::QData * context) = 0;

    };

    class QGaDataActionController;

    using QGaControllerHandler = std::function<bool(QGaDataActionController* ctrl, qlibc::QData* req, qlibc::QData* resp)>;

    class JE_API QGaDataActionController : public qlibc::QData {
    protected:
        const QGaControllerHandler* handler_;

    public:
        QJAMgServer& ctx_;
        QGaDataActionBase& base_;
        qlibc::QRTimer timer;
        qlibc::QData *newReq = nullptr, *newResp = nullptr;

        std::recursive_mutex m_;
        std::mutex wm_;
        std::condition_variable w_;
        bool quit_ = false;
        bool active_ = false;
    public:
        explicit QGaDataActionController(QJAMgServer& ctx, QGaDataActionBase& base, const QGaControllerHandler& execBody) :
            handler_(new QGaControllerHandler(execBody)), ctx_(ctx), base_(base) {
        }

        bool runDataAction (qlibc::QData* req, qlibc::QData* resp) {
            active_ = true;
            return (handler_!= nullptr) && (*handler_)(this, req, resp);
        }

        inline bool isActive() {
            return active_;
        }

        void stopControl() {
            RECURSIVE_LOCK(m_);
            quit_ = true;
            if(timer!= nullptr) {
                ctx_.timerEngine().stopTimer(timer);
                timer = nullptr;
            }

            auto syncerKey = getString("syncer");
            if(!syncerKey.empty()) {
                SyncerPool& pool = base_.getSyncerPool();
                auto syncer = pool.getSyncer(syncerKey);
                syncer->notify();
            }

            if(newReq!= nullptr) {
                ctx_.releaseMessage(newReq);
            }

            if(newResp!= nullptr) {
                ctx_.releaseMessage(newResp);
            }

            SINGLE_LOCK(wm_);
            w_.notify_all();
        }

        void initDataWith(QData *req, QData *resp) {
            RECURSIVE_LOCK(m_);
            if(newReq!= nullptr)
                newReq->clear();
            else
                newReq = ctx_.getBlankMessage();
            if(req!= nullptr)
                newReq->copyData(*req);
            if(newResp!= nullptr)
                newResp->clear();
            else
                newResp = ctx_.getBlankMessage();
            if(resp!= nullptr)
                newResp->copyData(*resp);
        }
    };
    using QGaDataActionControllerR = std::shared_ptr<QGaDataActionController>;

    using QGaDataActionControllerCreator = std::function<QGaDataActionController*(
            QJAMgServer& ctx, QGaDataActionBase &action, const std::string& ctrlKey, QStringList& params, const qlibc::QData* msg)>;

    class JE_API QGaControllerUtil {
    protected:
        qlibc::QPObjectHolder<QGaDataActionControllerCreator> creators_;
        explicit QGaControllerUtil();

    public:

        static QGaControllerUtil& instance();

        //添加一个controller的创建对象
        void addDataActionController(const std::string& ctrlKey, const QGaDataActionControllerCreator& creator);

        //创建一个controller
        QGaDataActionController* createController(QJAMgServer& ctx, QGaDataActionBase &action, const std::string& controller, const qlibc::QData* msg);
    };

#define DAControllerLamda(_c,_r,_s) (QGaDataActionController* (_c), qlibc::QData* (_r), qlibc::QData* (_s))->bool
#define DAControllerCreator(_ctx,_ac,_ck,_pm,_mg) (QJAMgServer& (_ctx), QGaDataActionBase &(_ac), const std::string& (_ck), QStringList& (_pm), const qlibc::QData* (_mg))->QGaDataActionController*

#define GaoActionControllerCreatorName(_t) createFor##_t
#define GaoActionRunnerName(_t) _t##Runner

#define bindGAOActionController(_t) \
       QGaControllerUtil::instance().addDataActionController(#_t, [this]DAControllerCreator(ctx,ac,ck,pm,mg) {\
            return GaoActionControllerCreatorName(_t)(ctx,ac,ck,pm,mg); \
       })
#define implGAOActionControllerCreator(_t,_ctx,_ac,_ck,_pm,_mg) \
        GaoActionControllerCreatorName(_t)(QJAMgServer &(_ctx), QGaDataActionBase &(_ac), const string &(_ck), QStringList &(_pm), const qlibc::QData* (_mg))

#define implGAOActionRunner(_t,_c,_r,_p) \
        GaoActionRunnerName(_t)(QGaDataActionController *(_c), qlibc::QData *(_r), qlibc::QData *(_p))

#define CreateControllerWithRunner(_t,_ctx,_ac) \
        new QGaDataActionController((_ctx), (_ac),\
            [](QGaDataActionController *_ctrl, qlibc::QData *_req, qlibc::QData *_resp) -> bool { \
            return GaoActionRunnerName(_t)(_ctrl, _req, _resp); \
        })
}

#endif //INC_1_LIBDEVCPP_QGACONTROLLERUTIL_H
