#include "il2cpp-config.h"

#if IL2CPP_TARGET_ANDROID

#include <string>

#include "os/Initialize.h"
#include "os/LibraryLoader.h"
#include "utils/Logging.h"

#include <jni.h>
#include <android/log.h>

static void AndroidLogCallback(const char* message)
{
    __android_log_print(ANDROID_LOG_INFO, "IL2CPP", "%s", message);
}

JavaVM *sJavaVM = nullptr;

static const Il2CppNativeChar* AndroidLoadLibrary(const Il2CppNativeChar* libName)
{
    if (sJavaVM == nullptr)
    {
        __android_log_print(ANDROID_LOG_INFO, "IL2CPP", "Java VM not initialized");
        return libName;
    }

    if (libName != nullptr)
    {
        JNIEnv* env = nullptr;
        bool detached = sJavaVM->GetEnv((void**)&env, JNI_VERSION_1_2) == JNI_EDETACHED;
        if (detached)
        {
            sJavaVM->AttachCurrentThread(&env, NULL);
        }
        env->ExceptionClear();

        jclass systemClass = env->FindClass("java/lang/System");
        if (systemClass != nullptr)
        {
            jmethodID loadLibrary = env->GetStaticMethodID(systemClass, "loadLibrary", "(Ljava/lang/String;)V");
            if (loadLibrary != nullptr)
            {
                jstring jstr = env->NewStringUTF(libName);
                env->CallStaticVoidMethod(systemClass, loadLibrary, jstr);
                if (env->ExceptionCheck())
                {
                    env->ExceptionClear();

                    // try without lib prefix
                    if (std::string(libName).find("lib") == 0)
                    {
                        jstr = env->NewStringUTF(libName + 3);
                        env->CallStaticVoidMethod(systemClass, loadLibrary, jstr);
                    }
                }
            }
        }
        if (env->ExceptionCheck())
        {
            env->ExceptionClear();
        }
        if (detached)
        {
            sJavaVM->DetachCurrentThread();
        }
    }

    return libName;
}

#if MONO_NET_BCL
extern "C" JNIEXPORT jint JNICALL
pal_JNI_OnLoad(JavaVM *vm, void *reserved);
#endif

extern "C"
JNIEXPORT jint JNI_OnLoad(JavaVM *jvm, void *reserved)
{
    __android_log_print(ANDROID_LOG_INFO, "IL2CPP", "JNI_OnLoad");
    sJavaVM = jvm;
    il2cpp::os::LibraryLoader::SetFindPluginCallback(AndroidLoadLibrary);
#if MONO_NET_BCL
    jint version = pal_JNI_OnLoad(jvm, reserved);
    if (version != JNI_VERSION_1_6)
    {
        __android_log_print(ANDROID_LOG_FATAL, "IL2CPP", "Unexpected version '%d' returned from pal_JNI_OnLoad.", version);
        abort();
    }
#endif
    return JNI_VERSION_1_6;
}

extern "C"
JNIEXPORT void JNI_OnUnload(JavaVM *jvm, void *reserved)
{
    __android_log_print(ANDROID_LOG_INFO, "IL2CPP", "JNI_OnUnload");
    sJavaVM = nullptr;
}

void il2cpp::os::Initialize()
{
    if (!utils::Logging::IsLogCallbackSet())
        utils::Logging::SetLogCallback(AndroidLogCallback);
}

void il2cpp::os::Uninitialize()
{
}

#endif
