//=============================================================================================================================
//
// 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 "BoxRenderer.h"

#include <OpenGLES/ES3/gl.h>

const char* box_vert="uniform mat4 trans;\n"
        "uniform mat4 proj;\n"
        "attribute vec4 coord;\n"
        "attribute vec4 color;\n"
        "varying vec4 vcolor;\n"
        "\n"
        "void main(void)\n"
        "{\n"
        "    vcolor = color;\n"
        "    gl_Position = proj*trans*coord;\n"
        "}\n"
        "\n"
;

const char* box_frag="#ifdef GL_ES\n"
        "precision highp float;\n"
        "#endif\n"
        "varying vec4 vcolor;\n"
        "\n"
        "void main(void)\n"
        "{\n"
        "    gl_FragColor = vcolor;\n"
        "}\n"
        "\n"
;

@implementation BoxRenderer {
    unsigned int program_box;
    int pos_coord_box;
    int pos_color_box;
    int pos_trans_box;
    int pos_proj_box;
    unsigned int vbo_coord_box;
    unsigned int vbo_color_box;
    unsigned int vbo_color_box_2;
    unsigned int vbo_faces_box;
}

+ (void)getGLMatrix:(easyar_Matrix44F *)m result:(float (*) [16])a {
    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 4; j++) {
            (*a)[i + j * 4] = [[m.data objectAtIndex:(i * 4 + j)] floatValue];
        }
    }
}

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

- (instancetype)init
{
    program_box = glCreateProgram();
    GLuint vertShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertShader, 1, &box_vert, 0);
    glCompileShader(vertShader);
    GLuint fragShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragShader, 1, &box_frag, 0);
    glCompileShader(fragShader);
    glAttachShader(program_box, vertShader);
    glAttachShader(program_box, fragShader);
    glLinkProgram(program_box);
    glUseProgram(program_box);
    pos_coord_box = glGetAttribLocation(program_box, "coord");
    pos_color_box = glGetAttribLocation(program_box, "color");
    pos_trans_box = glGetUniformLocation(program_box, "trans");
    pos_proj_box = glGetUniformLocation(program_box, "proj");

    glGenBuffers(1, &vbo_coord_box);
    glBindBuffer(GL_ARRAY_BUFFER, vbo_coord_box);
    const GLfloat cube_vertices[8][3] = {
        /* +z */{1.0f / 2, 1.0f / 2, 0.01f / 2}, {1.0f / 2, -1.0f / 2, 0.01f / 2}, {-1.0f / 2, -1.0f / 2, 0.01f / 2}, {-1.0f / 2, 1.0f / 2, 0.01f / 2},
        /* -z */{1.0f / 2, 1.0f / 2, -0.01f / 2}, {1.0f / 2, -1.0f / 2, -0.01f / 2}, {-1.0f / 2, -1.0f / 2, -0.01f / 2}, {-1.0f / 2, 1.0f / 2, -0.01f / 2}};
    glBufferData(GL_ARRAY_BUFFER, sizeof(cube_vertices), cube_vertices, GL_DYNAMIC_DRAW);

    glGenBuffers(1, &vbo_color_box);
    glBindBuffer(GL_ARRAY_BUFFER, vbo_color_box);
    const GLubyte cube_vertex_colors[24][4] = {
        {0, 0, 255, 255},
        {0, 0, 255, 255},
        {0, 0, 255, 255},
        {0, 0, 255, 255},
        {0, 255, 0, 255},
        {0, 255, 0, 255},
        {0, 0, 100, 255},
        {0, 0, 100, 255},
        {0, 255, 0, 255},
        {0, 255, 0, 255},
        {0, 0, 100, 255},
        {0, 0, 100, 255},
        {0, 100, 0, 255},
        {0, 100, 0, 255},
        {0, 100, 0, 255},
        {0, 100, 0, 255},
        {100, 0, 0, 255},
        {100, 0, 0, 255},
        {100, 0, 0, 255},
        {100, 0, 0, 255},
        {255, 0, 0, 255},
        {255, 0, 0, 255},
        {255, 0, 0, 255},
        {255, 0, 0, 255}};
    glBufferData(GL_ARRAY_BUFFER, sizeof(cube_vertex_colors), cube_vertex_colors, GL_STATIC_DRAW);

    glGenBuffers(1, &vbo_faces_box);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo_faces_box);
    const GLushort cube_faces[9][4] = {
        {0, 3, 2, 0},
        {1, 3, 8, 5},
        {4, 8, 9, 5},
        {10, 7, 6, 10},
        {11, 7, 12, 14},
        {13, 12, 15, 14},
        {16, 18, 17, 16},
        {19, 18, 20, 22},
        {21, 20, 23, 22},
    };
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(cube_faces), cube_faces, GL_STATIC_DRAW);

    return self;
}

- (void)dealloc
{
    glDeleteProgram(program_box);

    glDeleteBuffers(1, &vbo_coord_box);
    glDeleteBuffers(1, &vbo_color_box);
    glDeleteBuffers(1, &vbo_color_box_2);
    glDeleteTextures(1, &vbo_faces_box);
}

- (void)render:(easyar_Matrix44F *)projectionMatrix cameraview:(easyar_Matrix44F *)cameraview size:(easyar_Vec3F *)size
{
    float size0 = [[size.data objectAtIndex:0] floatValue];
    float size1 = [[size.data objectAtIndex:1] floatValue];
    float size2 = [[size.data objectAtIndex:2] floatValue];

    glBindBuffer(GL_ARRAY_BUFFER, vbo_coord_box);
    const GLfloat cube_vertices[24][3] = {
        {size0 / 2, 0, -size2 / 2},
        {-size0 / 2, 0, -size2 / 2},
        {size0 / 2, size1, -size2 / 2},
        {-size0 / 2, size1, -size2 / 2},
        {size0 / 2, size1, size2 / 2},
        {-size0 / 2, size1, size2 / 2},
        {size0 / 2, 0, size2 / 2},
        {-size0 / 2, 0, size2 / 2},
        {size0 / 2, size1, -size2 / 2},
        {-size0 / 2, size1, -size2 / 2},
        {size0 / 2, size1, size2 / 2},
        {-size0 / 2, size1, size2 / 2},
        {size0 / 2, 0, size2 / 2},
        {size0 / 2, 0, -size2 / 2},
        {-size0 / 2, 0, -size2 / 2},
        {-size0 / 2, 0, size2 / 2},
        {-size0 / 2, 0, -size2 / 2},
        {-size0 / 2, size1, -size2 / 2},
        {-size0 / 2, size1, size2 / 2},
        {-size0 / 2, 0, size2 / 2},
        {size0 / 2, 0, size2 / 2},
        {size0 / 2, size1, size2 / 2},
        {size0 / 2, size1, -size2 / 2},
        {size0 / 2, 0, -size2 / 2}};
    glBufferData(GL_ARRAY_BUFFER, sizeof(cube_vertices), cube_vertices, GL_DYNAMIC_DRAW);

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);
    glUseProgram(program_box);
    glBindBuffer(GL_ARRAY_BUFFER, vbo_coord_box);
    glEnableVertexAttribArray(pos_coord_box);
    glVertexAttribPointer(pos_coord_box, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glBindBuffer(GL_ARRAY_BUFFER, vbo_color_box);
    glEnableVertexAttribArray(pos_color_box);
    glVertexAttribPointer(pos_color_box, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);

    float cameraview_data[16] = {};
    float projectionMatrix_data[16] = {};
    [BoxRenderer getGLMatrix:cameraview result:&cameraview_data];
    [BoxRenderer getGLMatrix:projectionMatrix result:&projectionMatrix_data];
    glUniformMatrix4fv(pos_trans_box, 1, 0, cameraview_data);
    glUniformMatrix4fv(pos_proj_box, 1, 0, projectionMatrix_data);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo_faces_box);
    glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, 0);
}

@end
