#ifndef __ASYNC_HELPER_H__
#define __ASYNC_HELPER_H__
#include "commoninclude.h"
#include "logwrapper.h"
#include "help.h"


/**
 * Should be thrown by api work functions when a  function fails with a specific error code.
 *
 * Nb. the code throwing this exception should be placed in a worker or a wrapper function that
 * catches the exception and handles it.
 */
class YLReturnCodeException : public std::runtime_error
{
private:
    const std::string strCallerName;
    const Yealink_ReturnCode ylApiReturnCode;

    static inline std::string GenerateString(const char * pCallerName, const Yealink_ReturnCode ylApiReturnCode)
    {
        return std::string(pCallerName) + " got native sdk error " + std::to_string(ylApiReturnCode);
    }

public:
    explicit YLReturnCodeException(const char * pCallerName, const Yealink_ReturnCode ylApiReturnCode)
        :  std::runtime_error(GenerateString(pCallerName, ylApiReturnCode)), strCallerName(pCallerName), ylApiReturnCode(ylApiReturnCode) {}

    Yealink_ReturnCode GetYLApiReturnCode() const
    {
        return ylApiReturnCode;
    }

    const std::string& GetstrCallerName() const
    {
        return strCallerName;
    }

    static void LogAndThrow(const char * pCallerName, const Yealink_ReturnCode ylApiReturnCode)
    {
        ERROR_TRACE << GenerateString(pCallerName, ylApiReturnCode);
        throw YLReturnCodeException(pCallerName, ylApiReturnCode);
    }
};

class YLCallReturnCodeException : public std::runtime_error
{
private:
    const std::string strCallerName;
    const Yealink_CallReturn ylApiReturnCode;

    static inline std::string GenerateString(const char * pCallerName, const Yealink_CallReturn ylApiReturnCode)
    {
        return std::string(pCallerName) + " got native sdk call exception " + std::to_string(ylApiReturnCode);
    }

public:
    explicit YLCallReturnCodeException(const char * pCallerName, const Yealink_CallReturn ylApiReturnCode)
        :  std::runtime_error(GenerateString(pCallerName, ylApiReturnCode)), strCallerName(pCallerName), ylApiReturnCode(ylApiReturnCode)
    {}

    Yealink_CallReturn GetYLApiReturnCode() const
    {
        return ylApiReturnCode;
    }

    const std::string& GetstrCallerName() const
    {
        return strCallerName;
    }

    static void LogAndThrow(const char * pCallerName, const Yealink_CallReturn ylApiReturnCode)
    {
        ERROR_TRACE << GenerateString(pCallerName, ylApiReturnCode);
        throw YLCallReturnCodeException(pCallerName, ylApiReturnCode);
    }
};

/**
 * Should be thrown by api work functions when a yealink function fails without an error code.
 *
 * Nb. the code throwing this exception should be placed in a worker or a wrapper function that
 * catches the exception and handles it.
 */
class YLExcetion : public std::runtime_error
{
private:
    const std::string strCallerName;
    const std::string strReason;

    static inline std::string GenerateString(const char * pCallerName, const std::string& strReason)
    {
        if (strReason.length() > 0) {
            return std::string(pCallerName) + " failed with reason:" + strReason;
        } else {
            return std::string(pCallerName) + " failed.";
        }
    }

public:
    explicit YLExcetion(const char * pCallerName, const std::string& strReason = "")
        :  std::runtime_error(GenerateString(pCallerName, strReason)), strCallerName(pCallerName), strReason(strReason) {}

    const std::string& GetstrCallerName() const
    {
        return strCallerName;
    }

    const std::string& GetReason() const
    {
        return strReason;
    }

    static void LogAndThrow(const char * pCallerName, const std::string& strReason = "")
    {
        ERROR_TRACE << GenerateString(pCallerName, strReason);
        throw YLExcetion(pCallerName, strReason);
    }
};

/**
 * Async worker utility that can run any yealink (lambda) work function asynchronously.
 *
 * This is the central worker that (almost all) n-api implementations of yealink functions
 * should use to make sure code is non-blocking. This utility may be used directly for
 * complex cases or indirectly thorugh high-level helpers like f.x. SimpleDeviceAsyncFunction
 * for simple cases.
 *
 * The sole exception where this worker should NOT be used, is for handling yealink c-callbacks
 * in init and eventhandlers!
 *
 * Nb. Based on Napi::AsyncWorker that self-destorys (no explicit delete required)
 */
template <typename ReturnType, typename NapiReturnType>
class AsyncWorker : public Napi::AsyncWorker
{
private:
    Yealink_ReturnCode eRetCode;
    ReturnType ylResult;
    const char * const pCallerName;
    const std::function<ReturnType()> funcWorker;
    const std::function<NapiReturnType(const Napi::Env& env, const ReturnType& retData)> funcToNapiMapper;
    const std::function<void(ReturnType& retData)> funcCleanup;

public:
    /**
     * Construct a new worker:
     *
     * @pCallerName Thread-invariant name of function calling this worker used for documentation (should generally be called with __func__).
     * @funcJsResultCallback The javascript function to call back with the final result.
     * @funcWorker The async code (yealink sdk call) return a C data type (must NOT use any javascript / napi code or types).
     * @funcToNapiMapper Synchronous code converting the C data type to a javascript napi type.
     * @funcCleanup Synchronous code doing cleanup.
     */
    AsyncWorker(const char * const pCallerName,
                const Napi::Function &funcJsResultCallback,
                const std::function<ReturnType()>& funcWorker,
                const std::function<NapiReturnType(const Napi::Env& env, const ReturnType& retData)>& funcToNapiMapper,
    const std::function<void(ReturnType& retData)>& funcCleanup = [](ReturnType& retData) {}
               )
        : Napi::AsyncWorker(funcJsResultCallback),
          eRetCode(Yealink_ReturnCode::Return_Ok),
          ylResult(),
          pCallerName(pCallerName),
          funcWorker(funcWorker),
          funcToNapiMapper(funcToNapiMapper),
          funcCleanup(funcCleanup)
    {
    }

    AsyncWorker(const AsyncWorker&) = delete;
    ~AsyncWorker() {}

    void okError(const Napi::Env& env, const std::string& strErrMsg, bool duringJsCallback)
    {
        ERROR_TRACE << strErrMsg;
        try
        {
            if (!duringJsCallback)
            {
                Callback().Call({ Napi::String::New(env, strErrMsg), env.Undefined() });
            }
        }
        catch (const std::exception &e)
        {
            ERROR_TRACE << "Failed calling error callback with details " + std::string(e.what());
        }
        catch (...)
        {
            ERROR_TRACE << "Failed calling error callback";
        }
    }

    void executeError(const std::string& strErrMsg, const Yealink_ReturnCode eRetCode = Yealink_ReturnCode::Return_Ok) {
        ERROR_TRACE << strErrMsg;
        SetError(strErrMsg);
        this->eRetCode = eRetCode;
    }

    // Executed inside the worker-thread.
    // It is not safe to access JS engine data structure
    // here, so everything we need for input and output
    // should go on `this`.
    void Execute()
    {
        try
        {
            VERBOSE_TRACE << "AsyncWorker: " << pCallerName << " started async function call";
            ylResult = funcWorker();
            VERBOSE_TRACE << "AsyncWorker: " << pCallerName << " finished async function call";
        }
        catch (const YLReturnCodeException &e)
        {
            const std::string strErrMsg = "AsyncWorker execute failure: " + std::string(e.what());
            executeError(strErrMsg, e.GetYLApiReturnCode());
        }
        catch (const YLExcetion &e)
        {
            const std::string strErrMsg = "AsyncWorker execute failure: " + std::string(e.what());
            executeError(strErrMsg);
        }
        catch (const YLCallReturnCodeException &e)
        {
            const std::string strErrMsg = "AsyncWorker execute failure: " + std::string(e.what());
            executeError(strErrMsg);
        }
        catch (const std::exception &e)
        {
            const std::string strErrMsg = "AsyncWorker execute failure: " + std::string(pCallerName) + " -> " + e.what();
            executeError(strErrMsg);
        }
        catch (...)
        {
            const std::string strErrMsg = "AsyncWorker execute failure: " + std::string(pCallerName) + " -> unknown failure";
            executeError(strErrMsg);
        }
    }

    void cleanup() {
        try
        {
            VERBOSE_TRACE << "AsyncWorker: " << pCallerName << " started cleanup.";
            funcCleanup(ylResult);
            VERBOSE_TRACE << "AsyncWorker: " << pCallerName << " completed (and finished cleanup).";
        }
        catch (const std::exception &e)
        {
            ERROR_TRACE << "AsyncWorker cleanup failure with details " + std::string(e.what());
        }
        catch (...)
        {
            ERROR_TRACE << "AsyncWorker cleanup failure";
        }
    }

    // Executed when the async work is complete
    // this function will be run inside the main event loop
    // so it is safe to use JS engine data again
    void OnOK()
    {
        Napi::Env env = Env();
        Napi::HandleScope scope(env);
        NapiReturnType napiResult;
        bool callBackError = false;

        try
        {
            VERBOSE_TRACE << "AsyncWorker: " << pCallerName << " started mapping.";
            napiResult = funcToNapiMapper(env, ylResult);
            VERBOSE_TRACE << "AsyncWorker: " << pCallerName << " finished mapping.";

            callBackError = true;
            // TODO: Should Receiver().Value() be passed as first arg ?
            Callback().Call({ env.Undefined(), napiResult });
            callBackError = false;
        }
        catch (const YLReturnCodeException &e)
        {
            const std::string strErrMsg = "AsyncWorker ok failure: " + std::string(e.what());
            okError(env, strErrMsg, callBackError);
        }
        catch (const YLExcetion &e)
        {
            const std::string strErrMsg = "AsyncWorker ok failure: " + std::string(e.what());
            okError(env, strErrMsg, callBackError);
        }
        catch (const YLCallReturnCodeException &e)
        {
            const std::string strErrMsg = "AsyncWorker ok failure: " + std::string(e.what());
            executeError(strErrMsg);
        }
        catch (const std::exception &e)
        {
            const std::string strErrMsg = "AsyncWorker ok failure: " + std::string(pCallerName) + " -> " + e.what();
            okError(env, strErrMsg, callBackError);
        }
        catch (...)
        {
            const std::string strErrMsg = "AsyncWorker ok failure: " + std::string(pCallerName) + " -> unknown failure";
            okError(env, strErrMsg, callBackError);
        }

        cleanup();
    }

    // Executed when the async work fails.
    // this function will be run inside the main event loop
    // so it is safe to use JS engine data again
    void OnError(const Napi::Error& e)
    {
        try
        {
            Napi::Env env = e.Env();

            // Ugly code here: Unfortunately "e" is const, so we need to cast it away to modify the error:
            Napi::Error& mutableError = const_cast<Napi::Error&>(e);
            if (eRetCode != Yealink_ReturnCode::Return_Ok)
            {
                mutableError.Set(Napi::String::New(env, "code"), (Napi::Number::New(env, (int)eRetCode)));
            }

            Callback().Call(Receiver().Value(), std::initializer_list<napi_value> { mutableError.Value() });
        }
        catch (const std::exception &e)
        {
            ERROR_TRACE << "Failed calling error callback with details " + std::string(e.what());
        }
        catch (...)
        {
            ERROR_TRACE << "Failed calling error callback";
        }

        cleanup();
    }
};

/**
 * Async worker utility specilization that can run any yealink (lambda) work procedure asynchronously.
 *
 * Use this version when nothing should be returned from the async work other than a notification that
 * it is completed (by calling the callback).
 *
 * See full template of AsyncWorker template for additional information !
 */
template <>
class AsyncWorker<void,void> : public Napi::AsyncWorker
{
private:
    typedef Napi::AsyncWorker base;

    Yealink_ReturnCode eRetCode;
    const char * const pCallerName;
    const std::function<void()> funcWorker;
    const std::function<void()> funcCleanup;

public:
    /**
     * Create a new worker.
     * @pCallerName Thread-invariant name of function calling this worker used for documentation (should generally be called with __func__).
     * @funcJsResultCallback The javascript function to call back with the final result.
     * @funcWorker The async code (yealink sdk call) return a C data type (must NOT use any javascript / napi code or types).
     * @funcCleanup Synchronous code doing cleanup.
     */
    AsyncWorker(const char * const pCallerName,
                const Napi::Function &funcJsResultCallback,
                const std::function<void()>& funcWorker,
    const std::function<void()>& funcCleanup = []() {})
        : Napi::AsyncWorker(funcJsResultCallback),
          eRetCode(Yealink_ReturnCode::Return_Ok),
          pCallerName(pCallerName),
          funcWorker(funcWorker),
          funcCleanup(funcCleanup)
    {
    }

    AsyncWorker(const AsyncWorker&) = delete;
    ~AsyncWorker() {}

    void executeError(const std::string& strErrMsg, const Yealink_ReturnCode eRetCode = Yealink_ReturnCode::Return_Ok)
    {
        ERROR_TRACE << strErrMsg;
        SetError(strErrMsg);
        this->eRetCode = eRetCode;
    }

    // Executed inside the worker-thread.
    // It is not safe to access JS engine data structure
    // here, so everything we need for input and output
    // should go on `this`.
    void Execute()
    {
        try
        {
            VERBOSE_TRACE << pCallerName << " started async prodcedure call";
            funcWorker();
            VERBOSE_TRACE << pCallerName << " finished async procedure call";
        }
        catch (const YLReturnCodeException &e)
        {
            const std::string strErrMsg = "AsyncWorker execute failure: " + std::string(e.what());
            executeError(strErrMsg, e.GetYLApiReturnCode());
        }
        catch (const YLExcetion &e)
        {
            const std::string strErrMsg = "AsyncWorker execute failure: " + std::string(e.what());
            executeError(strErrMsg);
        }
        catch (const YLCallReturnCodeException &e)
        {
            const std::string strErrMsg = "AsyncWorker execute failure: " + std::string(e.what());
            executeError(strErrMsg);
        }
        catch (const std::exception &e)
        {
            const std::string strErrMsg = "AsyncWorker execute failure: " + std::string(pCallerName) + " -> " + e.what();
            executeError(strErrMsg);
        }
        catch (...)
        {
            const std::string strErrMsg = "AsyncWorker execute failure: " + std::string(pCallerName) + " -> unknown failure";
            executeError(strErrMsg);
        }
    }

    void cleanup()
    {
        try
        {
            VERBOSE_TRACE << "AsyncWorker: " << pCallerName << " started cleanup.";
            funcCleanup();
            VERBOSE_TRACE << "AsyncWorker: " << pCallerName << " completed (and finished cleanup).";
        }
        catch (const std::exception &e)
        {
            ERROR_TRACE << "AsyncWorker cleanup failure with details " + std::string(e.what());
        }
        catch (...)
        {
            ERROR_TRACE << "AsyncWorker cleanup failure";
        }
    }

    // Executed when the async work is complete
    // this function will be run inside the main event loop
    // so it is safe to use JS engine data again
    void OnOK()
    {
        Napi::Env env = Env();
        Napi::HandleScope scope(env);

        try
        {
            Callback().Call({ env.Undefined(), env.Undefined() });
        }
        catch (const std::exception &e)
        {
            ERROR_TRACE << "AsyncWorker ok callback failure with details " + std::string(e.what());
        }
        catch (...)
        {
            ERROR_TRACE << "AsyncWorker ok callback failure";
        }

        cleanup();
    }

    // Executed when the async work fails.
    // this function will be run inside the main event loop
    // so it is safe to use JS engine data again
    void OnError(const Napi::Error& e)
    {
        try
        {
            Napi::Env env = e.Env();

            // Ugly code here: Unfortunately "e" is const, so we need to cast it away to modify the error:
            Napi::Error& mutableError = const_cast<Napi::Error&>(e);
            if (eRetCode != Yealink_ReturnCode::Return_Ok) {
                mutableError.Set(Napi::String::New(env, "code"), (Napi::Number::New(env, (int)eRetCode)));
            }

            Callback().Call(Receiver().Value(), std::initializer_list<napi_value> { mutableError.Value() });
        }
        catch (const std::exception &e)
        {
            ERROR_TRACE << "AsyncWorker error callback failure with details " + std::string(e.what());
        }
        catch (...)
        {
            ERROR_TRACE << "AsyncWorker error callback failure";
        }

        cleanup();
    }
};

/**
* Does all the skeleton work for a simple call to a async yealink call without arguments returning
* a specific node type by a callback. The specific funcWorker function should do the actual async work, while funcToNapiMapper
* should convert the managed c++ result to a napi type that can be passed to the callback.
*
* @pCallerName Thread-invariant name of function calling this code used for documentation (should generally be called with __func__).
* @objInfo The javascript n-api function parameter informaton.
* @funcWorker The async code (yealink sdk call) return a C data type (must NOT use any javascript / napi code or types).
* @funcToNapiMapper Synchronous code converting the C data type to a javascript napi type.
* @funcCleanup Synchronous code doing cleanup.
**/
template <typename NapiReturnType, typename cppReturnType>
Napi::Value SimpleAsyncFunction(const char * const pCallerName,
                                const Napi::CallbackInfo &objInfo,
                                const std::function<cppReturnType ()>& funcWorker,
                                const std::function<NapiReturnType(const Napi::Env& env, const cppReturnType& retData)>& funcToNapiMapper,
const std::function<void(cppReturnType& retData)>& funcCleanup = [](cppReturnType& retData) {}
                               )
{
    Napi::Env env = objInfo.Env();

    if (NS_HELP::VerifyArgs(pCallerName, objInfo, { NS_HELP::FUNCTION }))
    {
        Napi::Function funcJsResultCallback = objInfo[0].As<Napi::Function>();

        auto *const pWorker = new AsyncWorker<cppReturnType, NapiReturnType>
        (pCallerName,
         funcJsResultCallback,
         funcWorker,
         funcToNapiMapper,
         funcCleanup
        );

        pWorker->Queue();
    }

    return env.Undefined();
};


/**
* Does all the skeleton work for a simple call to a async yealink call taking a deviceid as sole argument and returning
* a specific node type by a callback. The specific funcWorker function should do the actual async work, while funcToNapiMapper
* should convert the managed c++ result to a napi type that can be passed to the callback.
*
* @pCallerName Thread-invariant name of function calling this code used for documentation (should generally be called with __func__).
* @objInfo The javascript n-api function parameter informaton.
* @funcWorker The async code (yealink sdk call) return a C data type (must NOT use any javascript / napi code or types).
* @funcToNapiMapper Synchronous code converting the C data type to a javascript napi type.
* @funcCleanup Synchronous code doing cleanup.
**/
template <typename NapiReturnType, typename cppReturnType>
Napi::Value SimpleDeviceAsyncFunction(const char * const pCallerName,
                                      const Napi::CallbackInfo &objInfo,
                                      const std::function<cppReturnType (unsigned short)>& funcWorker,
                                      const std::function<NapiReturnType(const Napi::Env& env, const cppReturnType& retData)>& funcToNapiMapper,
const std::function<void(cppReturnType& retData)>& funcCleanup = [](cppReturnType& retData) {}
                                     )
{
    Napi::Env env = objInfo.Env();

    if (NS_HELP::VerifyArgs(pCallerName, objInfo, {NS_HELP::NUMBER, NS_HELP::FUNCTION}))
    {
        const unsigned short deviceId = objInfo[0].As<Napi::Number>().Int32Value();
        Napi::Function funcJsResultCallback = objInfo[1].As<Napi::Function>();

        auto *const pWorker = new AsyncWorker<cppReturnType, NapiReturnType>
        (pCallerName,
         funcJsResultCallback,
         std::bind(funcWorker, deviceId),
         funcToNapiMapper,
         funcCleanup
        );

        pWorker->Queue();
    }

    return env.Undefined();
};


/**
* Does all the skeleton work for a simple call to a async yealink setter taking a deviceid and a boolean as arguments with no result.
* The specific funcWorker function should do the actual async work
*
* @pCallerName Thread-invariant name of function calling this code used for documentation (should generally be called with __func__).
* @objInfo The javascript n-api function parameter informaton.
* @funcWorker The async code (yealink sdk call) return a C data type (must NOT use any javascript / napi code or types).
* @funcCleanup Synchronous code doing cleanup.
**/
inline Napi::Value SimpleDeviceAsyncBoolSetter(const char * const pCallerName,
        const Napi::CallbackInfo &objInfo,
        const std::function<void (unsigned short, bool enable)>& funcWorker,
const std::function<void()>& funcCleanup = []() {}
                                              )
{
    Napi::Env env = objInfo.Env();

    if (NS_HELP::VerifyArgs(pCallerName, objInfo, {NS_HELP::NUMBER, NS_HELP::BOOLEAN, NS_HELP::FUNCTION}))
    {
        const unsigned short deviceId = objInfo[0].As<Napi::Number>().Int32Value();
        const bool enable = objInfo[1].As<Napi::Boolean>().ToBoolean();
        Napi::Function funcJsResultCallback = objInfo[2].As<Napi::Function>();

        auto *const worker = new AsyncWorker<void, void>
        (pCallerName,
         funcJsResultCallback,
         std::bind(funcWorker, deviceId, enable),
         funcCleanup
        );

        worker->Queue();
    }

    return env.Undefined();
};



// Sync helpers ------------------------------------------------------------------------------------------

/**
 * Calls sync function returing values directly and throwing errors as JS exceptions (no callbacks).
 *
 * @pCallerName Thread-invariant name of function calling this code used for documentation (should generally be called with __func__).
 * @objInfo The javascript n-api function parameter informaton.
 * @func The sync code (yealink sdk call) return a n-api value of type T.
 */
template <typename T>
T SyncWrapper(const char * const pCallerName, const Napi::CallbackInfo& objInfo, const std::function<T (const char * const pCallerName, const Napi::CallbackInfo&)> func) {
    try
    {
        VERBOSE_TRACE << "SyncWrapper: " << pCallerName << " started sync function call.";
        auto result = func(pCallerName, objInfo);
        VERBOSE_TRACE << "SyncWrapper: " << pCallerName << " completed sync function call.";

        return result;
    }
    catch (const Napi::Error& e)
    {
        const std::string strErrMsg = "SyncWrapper execute failure: " + std::string(e.what());
        ERROR_TRACE << strErrMsg;
        throw; // Rethrow napi exceptions as they are handled.
    }
    catch (const YLReturnCodeException &e)
    {
        const std::string strErrMsg = "SyncWrapper execute failure: " + std::string(e.what());
        ERROR_TRACE << strErrMsg;

        Napi::Env env = objInfo.Env();
        Napi::Error objError = Napi::Error::New(env, strErrMsg);

        Yealink_ReturnCode eRetCode = e.GetYLApiReturnCode();
        if (eRetCode != Yealink_ReturnCode::Return_Ok) {
            objError.Set(Napi::String::New(env, "code"), (Napi::Number::New(env, (int)eRetCode)));
        }

        objError.ThrowAsJavaScriptException();
    }
    catch (const YLExcetion &e)
    {
        const std::string strErrMsg = "SyncWrapper execute failure: " + std::string(e.what());
        ERROR_TRACE << strErrMsg;
        Napi::Error::New(objInfo.Env(), strErrMsg).ThrowAsJavaScriptException();
    }
    catch (const YLCallReturnCodeException &e)
    {
        const std::string strErrMsg = "SyncWrapper execute failure: " + std::string(e.what());
        ERROR_TRACE << strErrMsg;

        Napi::Env env = objInfo.Env();
        Napi::Error objError = Napi::Error::New(env, strErrMsg);

        Yealink_CallReturn eRetCode = e.GetYLApiReturnCode();
        if (eRetCode != Yealink_CallReturn::CALL_RETURN_SUCCESS) {
            objError.Set(Napi::String::New(env, "code"), (Napi::Number::New(env, (int)eRetCode)));
        }

        objError.ThrowAsJavaScriptException();
    }
    catch (const std::exception &e)
    {
        const std::string strErrMsg = "SyncWrapper execute failure : " + std::string(pCallerName) + " -> " + e.what();
        ERROR_TRACE << strErrMsg;
        Napi::Error::New(objInfo.Env(), strErrMsg).ThrowAsJavaScriptException();
    }
    catch (...)
    {
        const std::string strErrMsg = "SyncWrapper execute failure : " + std::string(pCallerName) + " -> unknown error";
        ERROR_TRACE << strErrMsg;
        Napi::Error::New(objInfo.Env(), strErrMsg).ThrowAsJavaScriptException();
    }

    return objInfo.Env().Undefined(); // dummy
};


#define QUERY_ASYNC_WRAPPER_E(op, ...)          auto *const pWoreker = new AsyncWorker<void, void>(\
                                                pFuncName,\
                                                funcJsResultCallback,\
                                                [pFuncName, __VA_ARGS__]() {\
                                                    Yealink_ReturnCode eRetCode = Yealink_##op(__VA_ARGS__);\
                                                    if (Return_Ok != eRetCode)\
                                                        YLReturnCodeException::LogAndThrow(pFuncName, eRetCode);\
                                                });\
                                                if (pWoreker) \
                                                    pWoreker->Queue();

#endif // __ASYNC_HELPER_H__
