//
// Created by lixingchen on 2019/12/30.
//

#ifdef U_PLATFORM_ANDROID


#ifndef COOKENGINE_NATIVE_UTILS_H
#define COOKENGINE_NATIVE_UTILS_H


#include <android/log.h>
#include <string.h>
#include <vector>

#include "../../include/CookVariant.h"
#include "../../include/CookFunItem.h"
#include "../../include/CookLog.h"
#include "../../include/CookTaskInfoBase.h"
#include "cJSONex.h"

#define U_LOG_ID    "ucookengine-jni" // 这个是自定义的LOG的标识
#define U_LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,U_LOG_ID,__VA_ARGS__) // 定义LOGD类型
#define U_LOGI(...)  __android_log_print(ANDROID_LOG_INFO,U_LOG_ID,__VA_ARGS__) // 定义LOGI类型
//#define U_LOGW(...)  __android_log_print(ANDROID_LOG_WARN,U_LOG_ID,__VA_ARGS__) // 定义LOGW类型
#define U_LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,U_LOG_ID,__VA_ARGS__) // 定义LOGE类型
//#define U_LOGF(...)  __android_log_print(ANDROID_LOG_FATAL,U_LOG_ID,__VA_ARGS__) // 定义LOGF类型

using namespace ns_catering;

/** 用于宏函数的保护  */
#if !(defined (U_STMT_START) && defined (U_STMT_END))
#define U_STMT_START  do
#define U_STMT_END    while (0)
#endif

// 仅用作逻辑判断，不打印日志
#define U_RET_VAL_IF_FAIL(expr, val) U_STMT_START{    \
    if (!(expr)) {  \
        return val; \
    }   \
}U_STMT_END

// 仅用作逻辑判断，不打印日志
#define U_RET_IF_FAIL(expr) U_STMT_START{    \
    if (!(expr)) {  \
        return; \
    }   \
}U_STMT_END

static int8_t u_check_bytearray(JNIEnv *env, const jbyteArray bytes)
{
    if (0 == bytes)
    {
        COOK_LOG_ERROR("bytes is null. ");
        return 0;
    }
    else if (env->GetArrayLength(bytes) <= 0)
    {
        COOK_LOG_ERROR("bytes length is zero. ");
        return 0;
    }

    return 1;
}

static jbyteArray u_bytes_to_jbytearray(JNIEnv *env, const uint8_t* pInput, int32_t iInputLen)
{
    jbyteArray outBytes = 0;

    if (0 == pInput || iInputLen <= 0)
    {
        COOK_LOG_ERROR("input is null or len is zero .");
        return 0;
    }

    outBytes = env->NewByteArray(iInputLen);
    env->SetByteArrayRegion(outBytes, 0, iInputLen, (const jbyte* )pInput);

    return outBytes;
}

static uint8_t* u_jbytearray_tobytes(JNIEnv *env, const jbyteArray input, int8_t bStr, int32_t* pLen)
{
    jsize jInputLen = 0;
    jsize jAllocLen = 0;
    uint8_t* pRetData = 0;

    if (0 == input)
    {
        COOK_LOG_ERROR("input bytearray is null .");
        return 0;
    }
    jInputLen = env->GetArrayLength(input);
    if (jInputLen <= 0)
    {
        COOK_LOG_ERROR("bytearray is zero .");
        return 0;
    }
    jAllocLen = jInputLen;
    if (bStr)
    {
        jAllocLen += 1; // 包含一个\0
    }
    pRetData = (uint8_t*)calloc(jAllocLen, 1);
    env->GetByteArrayRegion(input, 0, jInputLen, (jbyte* )pRetData);
    if (pLen != 0)
    {
        *pLen = jAllocLen;
    }

    return pRetData;
}

// java hashmap转C++ map
static std::map<long, std::string> javaMapToCMap(JNIEnv *env, jobject hashMapInfo)
{
    std::map<long, std::string> retMap;
    if (0 == hashMapInfo)
        return retMap;

    // 获取HashMap类entrySet()方法ID
    jclass hashmapClass = env->FindClass("java/util/HashMap");
    jmethodID entrySetMID = env->GetMethodID(hashmapClass, "entrySet", "()Ljava/util/Set;");
    // 调用entrySet()方法获取Set对象
    jobject setObj = env->CallObjectMethod(hashMapInfo, entrySetMID);
    // 获取Set类中iterator()方法ID
    jclass setClass = env->FindClass("java/util/Set");
    jmethodID iteratorMID = env->GetMethodID(setClass, "iterator", "()Ljava/util/Iterator;");
    // 调用iterator()方法获取Iterator对象
    jobject iteratorObj = env->CallObjectMethod(setObj, iteratorMID);
    // 获取Iterator类中hasNext()方法ID
    // 用于while循环判断HashMap中是否还有数据
    jclass iteratorClass = env->FindClass("java/util/Iterator");
    jmethodID hasNextMID = env->GetMethodID(iteratorClass, "hasNext", "()Z");
    // 获取Iterator类中next()方法ID
    // 用于读取HashMap中的每一条数据
    jmethodID nextMID = env->GetMethodID(iteratorClass, "next", "()Ljava/lang/Object;");
    // 获取Map.Entry类中getKey()和getValue()的方法ID
    // 用于读取“课程-分数”键值对，注意：内部类使用$符号表示
    jclass entryClass = env->FindClass("java/util/Map$Entry");
    jmethodID getKeyMID = env->GetMethodID(entryClass, "getKey", "()Ljava/lang/Object;");
    jmethodID getValueMID = env->GetMethodID(entryClass, "getValue", "()Ljava/lang/Object;");
    // HashMap只能存放引用数据类型，不能存放int等基本数据类型
    // 使用Long类的longValue()方法获取long数据
    jclass longClass = env->FindClass("java/lang/Long");
    jmethodID longKeyMID = env->GetMethodID(longClass, "longValue", "()J");

    // 循环检测HashMap中是否还有数据
    while (env->CallBooleanMethod(iteratorObj, hasNextMID)) {
        // 读取一条数据
        jobject entryObj = env->CallObjectMethod(iteratorObj, nextMID);

        // 提取数据中key值
        jobject keyObj = (jobject) env->CallObjectMethod(entryObj, getKeyMID);
        jlong iKey = (jlong) env->CallLongMethod(keyObj, longKeyMID);
        if (keyObj == NULL)   // HashMap允许null类型
            continue;
        COOK_LOG_INFO("key=%ld.", iKey);

        // 提取数据中value值
        jstring valueObj = (jstring)env->CallObjectMethod(entryObj, getValueMID);
        if (valueObj == NULL)
            continue;
        // jstring转C风格字符串
        const char * pValueStr = env->GetStringUTFChars(valueObj, NULL);
        COOK_LOG_INFO("value=%s.", pValueStr);
        retMap.insert(std::make_pair(iKey, pValueStr));

        // 释放UTF字符串资源
        env->ReleaseStringUTFChars(valueObj, pValueStr);
        // 释放JNI局部引用资源
        env->DeleteLocalRef(entryObj);
        env->DeleteLocalRef(keyObj);
        env->DeleteLocalRef(valueObj);
    }

    // 释放JNI局部引用: jclass jobject
    env->DeleteLocalRef(hashmapClass);
    env->DeleteLocalRef(setObj);
    env->DeleteLocalRef(setClass);
    env->DeleteLocalRef(iteratorObj);
    env->DeleteLocalRef(iteratorClass);
    env->DeleteLocalRef(entryClass);
    env->DeleteLocalRef(longClass);

    return retMap;
}

static bool isInstanceOf(JNIEnv *env, jobject obj, const char* pClsName)
{
    // 获取class对象
    jclass clz = env->FindClass(pClsName);
    if (0 == clz)
    {
        COOK_LOG_ERROR("failed to get class=%s.", pClsName);
        return false;
    }
    bool bRet = env->IsInstanceOf(obj, clz);
    // 释放局部资源
    env->DeleteLocalRef(clz);

    return bRet;
}

static jobject callVoidObjectMethod(JNIEnv *env, jobject jThis, const char *pMethodName,
                                    const char *pMethodSignature)
{
    if (0 == env || 0 == jThis || 0 == pMethodName || 0 == pMethodSignature)
    {
        COOK_LOG_ERROR("call %s, invalid params.", __FUNCTION__);
        return 0;
    }
    // 获取class
    jclass clazz = env->GetObjectClass(jThis);
    if (0 == clazz)
    {
        COOK_LOG_ERROR("can not get class, methodName=%s, signature=%s.", pMethodName, pMethodSignature);
        return 0;
    }
    // first get method id by GetMethodID(JNIEnv, jclass, methodName, methodSignature)
    jmethodID jmID = env->GetMethodID(clazz, pMethodName, pMethodSignature);
    if (0 == jmID)
    {
        COOK_LOG_ERROR("can not get methodid, methodName=%s, signature=%s.", pMethodName, pMethodSignature);
        return 0;
    }
    // call method
    jobject retObj = env->CallObjectMethod (jThis, jmID);
    // 释放资源
    env->DeleteLocalRef(clazz);

    return retObj;
}

std::string javaStrToCStr(JNIEnv *env, jstring valueObj)
{
    if (0 == valueObj)
    {
        COOK_LOG_ERROR("string is null.");
        return "";
    }
    const char * pValueStr = env->GetStringUTFChars(valueObj, NULL);
    // 释放UTF字符串资源
    std::string retStr = pValueStr;
    env->ReleaseStringUTFChars(valueObj, pValueStr);

    return retStr;
}



static int jobjectToInt(JNIEnv *env, jobject obj)
{
    jclass intClass = env->FindClass("java/lang/Integer");
    jmethodID intMID = env->GetMethodID(intClass, "intValue", "()I");
    env->DeleteLocalRef(intClass);

    return env->CallIntMethod(obj, intMID);
}

static double jobjectToDouble(JNIEnv *env, jobject obj)
{
    jclass intClass = env->FindClass("java/lang/Double");
    jmethodID intMID = env->GetMethodID(intClass, "doubleValue", "()D");
    env->DeleteLocalRef(intClass);

    return env->CallDoubleMethod(obj, intMID);
}

static std::vector<unsigned char> jobjectToVector(JNIEnv *env, jstring obj)
{
    const char * pValueStr = env->GetStringUTFChars((jstring )obj, NULL);
    if (0 == pValueStr)
    {
        COOK_LOG_ERROR("invalid string, null.");
        return std::vector<unsigned char>();
    }
    std::vector<unsigned char> vc(pValueStr, pValueStr + strlen(pValueStr));
    env->ReleaseStringUTFChars(obj, pValueStr);

    return vc;
}

static CCookVariant jobjectToVaraint(JNIEnv *env, jobject obj)
{
    if (isInstanceOf(env, obj, "java/lang/Integer"))
    {
        // int
        int iVal = jobjectToInt(env, obj);
        COOK_LOG_INFO("int type, val=%d.", iVal);
        return CCookVariant(iVal);
    }
    else if (isInstanceOf(env, obj, "java/lang/Double"))
    {
        // double
        double dVal = jobjectToDouble(env, obj);
        COOK_LOG_INFO("Double type, val=%lf.", dVal);
        return CCookVariant(dVal);
    }
    else if (isInstanceOf(env, obj, "java/lang/String"))
    {
        // string
        std::vector<unsigned char> vcVal = jobjectToVector(env, (jstring )obj);
        COOK_LOG_INFO("String type, valSize=%ld.", vcVal.size());
        return CCookVariant(vcVal);
    }
    else
    {
        COOK_LOG_ERROR("invalid type.");
        return CCookVariant();
    }
}

static jstring cstringToJString(JNIEnv* env, const std::string& str)
{
    return env->NewStringUTF(str.c_str());
}

static jobject cfunitenToJfunitem(JNIEnv* env, jclass itemClz, CCookFunItem* pItem)
{
    jmethodID setTrueProductIdMid = env->GetMethodID(itemClz, "setTrueProductId", "(J)V");
    jmethodID setFunNameMid = env->GetMethodID(itemClz, "setFunName", "(Ljava/lang/String;)V");
    jmethodID setParamsMid = env->GetMethodID(itemClz, "setParams", "(Ljava/lang/String;)V");
    jmethodID setCmdIdMid = env->GetMethodID(itemClz, "setCmdId", "(Ljava/lang/String;)V");
    jmethodID ctrMid = env->GetMethodID(itemClz, "<init>", "()V");
    // 创建对象
    jobject itemObj = env->NewObject(itemClz, ctrMid);
    if (0 == itemObj)
    {
        COOK_LOG_ERROR("failed to new item object.");
        return 0;
    }

    // 赋值产品Id
    env->CallVoidMethod(itemObj, setTrueProductIdMid, (jlong )pItem->getTrueProductId());
    // 赋值函数名称
    env->CallVoidMethod(itemObj, setFunNameMid, cstringToJString(env, pItem->getFunName()));
    // 赋值函数参数
    const std::vector<CCookVariant*> funParams = pItem->getFunParams();
    std::string funOneParam = "";
    if (funParams.size() > 0)
    {
        funOneParam = funParams[0]->ToString();
    }
    env->CallVoidMethod(itemObj, setParamsMid, cstringToJString(env, funOneParam));
    // 赋值函数命令码
    env->CallVoidMethod(itemObj, setCmdIdMid, cstringToJString(env, pItem->GetCmdId()));
    return itemObj;
}

static void freeJobjectarray(JNIEnv* env, jobjectArray objList)
{
    if (0 == env || 0 == objList)
    {
        return;
    }
    // 获取数组的长度
    int iSize = env->GetArrayLength(objList);
    for (int i = 0; i < iSize; ++i)
    {
        jobject obj = env->GetObjectArrayElement(objList, i);
        env->DeleteLocalRef(obj);
    }
    env->DeleteLocalRef(objList);
}

static jobjectArray cfunitemsToJfunitems(JNIEnv *env, jclass jItemClz, const std::vector<CCookFunItem*>& funItemList)
{
    if (0 == env || 0 == jItemClz || funItemList.size() <= 0)
    {
        COOK_LOG_ERROR("funitemList size is zero.");
        return 0;
    }
    else
    {
        COOK_LOG_INFO("funitemList size=%d is not zero, env=%x.", funItemList.size(), env);
    }
    // 创建对象数组
    jobjectArray jRetArray =  env->NewObjectArray(funItemList.size(), jItemClz, NULL);//(jobjectArray)env->NewObjectArray(size, detRetClass, NULL);
    for (int i = 0; i < funItemList.size(); ++i)
    {
        jobject item = cfunitenToJfunitem(env, jItemClz, funItemList[i]);
        if (0 == item)
        {
            goto ErrOut;
        }
        // 加入到数组中
        env->SetObjectArrayElement(jRetArray, i, item);
    }

    return  jRetArray;

ErrOut:
    return 0;
}


static jbyteArray cTaskInfoToJTaskInfoTo(JNIEnv *env, const std::list<task_item_t>& taskItemList)
{
    if (0 == env || taskItemList.size() <= 0)
    {
        COOK_LOG_ERROR("taskItemList size is zero.");
        return 0;
    }
    else
    {
        COOK_LOG_INFO("funitemList size=%d is not zero, env=%x.", taskItemList.size(), env);
    }

    cJSON* cjsonTaskArray = cJSON_CreateArray();
    //任务列表
    for (std::list<task_item_t>::const_iterator taskIter = taskItemList.begin();
         taskIter != taskItemList.end(); ++taskIter)
    {
        cJSON* cjsonTaskObject = cJSON_CreateObject();
        //任务id
        cJSON_AddItemToObject(cjsonTaskObject, "id", cJSON_CreateString(taskIter->id.c_str()));

        cJSON* cjsonStepArray = cJSON_CreateArray();
        // 步骤列表
        for (std::list<thread_item_t>::const_iterator stepIter = taskIter->step.begin();
             stepIter != taskIter->step.end(); ++stepIter) {
            //步骤
            cJSON* cjsonStepObject = cJSON_CreateObject();
            //步骤id
            cJSON_AddItemToObject(cjsonStepObject, "id", cJSON_CreateString(stepIter->id.c_str()));
            cJSON_AddItemToObject(cjsonStepObject, "time", cJSON_CreateString(stepIter->time.c_str()));
            cJSON_AddNumberToObject(cjsonStepObject, "visibleStep", stepIter->visibleStep);
            cJSON_AddNumberToObject(cjsonStepObject, "visibleId", stepIter->visibleId);

            //output array
            cJSON* cjsonOutputArray = cJSON_CreateArray();
            for (std::list<output_item_t>::const_iterator outputIter = stepIter->output.begin();
                 outputIter != stepIter->output.end(); ++outputIter)
            {
                cJSON* cjsonOutputObject = cJSON_CreateObject();
                cJSON_AddItemToObject(cjsonOutputObject, "cmdId", cJSON_CreateString(outputIter->cmdid.c_str()));
                cJSON_AddItemToObject(cjsonOutputObject, "method", cJSON_CreateString(outputIter->method.c_str()));
                cJSON_AddItemToObject(cjsonOutputObject, "devindex", cJSON_CreateString(outputIter->devindex.c_str()));

                //par
                cJSON* cjsonParObject = cJSON_CreateObject();
                for (std::map<std::string, std::string>::const_iterator parIter = outputIter->par.begin();
                     parIter != outputIter->par.end(); ++parIter)
                {
                    cJSON_AddItemToObject(cjsonParObject, parIter->first.c_str(), cJSON_CreateString(parIter->second.c_str()));
                }
                cJSON_AddItemToObject(cjsonOutputObject, "par", cjsonParObject);

                //将output item 添加至 output array
                cJSON_AddItemToArray(cjsonOutputArray, cjsonOutputObject);
            }
            //将output array 添加至 step
            cJSON_AddItemToObject(cjsonStepObject, "output", cjsonOutputArray);


            //ingredient array
            cJSON* cjsonIngredientArray = cJSON_CreateArray();
            for (std::list<ingredient_item_t>::const_iterator ingredientIter = stepIter->ingredient.begin();
                 ingredientIter != stepIter->ingredient.end(); ++ingredientIter)
            {
                cJSON* cjsonIngredientObject = cJSON_CreateObject();
                cJSON_AddItemToObject(cjsonIngredientObject, "ssnid", cJSON_CreateString(ingredientIter->ssnid.c_str()));
                cJSON_AddItemToObject(cjsonIngredientObject, "vol", cJSON_CreateString(ingredientIter->vol.c_str()));

                //将ingredient item 添加至 ingredient array
                cJSON_AddItemToArray(cjsonIngredientArray, cjsonIngredientObject);
            }
            //ingredient array 添加至 step
            cJSON_AddItemToObject(cjsonStepObject, "ingredient", cjsonIngredientArray);


            //trigger  array
            cJSON* cjsonTriggerArray = cJSON_CreateArray();
            for (std::list<trigger_item_t>::const_iterator triggerIter = stepIter->trigger.begin();
                 triggerIter != stepIter->trigger.end(); ++triggerIter)
            {
                cJSON* cjsonTriggerObject = cJSON_CreateObject();
                cJSON_AddItemToObject(cjsonTriggerObject, "cmdid", cJSON_CreateString(triggerIter->cmdid.c_str()));
                cJSON_AddItemToObject(cjsonTriggerObject, "method", cJSON_CreateString(triggerIter->method.c_str()));

                //trigger par array
                cJSON* cjsonTriggerParArray = cJSON_CreateArray();
                for (std::list<trigger_func_t>::const_iterator triggerParIter = triggerIter->par.begin();
                     triggerParIter != triggerIter->par.end(); ++triggerParIter) {
                    cJSON *cjsonTriggerParObject = cJSON_CreateObject();
                    cJSON_AddItemToObject(cjsonTriggerParObject, "devid", cJSON_CreateString(triggerParIter->devid.c_str()));
                    cJSON_AddItemToObject(cjsonTriggerParObject, "sensor", cJSON_CreateString(triggerParIter->sensor.c_str()));

                    //将trigger par item 添加至 trigger par array
                    cJSON_AddItemToArray(cjsonTriggerParArray, cjsonTriggerParObject);
                }
                //将trigger par array 添加至 trigger par
                cJSON_AddItemToArray(cjsonTriggerObject, cjsonTriggerParArray);


                //将trigger item 添加至 trigger array
                cJSON_AddItemToArray(cjsonTriggerArray, cjsonTriggerObject);
            }
            //trigger array 添加至 step
            cJSON_AddItemToObject(cjsonStepObject, "trigger", cjsonTriggerArray);



            //将step item 添加至 step array
            cJSON_AddItemToArray(cjsonStepArray, cjsonStepObject);
        }
        //将step array 添加至 task item
        cJSON_AddItemToObject(cjsonTaskObject, "step", cjsonStepArray);

        cJSON_AddItemToArray(cjsonTaskArray, cjsonTaskObject);
    }

    char* cTaskListJsonStr = cJSON_PrintUnformatted(cjsonTaskArray);
    cJSON_Delete(cjsonTaskArray);

    jbyteArray ret = u_bytes_to_jbytearray(env, (uint8_t*)cTaskListJsonStr, strlen(cTaskListJsonStr));
    free(cTaskListJsonStr);
    return ret;
}

static jboolean callBooleanMethod(JNIEnv* env, jobject obj, const char* name, const char* sig, ...)
{
    if (0 == env || 0 == name || 0 == sig || 0 == obj)
    {
        COOK_LOG_ERROR("invalid params.");
        return 0;
    }
    jclass clazz = env->GetObjectClass(obj);
    jmethodID jmID = env->GetMethodID(clazz, name, sig);

    va_list args;
    va_start(args, sig);
    jboolean  bRet = env->CallBooleanMethodV(obj, jmID, args);
    va_end(args);
    env->DeleteLocalRef(clazz);

    return bRet;
}

void SplitString(const std::string& s, std::vector<std::string>& v, const std::string& c)
{
    std::string::size_type pos1, pos2;
    pos2 = s.find(c);
    pos1 = 0;
    while(std::string::npos != pos2)
    {
        v.push_back(s.substr(pos1, pos2-pos1));
        pos1 = pos2 + c.size();
        pos2 = s.find(c, pos1);
     }
    if(pos1 != s.length())
    {
        v.push_back(s.substr(pos1));
    }
}

// str的格式为： key1=value1;key2=value2;
static jboolean jstringToMap(JNIEnv* env, const jstring& str, std::map<std::string, std::string>& paramsMap)
{
    std::string cStr = javaStrToCStr(env, str);
    std::vector<std::string> results;
    // 获取所有的键值对
    SplitString(cStr, results, ";");
    for (int i = 0; i < results.size(); ++i)
    {
        std::vector<std::string> keyValue;
        SplitString(results[i], keyValue, "=");
        if (2 != keyValue.size())
        {
            // 不是有效的键值对
            return 0;
        }
        paramsMap.insert(std::make_pair(keyValue[0], keyValue[1]));
    }

    return 1;
}

#endif //COOKENGINE_NATIVE_UTILS_H

#endif //U_PLATFORM_A