//
// Created by Ht on 2021/10/28.
//

#ifndef LIBDEVCPP_QUNITTEST_H
#define LIBDEVCPP_QUNITTEST_H

#include <qlibc/httplib.h>
#include <qlibc/cmd/JCArgUtil.h>
#include <qlibc/QTimerEngine.h>
#include <qlibc/QDataMemoryPool.h>

namespace qlibc {

    class JE_API UnitCase;
    class JE_API UnitRunner;
    using CaseHandler = std::function<void(UnitRunner&,UnitCase*)>;

#define DefineCase(__n,__r) (__r).cases_.appendNew(#__n, new qlibc::UnitCase(__r,__n,#__n))
#define DefineCaseHandler(__n,__w,__r) (__r).cases_.appendNew(__n, new qlibc::UnitCase(__r,__w,__n))

    class JE_API UnitCase {
    protected:
        UnitRunner&  runner_;
        CaseHandler* case_;
        std::string  key_;
        bool         quit_:1;
        bool         active_:1;
        std::mutex   m_;
        std::condition_variable c_;
        JCArgNode*   run_param_ = nullptr;

    public:
        explicit UnitCase(UnitRunner& rr, const CaseHandler& func, std::string  key);
        ~UnitCase();

        void operator ()();

        void quit();

        bool isActive() const;

        const std::string& getKey();

        void runHolder();

        bool isQuitting() const;

        void waitWhileNotQuitting(int msWait);

        void copyRunParam(JCArgNode &param);

        inline JCArgNode* getRunParam() {
            std::unique_lock<std::mutex> l(m_);
            return run_param_;
        }
    };

    class JE_API UnitRunner : public qlibc::JCCmdRunner {
    protected:
        const char* helpInfo = " qunitmg test : qunitmg [-h] [-c timer | ... ]\n"
                               " -h : show this message\n"
                               " -c : case name ,e.g. timer\n";

    public:
        qlibc::QPObjectHolder<UnitCase>  cases_;
        httplib::RThreadPool pool_;
    public:
        explicit UnitRunner(const qlibc::JCArgNode& unitConfig, const std::function<void(qlibc::UnitRunner&)>& inor);

        ~UnitRunner() override;

    protected:
        bool cmd_run(unsigned int* retCode, void** d, void* ctx);

        bool cmd_stop(unsigned int* retCode, void** data, void* ctx);

        bool cmd_show(unsigned int* retCode, void** data, void* ctx);

    public:
        void buildCmdTables() override;

        int init(qlibc::JCArgNode& node);

    };

#define beginUnitTest(_n,_r,_c) \
    _r.pool_->enqueue([&_r,_c]() \
    {QLog("Unit test function %s start ... ", #_n)

#define endUnitTest(_r,_c) \
    cc->quit();});cc->runHolder()

#define implementCase(_n,_r,_c)\
    void _n(qlibc::UnitRunner& _r, qlibc::UnitCase* _c)

#define defineCase(_n,_r,_c)\
    implementCase(_n,_r,_c) {   \
    beginUnitTest(_n,_r,_c);

#define endCase(_n,_r,_c) \
    endUnitTest(_r,_c);}
}

#endif //LIBDEVCPP_QUNITTEST_H
