#include "remotemmi.h"
#include "asynchelper.h"
#include "help.h"
#include "commoninclude.h"


#define FUNC_ENV_SIGN                           const char * pFuncName = __func__;\
                                                Napi::Env env = objInfo.Env();


Napi::Value napi_SetRemoteMmiAction(const Napi::CallbackInfo& objInfo)
{
    FUNC_ENV_SIGN

    if (!NS_HELP::VerifyArgs(pFuncName, objInfo, {NS_HELP::NUMBER, NS_HELP::NUMBER, NS_HELP::OBJECT, NS_HELP::FUNCTION}))
    {
        return env.Undefined();
    }

    const unsigned short uDevId = (unsigned short)(objInfo[0].As<Napi::Number>().Int32Value());
    const RemoteMmiType eType = (RemoteMmiType)(objInfo[1].As<Napi::Number>().Int32Value());
    Napi::Object objAction = objInfo[2].As<Napi::Object>();
    Napi::Function funcJsResultCallback = objInfo[3].As<Napi::Function>();

    RemoteMmiActionOutput stAction;
    stAction.red = NS_HELP::getObjInt32OrDefault(objAction, "red", 0);
    stAction.green = NS_HELP::getObjInt32OrDefault(objAction, "green", 0);
    stAction.blue = NS_HELP::getObjInt32OrDefault(objAction, "blue", 0);
    stAction.sequence = NS_HELP::getObjEnumValueOrDefault<RemoteMmiSequence>(objAction, "sequence", RemoteMmiSequence::MMI_LED_SEQUENCE_OFF);

    QUERY_ASYNC_WRAPPER_E(SetRemoteMmiAction, uDevId, eType, stAction);

    return env.Undefined();
}

Napi::Value napi_ReleaseRemoteMmiFocus(const Napi::CallbackInfo& objInfo)
{
    FUNC_ENV_SIGN

    if (!NS_HELP::VerifyArgs(pFuncName, objInfo, {NS_HELP::NUMBER, NS_HELP::NUMBER, NS_HELP::FUNCTION}))
    {
        return env.Undefined();
    }

    const unsigned short uDevId = (unsigned short)(objInfo[0].As<Napi::Number>().Int32Value());
    const RemoteMmiType eType = (RemoteMmiType)(objInfo[1].As<Napi::Number>().Int32Value());
    Napi::Function funcJsResultCallback = objInfo[2].As<Napi::Function>();

    QUERY_ASYNC_WRAPPER_E(ReleaseRemoteMmiFocus, uDevId, eType);

    return env.Undefined();
}

Napi::Value napi_GetRemoteMmiFocus(const Napi::CallbackInfo& objInfo)
{
    FUNC_ENV_SIGN

    if (!NS_HELP::VerifyArgs(pFuncName, objInfo, {NS_HELP::NUMBER, NS_HELP::NUMBER, NS_HELP::NUMBER, NS_HELP::NUMBER, NS_HELP::FUNCTION}))
    {
        return env.Undefined();
    }

    const unsigned short uDevId = (unsigned short)(objInfo[0].As<Napi::Number>().Int32Value());
    const RemoteMmiType eType = (RemoteMmiType)(objInfo[1].As<Napi::Number>().Int32Value());
    const RemoteMmiInput eInput = (RemoteMmiInput)(objInfo[2].As<Napi::Number>().Int32Value());
    const RemoteMmiPriority ePriority = (RemoteMmiPriority)(objInfo[3].As<Napi::Number>().Int32Value());
    Napi::Function funcJsResultCallback = objInfo[4].As<Napi::Function>();

    QUERY_ASYNC_WRAPPER_E(GetRemoteMmiFocus, uDevId, eType, eInput, ePriority);

    return env.Undefined();
}

Napi::Value napi_IsRemoteMmiInFocus(const Napi::CallbackInfo& objInfo)
{
    FUNC_ENV_SIGN

    if (!NS_HELP::VerifyArgs(pFuncName, objInfo, {NS_HELP::NUMBER, NS_HELP::NUMBER, NS_HELP::FUNCTION}))
    {
        return env.Undefined();
    }

    const unsigned short uDevId = (unsigned short)(objInfo[0].As<Napi::Number>().Int32Value());
    const RemoteMmiType eType = (RemoteMmiType)(objInfo[1].As<Napi::Number>().Int32Value());
    Napi::Function funcJsResultCallback = objInfo[2].As<Napi::Function>();

    auto *const pWoreker = new AsyncWorker<bool, Napi::Boolean>(
        pFuncName,
        funcJsResultCallback,
    [pFuncName, uDevId, eType]() {
        bool bIsInFocus = false;
        Yealink_ReturnCode eRetCode = Yealink_IsRemoteMmiInFocus(uDevId, eType, &bIsInFocus);
        if (Return_Ok != eRetCode)
        {
            YLReturnCodeException::LogAndThrow(pFuncName, eRetCode);
        }

        return bIsInFocus;
    },
    [](const Napi::Env& objEnv, const bool bRet) {
        return Napi::Boolean::New(objEnv, bRet);
    });

    if (pWoreker)
    {
        pWoreker->Queue();
    }

    return env.Undefined();
}

Napi::Value napi_GetRemoteMmiTypes(const Napi::CallbackInfo& objInfo)
{
    FUNC_ENV_SIGN

    if (!NS_HELP::VerifyArgs(pFuncName, objInfo, {NS_HELP::NUMBER, NS_HELP::FUNCTION}))
    {
        return env.Undefined();
    }

    const unsigned short uDevId = (unsigned short)(objInfo[0].As<Napi::Number>().Int32Value());
    Napi::Function funcJsResultCallback = objInfo[1].As<Napi::Function>();

    struct RemoteMmiTyps
    {
        int iCnt = 0;
        RemoteMmiDefinition* pDef = nullptr;
    };

    auto *const pWoreker = new AsyncWorker<RemoteMmiTyps, Napi::Object>(
        pFuncName,
        funcJsResultCallback,
    [pFuncName, uDevId]() {
        RemoteMmiTyps stTypes;
        Yealink_ReturnCode eRetCode = Yealink_GetRemoteMmiTypes(uDevId, &stTypes.pDef, &stTypes.iCnt);
        VERBOSE_TRACE << "get remote mmi types, ret code:" << eRetCode;
        if (Return_Ok != eRetCode)
        {
            YLReturnCodeException::LogAndThrow(pFuncName, eRetCode);
        }

        VERBOSE_TRACE << "get remote mmi types, cnt:" << stTypes.iCnt;
        return stTypes;
    },
    [](const Napi::Env& objEnv, const RemoteMmiTyps stRet) {
        Napi::Array objTypes = Napi::Array::New(objEnv, stRet.iCnt);
        if (stRet.iCnt > 0 && stRet.pDef != nullptr)
        {
            for (int i = 0; i < stRet.iCnt; ++ i)
            {
                Napi::Object objVal = Napi::Object::New(objEnv);
                objVal.Set(Napi::String::New(objEnv, "type"), Napi::Number::New(objEnv, stRet.pDef[i].type));
                objVal.Set(Napi::String::New(objEnv, "priorityMask"), Napi::Number::New(objEnv, stRet.pDef[i].priorityMask));
                objVal.Set(Napi::String::New(objEnv, "sequenceMask"), Napi::Number::New(objEnv, stRet.pDef[i].sequenceMask));
                objVal.Set(Napi::String::New(objEnv, "inputMask"), Napi::Number::New(objEnv, stRet.pDef[i].inputMask));

                Napi::Object objOutput = Napi::Object::New(objEnv);
                objOutput.Set(Napi::String::New(objEnv, "red"), Napi::Boolean::New(objEnv, stRet.pDef[i].output.red));
                objOutput.Set(Napi::String::New(objEnv, "green"), Napi::Boolean::New(objEnv, stRet.pDef[i].output.green));
                objOutput.Set(Napi::String::New(objEnv, "blue"), Napi::Boolean::New(objEnv, stRet.pDef[i].output.blue));
                objVal.Set(Napi::String::New(objEnv, "output"), objOutput);

                objTypes.Set(i, objVal);
            }
        }

        return objTypes;
    },
    [](RemoteMmiTyps stRet) {
        if (stRet.pDef)
        {
            Yealink_FreeRemoteMmiTypes(stRet.pDef);
        }
    });

    if (pWoreker)
    {
        pWoreker->Queue();
    }

    return env.Undefined();
}
