/* Copyright (c) 2023-2024, Thomas Atkinson
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 the "License";
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "android/context.hpp"
#include "core/util/logging.hpp"
#include <jni.h>

extern "C"
{
	// TODO: Arguments can be parsed from the bundle
	JNIEXPORT void JNICALL
	    Java_com_khronos_vulkan_1samples_SampleLauncherActivity_sendArgumentsToPlatform(JNIEnv *env, jobject thiz, jobjectArray arg_strings)
	{
		std::vector<std::string> args;

		for (int i = 0; i < env->GetArrayLength(arg_strings); i++)
		{
			jstring arg_string = (jstring) (env->GetObjectArrayElement(arg_strings, i));

			const char *arg = env->GetStringUTFChars(arg_string, 0);

			args.push_back(std::string(arg));

			env->ReleaseStringUTFChars(arg_string, arg);
		}

		vkb::AndroidPlatformContext::android_arguments = args;
	}
}

namespace details
{
std::string get_external_storage_directory(android_app *app)
{
	return app->activity->externalDataPath;
}

std::string get_external_cache_directory(android_app *app)
{
	JNIEnv *env;
	app->activity->vm->AttachCurrentThread(&env, NULL);

	jclass    cls         = env->FindClass("android/app/NativeActivity");
	jmethodID getCacheDir = env->GetMethodID(cls, "getCacheDir", "()Ljava/io/File;");
	jobject   cache_dir   = env->CallObjectMethod(app->activity->javaGameActivity, getCacheDir);

	jclass    fcls        = env->FindClass("java/io/File");
	jmethodID getPath     = env->GetMethodID(fcls, "getPath", "()Ljava/lang/String;");
	jstring   path_string = (jstring) env->CallObjectMethod(cache_dir, getPath);

	const char *path_chars = env->GetStringUTFChars(path_string, NULL);
	std::string temp_folder(path_chars);

	env->ReleaseStringUTFChars(path_string, path_chars);
	app->activity->vm->DetachCurrentThread();
	return temp_folder;
}
}        // namespace details

namespace vkb
{
std::vector<std::string> AndroidPlatformContext::android_arguments = {};

AndroidPlatformContext::AndroidPlatformContext(android_app *app) :
    PlatformContext{}, app{app}
{
	_external_storage_directory = details::get_external_storage_directory(app);
	_temp_directory             = details::get_external_cache_directory(app);
	_arguments                  = android_arguments;
}
}        // namespace vkb
extern "C"
JNIEXPORT void JNICALL
Java_com_khronos_vulkan_1samples_MainActivity_sendArgumentsToPlatform(JNIEnv *env, jobject thiz, jobjectArray arg_strings) {
    // TODO: implement sendArgumentsToPlatform()
    std::vector<std::string> args;

    for (int i = 0; i < env->GetArrayLength(arg_strings); i++)
    {
        jstring arg_string = (jstring) (env->GetObjectArrayElement(arg_strings, i));

        const char *arg = env->GetStringUTFChars(arg_string, 0);

        args.push_back(std::string(arg));

        env->ReleaseStringUTFChars(arg_string, arg);
    }

    vkb::AndroidPlatformContext::android_arguments = args;
}
extern "C"
JNIEXPORT void JNICALL
Java_com_khronos_vulkan_1samples_MainActivity_nativeButtonClicked(JNIEnv *env, jobject thiz) {
    // TODO: implement nativeButtonClicked()
	LOGI("call MainActivity_nativeButtonClicked.");
}

extern void TextureLoadProcessWithVulkan(uint8_t* data, int width, int height, int rowStride, size_t dataSize);
extern void ReceiveFacePoint(float* pos, int count, int width, int height);

void processWithVulkan(uint8_t* data, int width, int height, int format,
                       int rowStride, size_t dataSize) {
    // Vulkan 处理逻辑
    // 1. 创建 Vulkan 图像（如果尚未创建）
    // 2. 将数据复制到 Vulkan 图像内存
    // 3. 执行 Vulkan 渲染或计算操作

    //LOGI("ProcessingImage: %dx%d, stride: %d, size: %zu", width, height, rowStride, dataSize);
    TextureLoadProcessWithVulkan(data, width, height, rowStride, dataSize);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_khronos_vulkan_1samples_MainActivity_processImageNative(JNIEnv *env, jobject thiz,
                                                                 jobject buffer, jint width,
                                                                 jint height, jint format,
                                                                 jint row_stride, jint pixel_stride,
                                                                 jint rotation) {
    // TODO: implement processImageNative()
    // 获取直接字节缓冲区指针
    uint8_t* imageData = static_cast<uint8_t*>(env->GetDirectBufferAddress(buffer));
    jlong capacity = env->GetDirectBufferCapacity(buffer);

    if (imageData == nullptr) {
        LOGI("Failed to get direct buffer address");
        return;
    }

    // 在这里将图像数据传递给 Vulkan
    // 创建 Vulkan 图像或更新现有图像
    processWithVulkan(imageData, width, height, format, row_stride, capacity);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_khronos_vulkan_1samples_MainActivity_passDataToNative(JNIEnv *env, jobject thiz,
                                                               jobject buffer, jint pointCount, jint width, jint height) {
    // TODO: implement passDataToNative()

    // 获取直接缓冲区的指针
    float* pos = static_cast<float*>(env->GetDirectBufferAddress(buffer));

    if (pos == nullptr) {
        // 处理错误
        return;
    }

    // 或者直接将指针传递给Vulkan
    ReceiveFacePoint(pos, pointCount, width, height);
}

extern void global_update_mvp(float fov, float x, float y, float z, float sx, float sy, float sz, float rotx, float roty, float rotz, float camx, float camy, float camz);

extern "C"
JNIEXPORT void JNICALL
Java_com_khronos_vulkan_1samples_MainActivity_upDateCameraData(JNIEnv *env, jobject thiz,
                                                               jfloat fov, jfloat x, jfloat y,jfloat z,
                                                               jfloat sx, jfloat sy,jfloat sz,
                                                               jfloat rotx, jfloat roty,jfloat rotz,
                                                               jfloat camx,jfloat camy, jfloat camz) {
    global_update_mvp(fov, x, y, z, sx, sy, sz, rotx, roty, rotz, camx, camy, camz);
}