/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * 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.
 */

//
// Created on 2022/12/20.
//
// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found,

#include "EGLRender.h"

#include <js_native_api.h>
#include <mutex>
#include <napi/native_api.h>
#include <stdlib.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES3/gl3.h>
#include <stdint.h>
#include <js_native_api_types.h>
#include "native_common.h"
#include "DebugLog.h"
#include "constant/constant_shape.h"
#include "GLUtils.h"
#include "../util/NapiUtil.h"


const int PARAM_TYPE_SHADER_INDEX = 300;

const int VERTEX_POS_LOC = 0;
const int TEXTURE_POS_LOC = 1;

const int32_t STR_DEFAULT_SIZE = 1024;
EGLRender *EGLRender::sInstance = nullptr;

// 顶点坐标
const static GLfloat vVertices[] = {
    -1.0f, -1.0f, 0.0f, // bottom left
    1.0f, -1.0f, 0.0f, // bottom right
    -1.0f, 1.0f, 0.0f, // top left
    1.0f, 1.0f, 0.0f, // top right
};

// 正常纹理坐标
const static GLfloat vTexCoors[] = {
    0.0f, 1.0f, // bottom left
    1.0f, 1.0f, // bottom right
    0.0f, 0.0f, // top left
    1.0f, 0.0f, // top right
};

// fbo 纹理坐标与正常纹理方向不同(上下镜像)
const static GLfloat vFboTexCoors[] = {
    0.0f, 0.0f, // bottom left
    1.0f, 0.0f, // bottom right
    0.0f, 1.0f, // top left
    1.0f, 1.0f, // top right
};

const static GLushort indices[] = { 0, 1, 2, 1, 3, 2 };
std::mutex mtx;

EGLRender* EGLRender::GetInstance()
{
    mtx.lock();
    if (sInstance == nullptr) {
        sInstance = new EGLRender();
    }
    mtx.unlock();
    return sInstance;
}

napi_value EGLRender::RenderInit(napi_env env, napi_callback_info info)
{
    napi_value exports;
    NAPI_CALL(env, napi_create_object(env, &exports));
    napi_property_descriptor desc[] = {};
    NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
    EGLRender::GetInstance() ->Init();
    return exports;
}


napi_value EGLRender::RenderSetData(napi_env env, napi_callback_info info)
{
    size_t argc = 3;
    napi_value args[3] = { nullptr };
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    void* buffer;
    size_t bufferLength;
    napi_status buffStatus = napi_get_arraybuffer_info(env, args[0], &buffer, &bufferLength);
    if (buffStatus != napi_ok) {
        return nullptr;
    }
    
    uint8_t* uint8_buf = reinterpret_cast<uint8_t *>(buffer);
    uint32_t width;
    napi_status wStatus = napi_get_value_uint32(env, args[1], &width);
    if (wStatus != napi_ok) {
        return nullptr;
    }
    uint32_t height;
    napi_status hStatus = napi_get_value_uint32(env, args[2], &height);
    if (hStatus != napi_ok) {
        return nullptr;
    }
    
    EGLRender::GetInstance() -> SetImageData(uint8_buf, width, height);
    return nullptr;
}

napi_value EGLRender::RenderSetIntParams(napi_env env, napi_callback_info info)
{
    LOGI("gl--> RenderSetIntParams start");
    size_t argc = 2;
    napi_value args[2] = { nullptr };
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    
    uint32_t type;
    napi_status tStatus = napi_get_value_uint32(env, args[0], &type);
    if (tStatus != napi_ok) {
        return nullptr;
    }
    uint32_t param;
    napi_status pStatus = napi_get_value_uint32(env, args[1], &param);
    if (pStatus != napi_ok) {
        return nullptr;
    }
    EGLRender::GetInstance() -> SetIntParams(type, param);
    return nullptr;
}

napi_value EGLRender::GetPixelMapOfSurface(napi_env env, napi_callback_info info)
{
    size_t argc = 4;
    napi_value args[4] = { nullptr };
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    
    uint32_t x;
    napi_status xStatus = napi_get_value_uint32(env, args[0], &x);
    if (xStatus != napi_ok) {
        return nullptr;
    }
    
    uint32_t y;
    napi_status yStatus = napi_get_value_uint32(env, args[1], &y);
    if (yStatus != napi_ok) {
        return nullptr;
    }

    uint32_t surfaceWidth;
    napi_status swStatus = napi_get_value_uint32(env, args[2], &surfaceWidth);
    if (swStatus != napi_ok) {
        return nullptr;
    }
    
    uint32_t surfaceHeight;
    napi_status shStatus = napi_get_value_uint32(env, args[3], &surfaceHeight);
    if (shStatus != napi_ok) {
        return nullptr;
    }
    
    uint8_t* pixels = (uint8_t*) malloc(surfaceWidth * surfaceHeight * DEFAULT_FOUR);
    
    glPixelStorei(GL_PACK_ALIGNMENT, 1);
    glReadPixels(x, y, surfaceWidth, surfaceHeight, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
    
    NativeImageUtil::Flip(&pixels, surfaceWidth, surfaceHeight);
    napi_value array;
    int byte_length = surfaceWidth * surfaceHeight * DEFAULT_FOUR;
    if (!NativeImageUtil::CreateArrayBuffer(env, pixels, byte_length, &array)) {
        LOGI("gl--> GetPixelMapOfSurface error");
    }
    free(pixels);
    return array;
}

napi_value EGLRender::EGLIsInit(napi_env env, napi_callback_info info)
{
    napi_value isInit;
    int32_t value;
    if (EGLRender::GetInstance() -> m_IsGLContextReady) {
        value = 1;
    } else {
        value = 0;
    }
    napi_status status = napi_create_int32(env, value, &isInit);
    if (status != napi_ok) {
        return nullptr;
    }
    return isInit;
}

napi_value EGLRender::DestroyGlesEnv(napi_env env, napi_callback_info info)
{
    EGLRender::GetInstance() -> UnInit();
    return nullptr;
}

napi_value EGLRender::StartUseProgram(napi_env env, napi_callback_info info)
{
    EGLRender::GetInstance() -> UseProgram();
    return nullptr;
}

napi_value EGLRender::Rendering(napi_env env, napi_callback_info info)
{
    // 渲染
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (const void *)0);
    glBindVertexArray(GL_NONE);
    glBindTexture(GL_TEXTURE_2D, GL_NONE);
    return nullptr;
}

napi_value EGLRender::setTypeArrayOfFloat(napi_env env, napi_callback_info info)
{
    size_t argc = 3;
    napi_value args[3] = { nullptr };
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    
    std::string locationContent;
    NapiUtil::JsValueToString(env, args[0], STR_DEFAULT_SIZE, locationContent);
    
    char* location = (char*)locationContent.c_str();
    
    std::string content;
    NapiUtil::JsValueToString(env, args[1], STR_DEFAULT_SIZE, content);
    
    char* key = (char*)content.c_str();
    
    napi_typedarray_type dataType = napi_float32_array;
    void* buffer;
    size_t bufferLength;
    size_t byte_offset;
    napi_status buffStatus = napi_get_typedarray_info(env,
        args[2], &dataType, &bufferLength, &buffer, &args[2], &byte_offset);
    if (buffStatus != napi_ok) {
        return nullptr;
    }
    
    float* value = reinterpret_cast<float *>(buffer);
    
    int uniformType;
    
    if (strcmp(key, "glUniform2fv") == 0) {
        uniformType = UNIFORM_TYPE_2FV;
    } else if (strcmp(key, "glUniform3fv") == 0) {
        uniformType = UNIFORM_TYPE_3FV;
    } else if (strcmp(key, "glUniform4fv") == 0) {
        uniformType = UNIFORM_TYPE_4FV;
    } else if (strcmp(key, "glUniform1fv") == 0) {
        uniformType = UNIFORM_TYPE_FV;
    } else if (strcmp(key, "glUniform2f") == 0) {
        uniformType = UNIFORM_TYPE_2F;
    }
    
    EGLRender::GetInstance() -> GlUniformArray(location, value, uniformType);
    return nullptr;
}

napi_value EGLRender::setTypeArrayOfMatrix3f(napi_env env, napi_callback_info info)
{
    size_t argc = 2;
    napi_value args[2] = { nullptr };
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    
    std::string locationContent;
    NapiUtil::JsValueToString(env, args[0], STR_DEFAULT_SIZE, locationContent);
    
    char* location = (char*)locationContent.c_str();
    
    napi_typedarray_type dataType = napi_float32_array;
    void* buffer;
    size_t bufferLength;
    size_t byte_offset;
    napi_status buffStatus = napi_get_typedarray_info(env,
        args[1], &dataType, &bufferLength, &buffer, &args[1], &byte_offset);
    if (buffStatus != napi_ok) {
        return nullptr;
    }
    
    float* value = reinterpret_cast<float *>(buffer);
    
    EGLRender::GetInstance() -> GlUniformMatrix(location, value, UNIFORM_TYPE_THREE);
    
    return nullptr;
}

napi_value EGLRender::setTypeArrayOfMatrix4f(napi_env env, napi_callback_info info)
{
    size_t argc = 2;
    napi_value args[2] = { nullptr };
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    
    std::string locationContent;
    NapiUtil::JsValueToString(env, args[0], STR_DEFAULT_SIZE, locationContent);
    
    char* location = (char*)locationContent.c_str();
    
    napi_typedarray_type dataType = napi_float32_array;
    void* buffer;
    size_t bufferLength;
    size_t byte_offset;
    napi_status buffStatus = napi_get_typedarray_info(env,
        args[1], &dataType, &bufferLength, &buffer, &args[1], &byte_offset);
    if (buffStatus != napi_ok) {
        return nullptr;
    }
    
    float* value = reinterpret_cast<float *>(buffer);
    
    EGLRender::GetInstance() -> GlUniformMatrix(location, value, UNIFORM_TYPE_FOUR);
    return nullptr;
}

napi_value EGLRender::RenderGlUniform1i(napi_env env, napi_callback_info info)
{
    // int
    size_t argc = 2;
    napi_value args[2] = { nullptr };
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    
    std::string content;
    NapiUtil::JsValueToString(env, args[0], STR_DEFAULT_SIZE, content);
    uint32_t value;
    napi_status status = napi_get_value_uint32(env, args[1], &value);
    if (status != napi_ok) {
        return nullptr;
    }
    EGLRender::GetInstance() -> GlUniform((char*)content.c_str(), value, UNIFORM_TYPE_ZERO);
    return nullptr;
}

napi_value EGLRender::RenderGlUniform1f(napi_env env, napi_callback_info info)
{
    // float
    size_t argc = 2;
    napi_value args[2] = { nullptr };
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    
    std::string content;
    NapiUtil::JsValueToString(env, args[0], STR_DEFAULT_SIZE, content);
    double value;
    napi_status status = napi_get_value_double(env, args[1], &value);
    if (status != napi_ok) {
        return nullptr;
    }
    EGLRender::GetInstance() -> GlUniform((char*)content.c_str(), value, UNIFORM_TYPE_ONE);
    return nullptr;
}

napi_value EGLRender::RenderGlUniform2fv(napi_env env, napi_callback_info info)
{
    // float 数组
    size_t argc = 3;
    napi_value args[3] = { nullptr };
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    
    std::string content;
    NapiUtil::JsValueToString(env, args[0], STR_DEFAULT_SIZE, content);
    
    double value;
    napi_status status = napi_get_value_double(env, args[1], &value);
    if (status != napi_ok) {
        return nullptr;
    }
    double value2;
    napi_status status2 = napi_get_value_double(env, args[2], &value2);
    if (status2 != napi_ok) {
        return nullptr;
    }
    
    float vce2[2];
    vce2[0] = value;
    vce2[1] = value2;
    EGLRender::GetInstance() -> GlUniformArray((char*)content.c_str(), vce2, UNIFORM_TYPE_2FV_SIZE);
    return nullptr;
}

EGLRender::EGLRender()
{
    m_ImageTextureId = GL_NONE;
    m_FboTextureId = GL_NONE;
    m_SamplerLoc = GL_NONE;
    m_TexSizeLoc = GL_NONE;
    
    m_FboId = GL_NONE;
    m_ProgramObj = GL_NONE;
    m_VertexShader = GL_NONE;
    m_FragmentShader = GL_NONE;
    m_eglDisplay = nullptr;
    m_IsGLContextReady = false;
    m_ShaderIndex = 0;
}
EGLRender::~EGLRender()
{
}
void EGLRender::TexturesInit()
{
    glGenTextures(1, &m_ImageTextureId);    // 生成纹理名称
    glBindTexture(GL_TEXTURE_2D, m_ImageTextureId);  // 允许建立一个绑定到目标纹理的有名称的纹理
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glBindTexture(GL_TEXTURE_2D, GL_NONE);
    glGenTextures(1, &m_FboTextureId);
    glBindTexture(GL_TEXTURE_2D, m_FboTextureId);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glBindTexture(GL_TEXTURE_2D, GL_NONE);
}
void EGLRender::Init()
{
    if (CreateGlEnv() == 0) {
        m_IsGLContextReady = true;
    }
    if (!m_IsGLContextReady) {
        return;
    }
    EGLRender::GetInstance() -> TexturesInit();
    m_ProgramObj = GLUtils::CreateProgram(vShaderStr, fShaderStr0, m_VertexShader,
                                          m_FragmentShader);
    if (!m_ProgramObj) {
        GLUtils::CheckGLError("Create Program");
        return;
    }
    glGenBuffers(DEFAULT_THREE, m_VboIds);
    glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[DEFAULT_ZERO]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vVertices), vVertices, GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[DEFAULT_ONE]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vFboTexCoors), vTexCoors, GL_STATIC_DRAW);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_VboIds[DEFAULT_TWO]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
    glGenVertexArrays(DEFAULT_ONE, m_VaoIds);
    glBindVertexArray(m_VaoIds[DEFAULT_ZERO]);
    glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[DEFAULT_ZERO]);
    glEnableVertexAttribArray(VERTEX_POS_LOC);
    glVertexAttribPointer(VERTEX_POS_LOC,
                          DEFAULT_THREE,
                          GL_FLOAT, GL_FALSE,
                          DEFAULT_THREE * sizeof(GLfloat),
                          (const void *)DEFAULT_ZERO);
    glBindBuffer(GL_ARRAY_BUFFER, GL_NONE);
    glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[DEFAULT_ONE]);
    glEnableVertexAttribArray(TEXTURE_POS_LOC);
    glVertexAttribPointer(TEXTURE_POS_LOC,
                          DEFAULT_TWO,
                          GL_FLOAT,
                          GL_FALSE,
                          DEFAULT_TWO * sizeof(GLfloat),
                          (const void *)DEFAULT_ZERO);
    glBindBuffer(GL_ARRAY_BUFFER, GL_NONE);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_VboIds[DEFAULT_TWO]);
    glBindVertexArray(GL_NONE);
}

int EGLRender::CreateGlEnv()
{
    const EGLint confAttr[] = {
        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR, EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
        EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8,
        EGL_DEPTH_SIZE, 16, EGL_STENCIL_SIZE, 8, EGL_NONE
    };
    const EGLint ctxAttr[] = {
        EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE
    };
    const EGLint surfaceAttr[] = {
        EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE
    };
    EGLint eglMajVers, eglMinVers;
    EGLint numConfigs;
    int resultCode = 0;
    do {
        m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
        if (m_eglDisplay == EGL_NO_DISPLAY) {
            resultCode = -1;
            break;
        }
        // 初始化 egl 方法
        if (!eglInitialize(m_eglDisplay, &eglMajVers, &eglMinVers)) {
            resultCode = -1;
            break;
        }
        // 获取 EGLConfig 对象,确定渲染表面的配置信息
        if (!eglChooseConfig(m_eglDisplay, confAttr, &m_eglConf, 1, &numConfigs)) {
            resultCode = -1;
            break;
        }
        // 创建渲染表面 EGLSurface 使用 eglCreateBufferSurface 创建屏幕外渲染区域
        m_eglSurface = eglCreatePbufferSurface(m_eglDisplay, m_eglConf, surfaceAttr);
        if (m_eglSurface == EGL_NO_SURFACE) {
            LOGI("gl-->::CreateGlesEnv happen default error");
            break;
        }
        // 创建渲染上下文 EGLContext
        m_eglCtx = eglCreateContext(m_eglDisplay, m_eglConf, EGL_NO_CONTEXT, ctxAttr);
        if (m_eglCtx == EGL_NO_CONTEXT) {
            EGLint error = eglGetError();
            if (error == EGL_BAD_CONFIG) {
                resultCode = -1;
                break;
            }
        }
        // 绑定上下文
        if (!eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglCtx)) {
            resultCode = -1;
            break;
        }
    } while (false);
    return resultCode;
}

void EGLRender::SetImageData(uint8_t *pData, int width, int height)
{
    if (pData && m_IsGLContextReady) {
        if (m_RenderImage.ppPlane[0]) {
            NativeImageUtil::FreeNativeImage(&m_RenderImage);
            m_RenderImage.ppPlane[0] = nullptr;
        }
        m_RenderImage.width = width;
        m_RenderImage.height = height;
        m_RenderImage.format = IMAGE_FORMAT_RGBA;
        NativeImageUtil::AllocNativeImage(&m_RenderImage);
        if (memcpy_s(m_RenderImage.ppPlane[0],
            width * height * DEFAULT_FOUR, pData, width * height * DEFAULT_FOUR) != EOK) {
            return;
        }
        
        glBindTexture(GL_TEXTURE_2D, m_ImageTextureId);
        glTexImage2D(GL_TEXTURE_2D,
                     0,
                     GL_RGBA,
                     m_RenderImage.width,
                     m_RenderImage.height,
                     0,
                     GL_RGBA,
                     GL_UNSIGNED_BYTE,
                     m_RenderImage.ppPlane[0]);
        glBindTexture(GL_TEXTURE_2D, GL_NONE);
        
        if (m_FboId == GL_NONE) {
            // Create FBO
            glGenFramebuffers(1, &m_FboId);
            glBindFramebuffer(GL_FRAMEBUFFER, m_FboId);
            glBindTexture(GL_TEXTURE_2D, m_FboTextureId);
            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_FboTextureId, 0);
            glTexImage2D(GL_TEXTURE_2D,
                         0,
                         GL_RGBA,
                         m_RenderImage.width,
                         m_RenderImage.height,
                         0,
                         GL_RGBA,
                         GL_UNSIGNED_BYTE,
                         nullptr);
            if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
                LOGI("gl--> EGLRender ::SetImageData glCheckFramebufferStatus status != GL_FRAMEBUFFER_COMPLETE");
            }
            glBindTexture(GL_TEXTURE_2D, GL_NONE);
            glBindFramebuffer(GL_FRAMEBUFFER, GL_NONE);
        }
        LOGI("gl--> :: SetImageData end");
    }
}

void EGLRender::SetIntParams(int paramType, int param)
{
    LOGI("gl--> EGLRender::SetIntParams paramType = %{public}d,param = %{public}d", paramType, param);
    switch (paramType) {
        case PARAM_TYPE_SHADER_INDEX: {
            if (param >= 0) {
                if (m_ProgramObj) {
                    glDeleteProgram(m_ProgramObj);
                    m_ProgramObj = GL_NONE;
                }
                const char* vShader[1];
                vShader[0] = vShaderStr;
                const char* fShader[1];
                switch (param) {
                    case SHADER_TYPE_KUWAHARA: {
                        fShader[0] = fShaderStr3;
                        break;
                    }
                    case SHADER_TYPE_SWIRL: {
                        fShader[0] = swirlFShaderStr;
                        break;
                    }
                    case SHADER_TYPE_BRIGHT: {
                        fShader[0] = brightFShaderStr;
                        break;
                    }
                    case SHADER_TYPE_CONTRAST: {
                        fShader[0] = contrastFShaderStr;
                        break;
                    }
                    case SHADER_TYPE_INVERT: {
                        fShader[0] = invertFShaderStr;
                        break;
                    }
                    case SHADER_TYPE_PIXELATION: {
                        fShader[0] = pixelFShaderStr;
                        break;
                    }
                    case SHADER_TYPE_SEPIA: {
                        fShader[0] = colorMatrixFShaderStr;
                        break;
                    }
                    case SHADER_TYPE_SKETCH: {
                        fShader[0] = sketchShaderStr;
                        vShader[0] = v3x3ShaderStr;
                        break;
                    }
                    case SHADER_TYPE_TOON: {
                        fShader[0] = toonFShaderStr;
                        vShader[0] = v3x3ShaderStr;
                        break;
                    }
                    case SHADER_TYPE_VIGNETTE: {
                        fShader[0] = vignetteFShaderStr;
                        break;
                    }
                    case SHADER_TYPE_GRAYSCALE: {
                        fShader[0] = grayScaleShaderStr;
                        break;
                    }
                    case SHADER_TYPE_BLUR: {
                        fShader[0] = blurShaderStr;
                        break;
                    }
                    default:
                        vShader[0] = vShaderStr;
                        break;
                }
                m_ProgramObj = GLUtils::CreateProgram(vShader[0], fShader[0], m_VertexShader,
                                                      m_FragmentShader);
                if (!m_ProgramObj) {
                    GLUtils::CheckGLError("Create Program");
                    LOGI("gl--> EGLRender::SetIntParams Could not create program.");
                    return;
                }
                m_SamplerLoc = glGetUniformLocation(m_ProgramObj, "s_TextureMap");
                m_TexSizeLoc = glGetUniformLocation(m_ProgramObj, "u_texSize");
            }
        }
            break;
        default:
            break;
    }
}

void EGLRender::UseProgram()
{
    if (m_ProgramObj == GL_NONE) {
        return;
    }
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    glViewport(0, 0, m_RenderImage.width, m_RenderImage.height);
    
    // DO FBO off screen rendering
    glUseProgram(m_ProgramObj);
    glBindFramebuffer(GL_FRAMEBUFFER, m_FboId);
    
    glBindVertexArray(m_VaoIds[0]);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, m_ImageTextureId);
    glUniform1i(m_SamplerLoc, 0);
    
    if (m_TexSizeLoc > -1) {
        GLfloat size[2];
        size[0] = m_RenderImage.width;
        size[1] = m_RenderImage.height;
        glUniform2f(m_TexSizeLoc, size[0], size[1]);
    }
}

void EGLRender::GlUniform(char* location, float value, int unType)
{
    GLint ll = glGetUniformLocation(m_ProgramObj, location);
    switch (unType) {
        case UNIFORM_TYPE_ZERO:
            glUniform1i(ll, (int)value);
            break;
        case UNIFORM_TYPE_ONE:
            glUniform1f(ll, value);
            break;
        default:
            break;
    }
}

void EGLRender::GlUniformArray(char* location, float* value, int unType)
{
    GLint ll = glGetUniformLocation(m_ProgramObj, location);
    switch (unType) {
        case UNIFORM_TYPE_2FV_SIZE:
            GLfloat vec2[2];
            vec2[0] = value[0] * m_RenderImage.width;
            vec2[1] = value[1] * m_RenderImage.height;
            glUniform2fv(ll, 1, vec2);
            break;
        case UNIFORM_TYPE_2F:
            glUniform2f(ll, value[0], value[1]);
            break;
        case UNIFORM_TYPE_FV:
            glUniform1fv(ll, 1, value);
            break;
        case UNIFORM_TYPE_2FV:
            glUniform2fv(ll, 1, value);
            break;
        case UNIFORM_TYPE_3FV:
            glUniform3fv(ll, 1, value);
            break;
        case UNIFORM_TYPE_4FV:
            glUniform4fv(ll, 1, value);
            break;
        default:
            break;
    }
}

void EGLRender::GlUniformMatrix(char* location, float* value, int unType)
{
    GLint ll = glGetUniformLocation(m_ProgramObj, location);
    switch (unType) {
        case UNIFORM_TYPE_THREE:
            glUniformMatrix3fv(ll, 1, false, value);
            break;
        case UNIFORM_TYPE_FOUR:
            glUniformMatrix4fv(ll, 1, false, value);
            break;
        default:
            break;
    }
}

void EGLRender::UnInit()
{
    if (m_ProgramObj) {
        glDeleteProgram(m_ProgramObj);
        m_ProgramObj = GL_NONE;
    }
    
    if (m_ImageTextureId) {
        glDeleteTextures(DEFAULT_ONE, &m_ImageTextureId);
        m_ImageTextureId = GL_NONE;
    }
    
    if (m_FboTextureId) {
        glDeleteTextures(DEFAULT_ONE, &m_FboTextureId);
        m_FboTextureId = GL_NONE;
    }
    
    if (m_VboIds[DEFAULT_ZERO]) {
        glDeleteBuffers(DEFAULT_THREE, m_VboIds);
        m_VboIds[DEFAULT_ZERO] = GL_NONE;
        m_VboIds[DEFAULT_ONE] = GL_NONE;
        m_VboIds[DEFAULT_TWO] = GL_NONE;
    }
    
    if (m_VaoIds[DEFAULT_ZERO]) {
        glDeleteVertexArrays(DEFAULT_ONE, m_VaoIds);
        m_VaoIds[DEFAULT_ZERO] = GL_NONE;
    }
    
    if (m_FboId) {
        glDeleteFramebuffers(1, &m_FboId);
        m_FboId = GL_NONE;
    }
    if (m_IsGLContextReady) {
        DestroyGl();
        m_IsGLContextReady = false;
    }
}
void EGLRender::DestroyGl()
{
    // 释放 EGL 环境
    if (m_eglDisplay != EGL_NO_DISPLAY) {
        eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
        eglDestroyContext(m_eglDisplay, m_eglCtx);
        eglDestroySurface(m_eglDisplay, m_eglSurface);
        eglReleaseThread();
        eglTerminate(m_eglDisplay);
    }
    
    m_eglDisplay = EGL_NO_DISPLAY;
    m_eglSurface = EGL_NO_SURFACE;
    m_eglCtx = EGL_NO_CONTEXT;
}