#ifndef TENCHU_LIB_H
#define TENCHU_LIB_H

#include <string.h>

void checkCode(int code, char* errorMsg) {
    if (code < 0) {
        fprintf(stderr, "Application Error %i: %s\n", code, errorMsg);
        exit(1);
    }
}

void* checkPtr(void *ptr, char* errorMsg) {
    if (ptr == NULL) {
        fprintf(stderr, "Application Error: %s\n", errorMsg);
        exit(1);
    }
    return ptr;
}

void checkShader(unsigned int shader, int statusFlag, char* actionName) {
    int success;
    glGetShaderiv(shader, statusFlag, &success);
    if (success < 0) {
        fprintf(stderr, "%s Error %i\n", actionName, success);
        exit(1);
    }
}

char* loadText(char* path) {
    char* buffer = NULL;
    long length;
    FILE* f = fopen(path, "rb");
    if (f) {
        fseek(f, 0, SEEK_END);
        length = ftell(f);
        fseek(f, 0, SEEK_SET);
        buffer = calloc(length, sizeof(char));
        if (buffer) {
            fread(buffer, 1, length, f);
        }
        fclose(f);
    }
    return buffer;
}

void checkCompileErrors(unsigned int object, char* type)
{
    int success;
    char infoLog[1024];
    if (strcmp("PROGRAM", type))
    {
        glGetShaderiv(object, GL_COMPILE_STATUS, &success);
        if (!success)
        {
            glGetShaderInfoLog(object, 1024, NULL, infoLog);
            fprintf(stderr, "| ERROR::SHADER: Compile-time error: Type: %s \n %s\n", type, infoLog);
        }
    }
    else
    {
        glGetProgramiv(object, GL_LINK_STATUS, &success);
        if (!success)
        {
            glGetProgramInfoLog(object, 1024, NULL, infoLog);
            fprintf(stderr, "| ERROR::Shader: Link-time error: Type: %s \n %s\n", type, infoLog);
        }
    }
}

unsigned int compileShaderProgram(char* vertSrcPath, char* fragSrcPath, char* geoSrcPath) {
    unsigned int vertShader, fragShader, geoShader;

    char* vertSrc = loadText(vertSrcPath);
    vertShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertShader, 1, (const GLchar* const*)&vertSrc, NULL);
    glCompileShader(vertShader);
    checkCompileErrors(vertShader, "VERTEX");

    char* fragSrc = loadText(fragSrcPath);

    fragShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragShader, 1, (const GLchar* const*)&fragSrc, NULL);
    glCompileShader(fragShader);
    checkCompileErrors(fragShader, "FRAGMENT");

    char* geoSrc = NULL;
    if (geoSrcPath != NULL) {
        geoSrc = loadText(geoSrcPath);
        geoShader = glCreateShader(GL_GEOMETRY_SHADER);
        glShaderSource(geoShader, 1, (const GLchar* const*)&geoSrc, NULL);
        glCompileShader(geoShader);
        checkCompileErrors(geoShader, "GEOMETRY");
    }

    unsigned int program = glCreateProgram();
    glAttachShader(program, vertShader);
    glAttachShader(program, fragShader);
    if (geoSrcPath != NULL) {
        glAttachShader(program, geoShader);
    }

    glLinkProgram(program);
    checkCompileErrors(program, "PROGRAM");

    glDeleteShader(vertShader);
    glDeleteShader(fragShader);
    free(vertSrc);
    free(fragSrc);
    if (geoSrc != NULL) {
        free(geoSrc);
    }

    return program;
}

#endif
