//
// Created by #Suyghur, on 2021/06/09.
//



#include <jni.h>
#include <toolkit.h>
#include <comm_map.h>
#include <params_kit.h>
#include <request_kit.h>
#include <md5/md5.h>
#include "include/logger.h"
#include "aes/aes_utils.h"

static void GetAesKey(JNIEnv *env, jobject thiz) {
    uint8_t *aes_key = AesUtils::GetKey();
    uint8_t *iv = AesUtils::GetIv(aes_key);
    LOGD("%s", aes_key);
    LOGD("%s", iv);
    LOGD("------ invoke free() ------ ");
    free(aes_key);
    free(iv);
//    free((void *) aes_key);
//    free((void *) iv);
    aes_key = nullptr;
    iv = nullptr;
    LOGD("%s", aes_key);
    LOGD("%s", iv);
}

//static jstring AesEncrypt(JNIEnv *env, jobject thiz, jstring src) {
//    const char *src_ = env->GetStringUTFChars(src, JNI_FALSE);
//    char *raw = AesUtils::Encrypt(src_);
//    LOGD("%s", raw);
//
//    env->ReleaseStringUTFChars(src, src_);
//
//    jstring result = ToolKit::GetJString(env, raw);
//    free(raw);
//
//    return result;
//}

static jstring AesDecrypt(JNIEnv *env, jobject thiz, jstring raw, jstring key) {
    const char *raw_ = env->GetStringUTFChars(raw, JNI_FALSE);
    const auto *key_ = reinterpret_cast<const uint8_t *>(env->GetStringUTFChars(key, JNI_FALSE));
    char *src = AesUtils::Decrypt(raw_, key_);
    env->ReleaseStringUTFChars(raw, raw_);
    jstring result = ToolKit::GetJString(env, src);
    free(src);
    return result;
}


//static jstring RsaEncrypt(JNIEnv *env, jobject thiz, jstring src) {
//    const char *src_ = env->GetStringUTFChars(src, JNI_FALSE);
//    std::string key = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCAVatPhPaZtxBkLBZDnOLf7no6VfnjBRMXhpOnWmkkDX4zurB/OKisYDFnR0UUVnKn717+absfLlDk9HZWBcTMznsUju9fuXlu3Elr8HeI3en7E0KIQzxkY1GT1+qtZ+tCjWuyMmUw4vWpO8/MRU3f6nc4io3w+7N+dP24BIo0ZwIDAQAB";
//    std::string raw = RsaUtils::EncryptByPublicKey(key, src_);
////    LOGD("base64 raw : %s", raw.c_str());
//    env->ReleaseStringUTFChars(src, src_);
//    jstring result = ToolKit::GetJString(env, raw.c_str());
//    return result;
//}
//
//static jstring RsaDecrypt(JNIEnv *env, jobject thiz, jstring raw) {
//    const char *raw_ = env->GetStringUTFChars(raw, JNI_FALSE);
////    std::string key = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCAVatPhPaZtxBkLBZDnOLf7no6VfnjBRMXhpOnWmkkDX4zurB/OKisYDFnR0UUVnKn717+absfLlDk9HZWBcTMznsUju9fuXlu3Elr8HeI3en7E0KIQzxkY1GT1+qtZ+tCjWuyMmUw4vWpO8/MRU3f6nc4io3w+7N+dP24BIo0ZwIDAQAB";
//    std::string key = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCbROlODv7iVaFhRAomQ9kSoV+whoebwVUDWUd968V8jbLQg0bAh6ad22O1bo33f4zNmIuDzdQMGOVgRyuvKsS9GyE+gh4XPDpSUmi/bZTl0wr8Y/yTi8eW+sXxn6Ao4oo4bg+qPewUxjTS2u4Pe1Zl3IC48HFALTAo0PSSXS5b7wIDAQAB";
//    std::string src = RsaUtils::DecryptByPublicKey2(env, key, raw_);
//    env->ReleaseStringUTFChars(raw, raw_);
//    jstring result = ToolKit::GetJString(env, src.c_str());
//    return result;
//}

static void InitSdkDrive(JNIEnv *env, jobject thiz, jobject context) {
    CommMap::GetInstance()->Init(env, context);
    Logger::is_debug = ParamsKit::GetDebugModel(env, context);
    Logger::D(env, ToolKit::ToJsonString(CommMap::GetInstance()->comm_params));
}

static void SetParam(JNIEnv *env, jobject thiz, jstring key, jstring value) {
    std::string key_ = ToolKit::JString2String(env, key);
    std::string value_ = ToolKit::JString2String(env, value);
    if (key_ == "device_id") {
        if (value_ == CommMap::GetInstance()->comm_params["android_id"].asString()) {
            CommMap::GetInstance()->comm_params["id_type"] = 1;
        } else {
            CommMap::GetInstance()->comm_params["id_type"] = 0;
        }
    }
    CommMap::GetInstance()->comm_params[key_] = value_;
    Logger::D(env, ToolKit::ToJsonString(CommMap::GetInstance()->comm_params));
}

static jstring GetParam(JNIEnv *env, jobject thiz, jstring key) {
    std::string key_ = ToolKit::JString2String(env, key);
    return env->NewStringUTF(CommMap::GetInstance()->comm_params[key_].asString().c_str());
}

static jstring InvokeJob(JNIEnv *env, jobject thiz, jobject context, jstring key, jstring data) {
    const char *key_ = env->GetStringUTFChars(key, JNI_FALSE);
    const char *data_ = env->GetStringUTFChars(data, JNI_FALSE);
//    Json::Value root = ToolKit::ToJsonObject(data_);
//    if (root.)
//    LOGD("aaaaa , %s", data_);
//    LOGD("json str %s", root.asCString());
//    std::string raw = ToolKit::ToJsonString(root);
//    LOGD("aes key : %s", raw);
    char *enc = AesUtils::Encrypt(data_, reinterpret_cast<const uint8_t *>(key_));
    jstring result = ToolKit::GetJString(env, enc);
    env->ReleaseStringUTFChars(key, key_);
    env->ReleaseStringUTFChars(data, data_);
//    delete (raw);
    free(enc);
    return result;

//    std::string enc = RequestKit::EncryptRequest2(env, context, data);
//    env->ReleaseStringUTFChars(raw, raw_);
//    jstring result = ToolKit::GetJString(env, enc.c_str());
//    free(src);
//    std::string raw = ToolKit::ToJsonString(HandleRequest(env, params));
//    //aes key
//    char *raw_key = AesUtils::GetRawKey();
//    std::string raw_key_md5 = md5(raw_key);
//    char *iv_32 = AesUtils::GetIv32(raw_key_md5.c_str());
//    char *key_64 = static_cast<char *>(malloc(65));
//    sprintf(key_64, "%s%s", raw_key_md5.c_str(), iv_32);
//    key_64[65] = '\0';
//    std::string key = md5(key_64);
//    std::string aes_key = key.substr(8, 16);
//    std::string p = AesUtils::Encrypt(raw.c_str(), reinterpret_cast<const uint8_t *>(aes_key.c_str()));
//    Json::Value root;
//    root["p"] = p;
//    root["ts"] = raw_key_md5;
//    free(raw_key);
//    free(iv_32);
//    free(key_64);
//    delete aes_key;
//    free((void *) aes_key);
}

static jstring ParseJob(JNIEnv *env, jobject thiz, jobject context, jstring key, jstring data) {
    const char *key_ = env->GetStringUTFChars(key, JNI_FALSE);
    const char *data_ = env->GetStringUTFChars(data, JNI_FALSE);

//    Json::Value root = ToolKit::ToJsonObject(data_);
//    const char *p = root["p"].asCString();
    char *raw = AesUtils::Decrypt(data_, reinterpret_cast<const uint8_t *>(key_));
    jstring result = ToolKit::GetJString(env, raw);
    env->ReleaseStringUTFChars(key, key_);
    env->ReleaseStringUTFChars(data, data_);
//    delete (p);
    free(raw);
    return result;
//    std::string raw = RequestKit::DecryptResponse2(env, context, data);
//    jstring result = ToolKit::GetJString(env, raw.c_str());
//    return result;
//    time_t ts = time(nullptr);
//    LOGD("%ld", ts);
//    char *key = AesUtils::GetRawKey();
//    return env->NewStringUTF("");
}


static JNINativeMethod gMethod[] = {
        {"initSdkDrive", "(Landroid/content/Context;)V",                                                      (void *) InitSdkDrive},
        {"setParam",     "(Ljava/lang/String;Ljava/lang/String;)V",                                           (void *) SetParam},
        {"getParam",     "(Ljava/lang/String;)Ljava/lang/String;",                                            (void *) GetParam},
        {"invokeJob",    "(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", (void *) InvokeJob},
        {"parseJob",     "(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", (void *) ParseJob},

//        {"getAesKey",  "()V",                                                      (void *) GetAesKey},
//        {"aesEncrypt",   "(Ljava/lang/String;)Ljava/lang/String;",                                            (void *) AesEncrypt},
//        {"aesDecrypt",   "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",        (void *) AesDecrypt},
//        {"rsaEncrypt", "(Ljava/lang/String;)Ljava/lang/String;",                   (void *) RsaEncrypt},
//        {"rsaDecrypt",   "(Ljava/lang/String;)Ljava/lang/String;",                          (void *) RsaDecrypt}
};

extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env = nullptr;
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
        return JNI_ERR;
    }
    jclass clz = env->FindClass("cn/yyxx/eyuangame/core/impl/SdkDrive");
    if (env->RegisterNatives(clz, gMethod, sizeof(gMethod) / sizeof(gMethod[0])) < 0) {
        return JNI_ERR;
    }
    return JNI_VERSION_1_6;
}