#include "RNGLContext.h"
#include <stdexcept>
#include <iostream>

struct CompileResult
{
    bool success;
    std::string error;
    std::unordered_map<std::string, std::string> uniforms;
};

const std::string RNGLContext::STATIC_VERT =
    "attribute vec2 position;\n"
    "varying vec2 uv;\n"
    "void main() {\n"
    "    gl_Position = vec4(position,0.0,1.0);\n"
    "    uv = vec2(0.5, 0.5) * (position+vec2(1.0, 1.0));\n"
    "}\n";

RNGLContext::RNGLContext() {}

RNGLContext::~RNGLContext() {}

napi_value RNGLContext::AddShader(napi_env env, napi_callback_info info)
{
    size_t argc = 3;
    napi_value args[3];
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    int32_t id;
    napi_get_value_int32(env, args[0], &id);

    napi_value config = args[1];
    napi_value callback = args[2];

    napi_value name_value;
    napi_value frag_value;
    napi_get_named_property(env, config, "name", &name_value);
    napi_get_named_property(env, config, "frag", &frag_value);
    
    size_t name_size, frag_size;
    char name[256], frag[256];
    napi_get_value_string_utf8(env, name_value, name, sizeof(name), &name_size);
    napi_get_value_string_utf8(env, frag_value, frag, sizeof(frag), &frag_size);
    
    RNGLContext *self = nullptr;
    napi_unwrap(env, args[0], (void **)&self);
    {
        std::lock_guard<std::mutex> lock(self->mtx);
        self->addShader(id, name, frag);
    }
    

//    napi_value resource_name;
//    napi_create_string_utf8(env, "ShaderCallback", NAPI_AUTO_LENGTH, &resource_name);
//    napi_threadsafe_function tsfn;
//    napi_create_threadsafe_function(env, callback, nullptr, resource_name, 0, 1, nullptr,
//                                    nullptr, nullptr, OH_ShaderCallback, &tsfn);

    return nullptr;
}

napi_value RNGLContext::RemoveShader(napi_env env, napi_callback_info info)
{
    // TODO: 实现移除逻辑
    return nullptr;
}

void RNGLContext::OH_ShaderCallback(napi_env env, napi_value jsCallback, void *context, void *data)
{
    auto *result = static_cast<CompileResult *>(data);
    napi_value argv[2];

    if (result->success)
    {
        napi_create_object(env, &argv[1]);
        napi_value uniforms;
        napi_create_object(env, &uniforms);

        for (const auto &[name, type] : result->uniforms)
        {
            napi_value typeStr;
            napi_create_string_utf8(env, type.c_str(), NAPI_AUTO_LENGTH, &typeStr);
            napi_set_named_property(env, uniforms, name.c_str(), typeStr);
        }
        napi_set_named_property(env, argv[1], "uniforms", uniforms);
        argv[0] = nullptr;
    }
    else
    {
        napi_create_string_utf8(env, result->error.c_str(), NAPI_AUTO_LENGTH, &argv[0]);
        argv[1] = nullptr;
    }
    napi_call_function(env, nullptr, jsCallback, 2, argv, nullptr);
    delete result;
}

void RNGLContext::addShader(int id, const std::string& name, const std::string& frag) {
    // 假设这里实现了 GLShaderData 的创建
    auto data = std::make_shared<GLShaderData>(name, STATIC_VERT, frag);
    std::lock_guard<std::mutex> lock(mtx);
    shaders[id] = data;
//    if (onCompile) {
//        onCompileCallbacks[id] = onCompile;
//    }
}

void RNGLContext::removeShader(int id) {
    std::lock_guard<std::mutex> lock(mtx);
    auto it = shaders.find(id);
    if (it == shaders.end()) {
        throw std::runtime_error("removeShader(" + std::to_string(id) + "): shader does not exist");
    }
    shaders.erase(it);
}

void RNGLContext::shaderFailedToCompile(int id, const std::string& error) {
    std::lock_guard<std::mutex> lock(mtx);
    auto it = onCompileCallbacks.find(id);
    if (it == onCompileCallbacks.end()) {
        std::cerr << "RNGLContext: " << error << std::endl;
    } else {
        it->second(error);
        onCompileCallbacks.erase(it);
    }
}

void RNGLContext::shaderSucceedToCompile(int id, const std::unordered_map<std::string, int>& uniformTypes) {
    std::lock_guard<std::mutex> lock(mtx);
    auto it = onCompileCallbacks.find(id);
    if (it != onCompileCallbacks.end()) {
        std::string result;
        for (const auto& pair : uniformTypes) {
            result += pair.first + ": " + glTypeString(pair.second) + ", ";
        }
        if (!result.empty()) {
            result.pop_back();
            result.pop_back();
        }
        it->second(result);
        onCompileCallbacks.erase(it);
    }
}

std::shared_ptr<GLShaderData> RNGLContext::GetShaderData(int id) {
    if (shaders.find(id) != shaders.end()) {
        return shaders[id];
    }
    return nullptr;
}

std::string RNGLContext::glTypeString(int type) {
    switch (type) {
        case GL_FLOAT: return "float";
        case GL_FLOAT_VEC2: return "vec2";
        case GL_FLOAT_VEC3: return "vec3";
        case GL_FLOAT_VEC4: return "vec4";
        case GL_INT: return "int";
        case GL_INT_VEC2: return "ivec2";
        case GL_INT_VEC3: return "ivec3";
        case GL_INT_VEC4: return "ivec4";
        case GL_BOOL: return "bool";
        case GL_BOOL_VEC2: return "bvec2";
        case GL_BOOL_VEC3: return "bvec3";
        case GL_BOOL_VEC4: return "bvec4";
        case GL_FLOAT_MAT2: return "mat2";
        case GL_FLOAT_MAT3: return "mat3";
        case GL_FLOAT_MAT4: return "mat4";
        case GL_SAMPLER_2D: return "sampler2D";
        case GL_SAMPLER_CUBE: return "samplerCube";
        default: return "";
    }
}