//
// Created by Ht on 2020/12/3.
//

#ifndef LIBDEVCPP_JCCMDRUNNERHELPER_H
#define LIBDEVCPP_JCCMDRUNNERHELPER_H


#include <functional>
#include <qlibc/cmd/JCCmdRunner.h>

namespace qlibc {

#ifdef DEBUG
    static const bool changeDirSetForDebug = true;
#else
    static const bool changeDirSetForDebug = false;
#endif

#ifndef BASEDIR
    #define BASEDIR "NONE"
//#error "BASEDIR Must be defined in compile params"
#endif

#ifndef APPNAME
#define APPNAME "noname"
#endif

#ifndef RELEASE_DATE
#define RELEASE_DATE "2022-08-09"
#endif
#ifndef APP_PLATFORM
#define APP_PLATFORM "linux"
#endif
#ifndef RELEASE_DATE
#define RELEASE_DATE ("2019-12-07")
#endif

    //启动一个Cmd服务, ac/av是启动参数, clsDef是要启动的Runner的类，由bindRunner定义
#define RunCmdApp(ac,av, clsDef)\
    qlibc::JCCmdRunnerHelper::runCmdApp(ac, av, [](const qlibc::JCArgNode& initData) -> qlibc::JCCmdRunner* { \
        return new clsDef(initData); \
    }, false)

    //启动一个Cmd服务、跑在后台, ac/av是启动参数, clsDef是要启动的Runner的类，由bindRunner定义
#define RunCmdAppInBackGround(ac,av, clsDef)\
    qlibc::JCCmdRunnerHelper::runCmdApp(ac, av, [](const qlibc::JCArgNode& initData) -> qlibc::JCCmdRunner* { \
        return new clsDef(initData); \
    }, true)

    //启动时、自动执行命令序列 auxcmds, ac/av是启动参数, clsDef是要启动的Runner的类，由bindRunner定义
    //auxcmds是一个以;隔开的字符串，每一段代表一个命令行指令
#define RunCmdAppWithDefaultInitCmds(ac, av, clsDef, auxcmds)\
    qlibc::JCCmdRunnerHelper::runCmdApp(ac, av, auxcmds, [](const qlibc::JCArgNode& initData) -> qlibc::JCCmdRunner* { \
        return new clsDef(initData); \
    }, false)

    //启动一个Cmd服务、跑在后台, ac/av是启动参数, clsDef是要启动的Runner的类，由bindRunner定义
#define RunCmdAppInBackgroundWithDefaultInitCmds(ac,av, clsDef, auxcmds)\
    qlibc::JCCmdRunnerHelper::runCmdApp(ac, av, auxcmds, [](const qlibc::JCArgNode& initData) -> qlibc::JCCmdRunner* { \
        return new clsDef(initData); \
    }, true)

#if defined(DEBUG) && (defined(CYGWIN) || defined(LINUX) || defined(WIN32) || defined(MACOS))
    //开发时可采用：
#define jedgeRunServiceAutoStart(ac,av,cl) RunCmdAppWithDefaultInitCmds(ac, av, cl, "s")
#else
    //实际的应用时、可采用 StartChannelServiceInBackground 的方式
    #define jedgeRunServiceAutoStart(ac,av,cl) RunCmdAppInBackgroundWithDefaultInitCmds(ac, av, cl, "s")
#endif

#define RunCmdAppWithDefaultInitCmdsObj(ac, av, clsDef, auxcmds, appname, obj)\
    qlibc::JCCmdRunnerHelper::runCmdApp(ac, av, auxcmds, [appname, &obj](const qlibc::JCArgNode& initData) -> qlibc::JCCmdRunner* { \
        return new clsDef(initData,appname,obj); \
    }, false)

    //启动一个Cmd服务、跑在后台, ac/av是启动参数, clsDef是要启动的Runner的类，由bindRunner定义
#define RunCmdAppInBackgroundWithDefaultInitCmdsObj(ac,av, clsDef, auxcmds, appname, obj)\
    qlibc::JCCmdRunnerHelper::runCmdApp(ac, av, auxcmds, [appname, &obj](const qlibc::JCArgNode& initData) -> qlibc::JCCmdRunner* { \
        return new clsDef(initData, appname, obj); \
    }, true)

#if defined(DEBUG) && (defined(CYGWIN) || defined(LINUX) || defined(WIN32) || defined(MACOS))
    //开发时可采用：
#define jedgeRunServiceAutoStartObj(ac,av,cl,obj) RunCmdAppWithDefaultInitCmdsObj(ac, av, cl, "s",obj)
#else
    //实际的应用时、可采用 StartChannelServiceInBackground 的方式
    #define jedgeRunServiceAutoStartObj(ac,av,cl,obj) RunCmdAppInBackgroundWithDefaultInitCmdsObj(ac, av, cl,"s",obj)
#endif

    //启动时、自动添加额外的命令行参数，argc/argv变为nac/nav, ps为字符串型的额外命令参数
#define AppendAuxProgramParameters(nac, nav, ac, av, ps) \
    static QStringList __##ps##_sl; \
    StringUtils::QuoteSplit(ps, " ", '\'', __##ps##_sl); \
    nav = new const char*[ac+__##ps##_sl.size()]; \
    memcpy(nav, argv, (ac) * sizeof(const char*)); \
    nac = argc;\
    for(const auto& s : __##ps##_sl){ \
        (nav)[nac++] = s.c_str(); \
    }

    using JCCmdRunnerCreator = std::function<qlibc::JCCmdRunner*(JCArgNode &)>;

    class JE_API JCCmdRunnerHelper {
    public:
        static void runCmdApp(qlibc::JCArgNode& node, const std::string& aux_start_cmds, const JCCmdRunnerCreator &creator,
                              bool runBack);

       static void runCmdApp(qlibc::JCArgNode& node, const JCCmdRunnerCreator& creator, bool runBack = false);

        static void runCmdApp(int argc, const char* argv[], const JCCmdRunnerCreator& creator, bool runBack = false);

        static void runCmdApp(int argc, const char **argv, const std::string& aux_start_cmds, const JCCmdRunnerCreator &creator,
                              bool runBack);

        static bool updateArgsForPath(int argc, const char *argv[], int& nargc, const char *** nargv, const char* appPath, bool changePath = changeDirSetForDebug) {
            //自动设置数据目录
            bool has_dir_input_ = false;  //if    !changePath, has_dir_path = true
            bool mis_jam_param = true;

            for (int i = 0; i < argc; i++) { // NOLINT(altera-unroll-loops)
                if (strcmp(argv[i], "--dir") == 0) {
                    has_dir_input_ = true;
                } else if(strcmp(argv[i], "--jam") == 0)
                    mis_jam_param = false;
            }

            if(!has_dir_input_ && changePath) {
#if defined(WIN32)
                std::string auxParams;
                if(mis_jam_param)
                    StringUtils::formatString(auxParams, R"(--jam %s --%s ..\..\..\..\..\%s\unit\%s)", appPath, "dir", BASEDIR, appPath);
                else
                    StringUtils::formatString(auxParams, R"(--%s ..\..\..\..\..\%s\unit\%s)", "dir", BASEDIR, appPath);
#else
                std::string auxParams;
                if(mis_jam_param)
                    StringUtils::formatString(auxParams, "--jam %s --%s ../../../../../%s/unit/%s", appPath, "dir", BASEDIR, appPath);
                else
                    StringUtils::formatString(auxParams, "--%s ../../../../../%s/unit/%s", "dir", BASEDIR, appPath);
#endif
                //额外添加参数
//                AppendAuxProgramParameters(nargc, (*nargv), argc, argv, auxParams);
                static QStringList __auxParams_sl;
                StringUtils::QuoteSplit(auxParams, " ", '\'', __auxParams_sl);
                (*nargv) = new const char *[argc + __auxParams_sl.size()];
                memcpy((*nargv), argv, (argc) * sizeof(const char *));
                nargc = argc;
                for (const auto &s: __auxParams_sl) {
                    ((*nargv))[nargc++] = s.c_str();
                }
            } else { //不自动添加dir
                if(mis_jam_param) {
                    std::string auxParams;
                    StringUtils::formatString(auxParams, "--jam %s", appPath);
                    AppendAuxProgramParameters(nargc, (*nargv), argc, argv, auxParams);
                } else {
                    *nargv = argv;
                    nargc = argc;
                }
            }
            return true;
        }
    };

    /**
     *  定义一个Runner的类、于jedge服务进行捆绑命令回调方法
     *      clsName是自定义Runner的类名称
     *      serverType为jedge的服务类
     */
#define bindRunner(clsName, serverType) \
    class clsName : \
    public  qlibc::JCCmdRunner { \
    public: \
        explicit clsName(const qlibc::JCArgNode& node) : qlibc::JCCmdRunner(node)  { \
                auto* _p = new serverType(node);\
                auto* _tp = dynamic_cast<ja::JAContext*>(_p); \
                if(_tp!=nullptr)\
                    RunObjectVarName = (_tp)->self<serverType>(true); \
                else\
                    RunObjectVarName = std::shared_ptr<serverType>(_p);\
                setRunnerToTargetObject();} \
        ~clsName() override { RunObjectVarName->shutdown();                      \
                auto* _tp = dynamic_cast<ja::JAContext*>(RunObjectVarName.get()); \
                if(_tp!=nullptr) _tp->delMe(); \
                RunObjectVarName = nullptr; /*删除Context*/ } \
        void buildCmdTables() override; \
        declearRunObject(serverType);   \
        void markCommandInputed() override { \
            RunObjectVarName->markCommandInput();            \
        }                                    \
        void markLineKeyInputed() override { \
            RunObjectVarName->markLineKeyInputed();            \
        }                                    \
    }

    //作用同上，但可为服务对象增加初始化参数
#define bindRunnerWithInitObj(clsName, serverType, appname, objCls) \
    class clsName : \
        public  qlibc::JCCmdRunner { \
    public: \
        explicit clsName(qlibc::JCArgNode& node, const std::string& appname, objCls& obj) : qlibc::JCCmdRunner(node) { \
            RunObjectVarName = (new serverType(node, appname, (obj)))->self<serverType>(true); \
            setRunnerToTargetObject();} \
        ~clsName() override { RunObjectVarName->shutdown(); RunObjectVarName->delMe(); RunObjectVarName = nullptr; /*删除Context*/ } \
        void buildCmdTables() override; \
        declearRunObject(serverType);                               \
            void markCommandInputed() override { \
            RunObjectVarName->markCommandInput();            \
        }                                    \
        void markLineKeyInputed() override { \
            RunObjectVarName->markLineKeyInputed();            \
        }                                    \
    }

    /** 定义回调表的开始：在开始和结束中间，使用 RegisterFunctionCommandWithHelp 宏定义回调函数
     *  例如：
     *      RegisterFunctionCommandWithHelp("s,start", Start,"开始测试");
     *      其中Start使用   DefCmdCallback 定义
     *  例如：
     *      DefCmdCallback(Start, s, p, VDalRunner, jedge::VDalServer) {
     *          return true;
     *      ｝
     */
#define bindCallbackTable_Begin(runner) \
void runner::buildCmdTables() {\
    JCCmdRunner::buildCmdTables();

    //定义回调表的结束
#define bindCallbackTable_End(runner) }

}

#endif //LIBDEVCPP_JCCMDRUNNERHELPER_H
