//
// Created by #Suyghur, on 2021/10/15.
//

#include <random>
#include "include/comm_map.h"
#include "external/md5/md5.h"
#include "include/yylog.h"
#include "include/tool_kit.h"

static const unsigned char CHAR_SET[62] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
                                           'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
                                           'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
                                           'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D',
                                           'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
                                           'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
                                           'Y', 'Z'};

#define RSA_PUBLIC_KEY "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC3eXf1JxdFtx6c8AJTdlSverL8WqRE11yFB6Q+GbQeEVXjSCgQN48qePat7mXbH4LAtjaSEqXHruP4hJO8777wYtEKNKIN2VZgWQElrllAuAtaHyA+UGKwulOKmkR8k1Oxmfd46fnQBwzy+Giab4lqQRQAObCT0QtUrlrsU1U+zwIDAQAB"


jbyteArray ToolKit::GetJbyteArray(JNIEnv *Env, const char *src) {
    return nullptr;
}

jstring ToolKit::GetJString(JNIEnv *env, const char *src) {
    jclass clz = env->FindClass("java/lang/String");
    jmethodID methodID = env->GetMethodID(clz, "<init>", "([BLjava/lang/String;)V");
    jstring encoding = env->NewStringUTF("utf-8");

    auto length = static_cast<jsize>(strlen(src));
    jbyteArray bytes = env->NewByteArray(length);
    env->SetByteArrayRegion(bytes, 0, length, (jbyte *) src);
    auto result = (jstring) env->NewObject(clz, methodID, bytes, encoding);

    env->DeleteLocalRef(clz);
    env->DeleteLocalRef(encoding);
    env->DeleteLocalRef(bytes);

    return result;
}

std::string ToolKit::JString2String(JNIEnv *env, jstring src) {
    if (env == nullptr || src == nullptr) {
        LOGE("jstring to string has some error");
        return "";
    }
    char *tmp_ptr = nullptr;
    jclass clz = env->FindClass("java/lang/String");
    jstring encode_mode = env->NewStringUTF("UTF-8");
    jmethodID mid = env->GetMethodID(clz, "getBytes", "(Ljava/lang/String;)[B");
    auto byte_array = (jbyteArray) (env->CallObjectMethod(src, mid, encode_mode));
    jsize len = env->GetArrayLength(byte_array);
    if (len == 0) {
        free(tmp_ptr);
        return "";
    }
    jbyte *byte = env->GetByteArrayElements(byte_array, JNI_FALSE);
    if (len > 0) {
        tmp_ptr = (char *) malloc(len + 1);
        memcpy(tmp_ptr, byte, len);
        tmp_ptr[len] = 0;
    }
    env->ReleaseByteArrayElements(byte_array, byte, JNI_FALSE);
    std::string result(tmp_ptr);
    free(tmp_ptr);
    env->DeleteLocalRef(clz);
    env->DeleteLocalRef(encode_mode);
    env->DeleteLocalRef(byte_array);
    return result;
}

std::string ToolKit::GetGameCode(JNIEnv *env, jobject context) {
    jclass clz = env->FindClass("com/yyrh/utils/ManifestUtils");
    if (clz == nullptr) {
        LOGE("ManifestUtils clz is nullptr !!!");
        return "";
    }
    const char *method_name = "getMetaDataValue";
    const char *sig = "(Landroid/content/Context;Ljava/lang/String;)Ljava/lang/String;";
    jmethodID mid = env->GetStaticMethodID(clz, method_name, sig);
    jstring key = env->NewStringUTF("yyrh_game_code");
    auto result = (jstring) env->CallStaticObjectMethod(clz, mid, context, key);
    env->DeleteLocalRef(clz);
    env->DeleteLocalRef(key);
    return JString2String(env, result);
}

std::string ToolKit::GetSpCode(JNIEnv *env, jobject context) {
    jclass clz = env->FindClass("com/yyrh/utils/ManifestUtils");
    if (clz == nullptr) {
        LOGE("ManifestUtils clz is nullptr !!!");
        return "";
    }
    const char *method_name = "getMetaDataValue";
    const char *sig = "(Landroid/content/Context;Ljava/lang/String;)Ljava/lang/String;";
    jmethodID mid = env->GetStaticMethodID(clz, method_name, sig);
    jstring key = env->NewStringUTF("yyrh_sp_code");
    auto result = (jstring) env->CallStaticObjectMethod(clz, mid, context, key);
    env->DeleteLocalRef(clz);
    env->DeleteLocalRef(key);
    return JString2String(env, result);
}

std::string ToolKit::GetClientVersion(JNIEnv *env, jobject context) {
    jclass clz = env->FindClass("com/yyrh/Version");
    if (clz == nullptr) {
        LOGE("Version clz is nullptr !!!");
        return "";
    }
    const char *field_name = "VERSION_NAME";
    const char *sig = "Ljava/lang/String;";
    jfieldID fid = env->GetStaticFieldID(clz, field_name, sig);
    auto result = (jstring) env->GetStaticObjectField(clz, fid);
    env->DeleteLocalRef(clz);
    return JString2String(env, result);
}

std::string ToolKit::GetPackageName(JNIEnv *env, jobject context) {
    jclass clz = env->GetObjectClass(context);
    if (clz == nullptr) {
        LOGE("clz is nullptr !!!");
        return "";
    }
    const char *method_name = "getPackageName";
    const char *sig = "()Ljava/lang/String;";
    jmethodID mid = env->GetMethodID(clz, method_name, sig);
    auto result = (jstring) env->CallObjectMethod(context, mid);
    env->DeleteLocalRef(clz);
    return JString2String(env, result);
}

std::string ToolKit::GetDeviceSoftwareVersion() {
    char *version = (char *) malloc(128 * sizeof(char));
    __system_property_get("ro.build.version.release", version);
    std::string result = version;
    free(version);
    return result;
}

std::string ToolKit::GetDeviceModel() {
    char *model = (char *) malloc(128 * sizeof(char));
    __system_property_get("ro.product.model", model);
    std::string result = model;
    free(model);
    return result;
}

std::string ToolKit::GetAndroidDeviceId(JNIEnv *env, jobject context) {
    jclass clz = env->FindClass("cn/yyxx/support/device/DeviceInfoUtils");
    if (clz == nullptr) {
        LOGE("DeviceInfoUtils is nullptr !!!");
        return "";
    }
    const char *method_name = "getAndroidDeviceId";
    const char *sig = "(Landroid/content/Context;)Ljava/lang/String;";
    jmethodID mid = env->GetStaticMethodID(clz, method_name, sig);
    auto result = (jstring) env->CallStaticObjectMethod(clz, mid, context);
    env->DeleteLocalRef(clz);
    return JString2String(env, result);
}


jstring ToolKit::RsaEncrypt(JNIEnv *env, const char *data) {
    jclass clz = env->FindClass("cn/yyxx/support/encryption/rsa/RsaUtils");
    if (clz == nullptr) {
        LOGE("RsaUtils is nullptr !!!");
        return nullptr;
    }
    const char *method_name = "encryptByPublicKey";
    const char *sig = "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;";
    jmethodID mid = env->GetStaticMethodID(clz, method_name, sig);
    jstring raw = env->NewStringUTF(data);
    jstring key = env->NewStringUTF(RSA_PUBLIC_KEY);
    auto result = (jstring) env->CallStaticObjectMethod(clz, mid, raw, key);
    env->DeleteLocalRef(clz);
    env->DeleteLocalRef(raw);
    env->DeleteLocalRef(key);
    return result;
}

std::string ToolKit::ToJsonString(const Json::Value &root) {
    Json::StreamWriterBuilder builder;
    std::ostringstream oss;
    //无格式输出
    builder.settings_["indentation"] = "";
    std::unique_ptr<Json::StreamWriter> json_writer(builder.newStreamWriter());
    json_writer->write(root, &oss);
    return oss.str();
}

Json::Value ToolKit::ToJsonObject(const std::string &json) {
    Json::CharReaderBuilder builder;
    Json::CharReader *reader_ptr(builder.newCharReader());
    JSONCPP_STRING errs;
    Json::Value root;
    reader_ptr->parse(json.c_str(), json.c_str() + json.length(), &root, &errs);
    return root;
}


std::string ToolKit::GetJsonSortSignature(const Json::Value &root) {
    Json::Value::Members members = root.getMemberNames();
    std::string signature;
    for (auto &member : members) {
        signature.append(member).append("=").append(root[member].asString()).append("&");
    }
    signature.erase(signature.length() - 1);
    return signature;
}

std::string ToolKit::GenerateRandString(int len) {
    srandom(time(nullptr));
    std::string random_str;
    for (int i = 0; i < len; ++i) {

        random_str += CHAR_SET[random() % 62];
    }
    return random_str;
}






