//
// Created by Ht on 2020/5/8.
//

#ifndef TESTBIN_MUFISREQEUSTQUEUE_H
#define TESTBIN_MUFISREQEUSTQUEUE_H

#include <unordered_map>
#include <mutex>
#include <queue>
#include <vector>
#include <thread>
#include <ctime>


#include <qlibc/qlibc.h>
#include <cross/SystemUtil.h>

namespace jedge {

    enum CallInfoKeyType {
        typeDataChannelSource = 0,
        typeDataChannelTarget
    };

    class JE_API JMessageSyncUtil {
    public:
        std::mutex              rmutex_;
        std::mutex              mutex_;
        std::condition_variable cond_;
    };

    class JE_API MgbusRequest  {
    protected:
        qlibc::QData *prsp_ = nullptr;
        qlibc::QData *preq_ = nullptr;

        JMessageSyncUtil*      sync_ = nullptr;
        int  timeout_ms_ = default_time_out_msec;
        bool returned_ = false; //消息是否已返回
        const void* exData_ = nullptr;
    public:
        explicit MgbusRequest() {
            prsp_ = new qlibc::QData;
            preq_ = new qlibc::QData;
        }

        explicit MgbusRequest(qlibc::QData& request){
            prsp_ = new qlibc::QData;
            preq_ = &request;
        };

        explicit MgbusRequest(qlibc::QData& request, qlibc::QData& response){
            prsp_ = &response;
            preq_ = &request;
        };

        ~MgbusRequest() {
            delete sync_;
        }

        MgbusRequest& operator=(const MgbusRequest& request){
            if(this==&request)return *this;
            prsp_->copyData(request.request());
            preq_->copyData(request.request());
            return *this;
        }

        inline qlibc::QData& response() const {
            return *prsp_;
        }

        inline qlibc::QData& request() const {
            return *preq_;
        }

        void waitForResponse(int to_ms = -1) {
            sync_->rmutex_.lock();  //需要阻塞等待返回OK
            if(returned_){  //已返回
                return;
            }
            if(sync_ != nullptr) {
                std::unique_lock<std::mutex> lock(sync_->mutex_);
                sync_->cond_.wait_for(lock, std::chrono::milliseconds(to_ms>10?to_ms:timeout_ms_));    //超时在队列中触发、自己做超时。
                //设置返回为超时结果
                if(!returned_) {
                    MkPRsp(prsp_, REQUEST_RESULT_CODE_TIMEOUT, TIME_OUT_MSG);

                }
            }
        }

        void createSyncUtil(){
            if(sync_==nullptr)
                sync_ = new JMessageSyncUtil;
        }

        int createCallId(){
            auto callid = NumberUtils::randomIntger(0x10000000, 0x6FFFFFFF);
            preq_->setObjFmtInt(JEDGE_CALL_CALLID_KEY, callid);
            return callid;
        }

        inline void notify() {
            std::unique_lock<std::mutex> lock(sync_->mutex_);
            sync_->cond_.notify_all();
        }

        void notifyResult(const qlibc::QData &data) {
            bool tr = sync_->rmutex_.try_lock();    //不需要等待
            prsp_->copyData(data);                  //TODO::减少数据拷贝
            returned_ = true;
            if(tr)
                sync_->rmutex_.unlock();
            else
                notify();
        }

        inline void reset(qlibc::QData &request, const void* data = nullptr) {
            std::unique_lock<std::mutex> lock(sync_->mutex_);
            preq_->copyDataNotStartWith(request,"@");
            prsp_->clear();
            exData_ = data;
            createSyncUtil();
        }

        inline const void *exData() {
            return exData_;
        }

        void setTimeout(int to) {
            timeout_ms_ = to;
        }
    };

    class JE_API MgReqeustQueue {
    protected:
        bool quit_ = true;     //处于退出状态的队列、不再接受消息push
        bool quitted_ = true;  //处于退出状态的队列、不再接受消息push
        std::unordered_map<int, MgbusRequest*> mReqeustQue;
        std::recursive_mutex    mQueMutex;

        int  timeout_ms_ = default_time_out_msec;

    public:
        explicit MgReqeustQueue(){};

        ~MgReqeustQueue(){
            //清理请求队列
            RECURSIVE_LOCK(mQueMutex);
            if(!quitted_)
                clear();
        }

        inline void pushRequest(int callid, MgbusRequest* req) {
            if(quit_)
                return;
            req->setTimeout(timeout_ms_);
            RECURSIVE_LOCK(mQueMutex);
            mReqeustQue.insert(std::pair<int, MgbusRequest*>(callid, req));
        }

        inline bool existsRequest(int callid) {
            RECURSIVE_LOCK(mQueMutex);
            return mReqeustQue.find(callid)!=mReqeustQue.end();
        }

        inline MgbusRequest * retreiveRequest(int callid) {
            MgbusRequest *mr = nullptr;
            RECURSIVE_LOCK(mQueMutex);
            auto r = mReqeustQue.find(callid);
            if(r!=mReqeustQue.end()){
                mr = r->second;
                mReqeustQue.erase(callid);
            }
            return mr;
        }

        void clear() {
            RECURSIVE_LOCK(mQueMutex);
            quit_ = true;
            quitted_ = false;
            if(!mReqeustQue.empty()) {
                for (auto it: mReqeustQue) {
                    it.second->notify();    //全部超时
                }
                mReqeustQue.clear();
            }
        }

        void reset() {
            RECURSIVE_LOCK(mQueMutex);
            quit_=false;
            quitted_ = false;
        }

        void setTimeout(int timeout_ms) {
            timeout_ms_ = timeout_ms;
        }
    };

}
#endif //TESTBIN_MUFISREQEUSTQUEUE_H
