//=============================================================================================================================
//
// Copyright (c) 2015-2022 VisionStar Information Technology (Shanghai) Co., Ltd. All Rights Reserved.
// EasyAR is the registered trademark or trademark of VisionStar Information Technology (Shanghai) Co., Ltd in China
// and other countries for the augmented reality technology developed by VisionStar Information Technology (Shanghai) Co., Ltd.
//
//=============================================================================================================================

#import "RecorderRenderer.h"

#include <OpenGLES/ES3/gl.h>

@implementation RecorderRenderer {
    int width;
    int height;

    //stage states
    GLuint fboPost_;
    GLuint texturePost_;
    GLuint depthPost_;
    GLuint stencilPost_;

    //draw back buffer states
    GLuint program_bk;
    GLuint vertex_vbo_bk;
    GLuint uv_vbo_bk;
    GLuint index_vbo_bk;
    GLuint aVertex;
    GLuint aUV;
    GLuint uTexture;
}

+ (instancetype)create
{
    return [[RecorderRenderer alloc] init];
}

- (id)init
{
    width = -1;
    height = -1;

    fboPost_ = (GLuint)(-1);
    texturePost_ = (GLuint)(-1);
    depthPost_ = (GLuint)(-1);
    stencilPost_ = (GLuint)(-1);

    program_bk = (GLuint)(-1);
    vertex_vbo_bk = (GLuint)(-1);
    uv_vbo_bk = (GLuint)(-1);
    index_vbo_bk = (GLuint)(-1);
    aVertex = (GLuint)(-1);
    aUV = (GLuint)(-1);
    uTexture = (GLuint)(-1);

    const char* vert_source =
        "attribute vec4 coord;\n"
        "attribute vec2 texCoord;\n"
        "varying vec2 texc; \n"
        "void main(void)\n"
        "{\n"
        "    gl_Position = coord;\n"
        "    texc = texCoord; \n"
        "}\n";
    const char* frag_source =
        "#ifdef GL_ES\n"
        "precision mediump float;\n"
        "#endif\n"
        "varying vec2 texc;\n"
        "uniform sampler2D texture;\n"
        "void main(void)\n"
        "{\n"
        "    gl_FragColor = texture2D(texture, texc);\n"
        "}\n";

    int vertShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertShader, 1, &vert_source, NULL);
    [self getError:"glShaderSource"];

    glCompileShader(vertShader);
    [self getError:"vertShader"];

    int fragShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragShader, 1, &frag_source, NULL);
    glCompileShader(fragShader);

    [self getError:"fragShader"];

    program_bk = glCreateProgram();
    glAttachShader(program_bk, vertShader);
    glAttachShader(program_bk, fragShader);
    glLinkProgram(program_bk);
    [self getError:"glLinkProgram"];

    glUseProgram(program_bk);
    [self getError:"glUseProgram"];
    glDeleteShader(fragShader);
    glDeleteShader(vertShader);

    aVertex = glGetAttribLocation(program_bk, "coord");
    aUV = glGetAttribLocation(program_bk, "texCoord");
    uTexture = glGetUniformLocation(program_bk, "texture");

    glGenBuffers(1, &vertex_vbo_bk);
    glBindBuffer(GL_ARRAY_BUFFER, vertex_vbo_bk);
    const GLfloat vertexs[4][3] = {{-1.0f, -1.0f, 0.0f},{1.0f, -1.0f, 0.0f,},{-1.0f, 1.0f, 0.0f},{1.0f, 1.0f, 0.0f}};
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertexs), vertexs, GL_STATIC_DRAW);
    [self getError:"vertex_vbo_bk"];

    glGenBuffers(1, &uv_vbo_bk);
    glBindBuffer(GL_ARRAY_BUFFER, uv_vbo_bk);
    const GLfloat uvs[4][2] = {{0.0f, 0.0f},{1.0f, 0.0f},{0.0f, 1.0f},{1.0f, 1.0f}};
    glBufferData(GL_ARRAY_BUFFER, sizeof(uvs), uvs, GL_STATIC_DRAW);
    [self getError:"uv_vbo_bk"];

    glGenBuffers(1, &index_vbo_bk);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_vbo_bk);
    const GLushort indexs[2][3] = {{0, 1, 2},{1, 3, 2}};
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indexs), indexs, GL_STATIC_DRAW);
    [self getError:"index_vbo_bk"];

    return self;
}

- (void)getError:(const char *)name
{
    int error = glGetError();
    if (error != GL_NO_ERROR) {
        NSLog(@"%s %d", name, error);
    }
}

- (void)dealloc
{
    glDeleteProgram(program_bk);

    glDeleteBuffers(1, &vertex_vbo_bk);
    glDeleteBuffers(1, &uv_vbo_bk);
    glDeleteBuffers(1, &index_vbo_bk);

    if (texturePost_ != -1) {
        //delete Post_ GL Resources
        glDeleteTextures(1, &texturePost_);
        texturePost_ = -1;
        glDeleteRenderbuffers(1, &depthPost_);
        depthPost_ = -1;
        glDeleteFramebuffers(1, &fboPost_);
        fboPost_ = -1;
        [self getError:"glDeleteFramebuffers"];
    }
}

- (void)resize:(int)width height:(int)height
{
    if (texturePost_ != -1) {
        //delete Post_ GL Resources
        glDeleteTextures(1, &texturePost_);
        texturePost_ = -1;
        glDeleteRenderbuffers(1, &depthPost_);
        depthPost_ = -1;
        glDeleteFramebuffers(1, &fboPost_);
        fboPost_ = -1;
        [self getError:"glDeleteFramebuffers"];
    }

    //re-create
    glGenFramebuffers(1, &fboPost_);
    glActiveTexture(GL_TEXTURE0);
    glGenTextures(1, &texturePost_);
    glBindTexture(GL_TEXTURE_2D, texturePost_);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    [self getError:"glTexImage2D"];

    glBindFramebuffer(GL_FRAMEBUFFER, fboPost_);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texturePost_, 0);
    [self getError:"GL_COLOR_ATTACHMENT0"];

    glGenRenderbuffers(1, &depthPost_);
    glBindRenderbuffer(GL_RENDERBUFFER, depthPost_);
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthPost_);
    [self getError:"GL_DEPTH_COMPONENT16"];

    self->width = width;
    self->height = height;
}

- (void)preRender
{
    if (width == -1 || height == -1) {
        return;
    }

    glBindFramebuffer(GL_FRAMEBUFFER, fboPost_);
}

- (void)postRender
{
    [self getError:"glBindFramebuffer before"];

    glClearColor(0.f, 0.f, 0.f, 1.f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glDisable(GL_BLEND);
    glDisable(GL_DEPTH_TEST);

    glUseProgram(program_bk);
    glBindBuffer(GL_ARRAY_BUFFER, vertex_vbo_bk);
    glEnableVertexAttribArray(aVertex);
    glVertexAttribPointer(aVertex, 3, GL_FLOAT, GL_FALSE, 0, NULL);
    [self getError:"aVertex"];

    glBindBuffer(GL_ARRAY_BUFFER, uv_vbo_bk);
    glEnableVertexAttribArray(aUV);
    glVertexAttribPointer(aUV, 2, GL_FLOAT, GL_FALSE, 0, NULL);
    [self getError:"uv_vbo_bk"];

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texturePost_);
    [self getError:"texturePost_"];

    glUniform1i(uTexture, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_vbo_bk);
    [self getError:"GL_ELEMENT_ARRAY_BUFFER"];

    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, NULL);
    [self getError:"glDrawElements"];
}

- (int)getTextureId
{
    return (int)(texturePost_);
}

@end
