#include "help.h"


namespace NS_HELP
{

static inline std::string ToString(FormalParameterType type) {
    switch (type) {
    case _VOID:
        return "void";
        break;
    case BOOLEAN:
        return "boolean";
        break;
    case NUMBER:
        return "number";
        break;
    // BIGINT: return "bigint"; break;
    case STRING:
        return "string";
        break;
    case SYMBOL:
        return "symbol";
        break;
    case ARRAYBUFFER:
        return "arraybuffer";
        break;
    case TYPEDARRAY:
        return "typedarray";
        break;
    case OBJECT:
        return "object";
        break;
    case ARRAY:
        return "array";
        break;
    case FUNCTION:
        return "function";
        break;
    case PROMISE:
        return "promise";
        break;
    case DATAVIEW:
        return"dataview";
        break;
    case BUFFER:
        return "buffer";
        break;
    case EXTERNAL:
        return "external";
        break;
    case OBJECT_OR_STRING:
        return "object | string";
        break;
    default:
        return "???";
        break;
    }
}

static inline bool VerifyValueType(const Napi::Value& value, FormalParameterType type)
{
    switch (type)
    {
    case _VOID:
        return value.IsNull() || value.IsUndefined();
    case BOOLEAN:
        return value.IsBoolean();
    case NUMBER:
        return value.IsNumber();
    // BIGINT: return value.IsBigInt(); break;
    case STRING:
        return value.IsString();
    case SYMBOL:
        return value.IsSymbol();
    case ARRAYBUFFER:
        return value.IsArrayBuffer();
    case TYPEDARRAY:
        return value.IsTypedArray();
    case OBJECT:
        return value.IsObject();
    case ARRAY:
        return value.IsArray();
    case FUNCTION:
        return value.IsFunction();
    case PROMISE:
        return value.IsPromise();
    case DATAVIEW:
        return value.IsDataView();
    case BUFFER:
        return value.IsBuffer();
    case EXTERNAL:
        return value.IsExternal();
    case OBJECT_OR_STRING:
        return value.IsObject() || value.IsString();
    default:
        throw std::runtime_error(std::string("Unknown enum type value " + std::to_string(type)));
    }

    return false;
}

bool VerifyArgs(const char * const strCallFunctionName, const Napi::CallbackInfo& objInfo, std::initializer_list<FormalParameterType> listExpectedArgumentTypes) {
    const Napi::Env env = objInfo.Env();

    if (objInfo.Length() != listExpectedArgumentTypes.size()) {
        const std::string errMsg = "Wrong number of arguments to " + std::string(strCallFunctionName) + " (got #" + std::to_string(objInfo.Length()) + ",  expected #" + std::to_string(listExpectedArgumentTypes.size()) + ")";
        //LOG_ERROR_(LOGINSTANCE) << errMsg;
        Napi::TypeError::New(env, errMsg).ThrowAsJavaScriptException();
        return false;
    }

    std::initializer_list<FormalParameterType>::iterator it = listExpectedArgumentTypes.begin();
    for (int j = 0; it != listExpectedArgumentTypes.end(); ++ it, j ++)
    {
        //std::cout << "is string:" << objInfo[j].IsString() << ", is bumber:" << objInfo[j].IsNumber() << ", expect:" << ToString(*it) << std::endl;;
        if (!VerifyValueType(objInfo[j], *it))
        {
            const std::string errMsg = "Wrong type of argument to " + std::string(strCallFunctionName) + " #" +  std::to_string(j) + ". Got value of unexpected " + ToString(*it) + " type)";
            //LOG_ERROR_(LOGINSTANCE) << errMsg;
            Napi::TypeError::New(env, errMsg).ThrowAsJavaScriptException();
            return false;
        }
    }

    return true;
}

/**
  * Create a C-string from a std:string
  */
char * NewCString(const std::string& strSrc)
{
    char * pDst = new char[strSrc.length() + 1];
    strncpy(pDst, strSrc.c_str(), strSrc.length());
    pDst[strSrc.length()] = 0;

    return pDst;
}

/**
 * Create a C-string from a napi string/null object.
 */
char * NewCString(const Napi::Value& objSrc)
{
    if (objSrc.IsString())
    {
        Napi::String strSrc = objSrc.As<Napi::String>();
        return NewCString((std::string)strSrc);
    }

    return nullptr;
}

}
