#include <basilisk/render/texture.h>

namespace bsk::internal {


/**
 * @brief Construct a new Texture object from data
 * 
 * @param data 
 * @param width 
 * @param height 
 * @param format 
 * @param type 
 */
Texture::Texture(const void* data, unsigned int width, unsigned int height, unsigned int format, unsigned int type): width(width), height(height) {
    // Create one texture, and update texture with the ID
    glGenTextures(1, &ID); 
    // Bind the texture to start working on it
    glBindTexture(GL_TEXTURE_2D, ID);
    // Add image data to the texture
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, format, type, data);
    // Generate Mipmaps
    glGenerateMipmap(GL_TEXTURE_2D);
}

/**
 * @brief Destroy the Texture object, release GL data
 *        Does not destroy the image.
 * 
 */
Texture::~Texture() {
    glDeleteTextures(1, &ID);
}

/**
 * @brief Binds the texture to make changes or use for texture unit. Does not bind to shader.
 * 
 */
void Texture::bind() {
    glBindTexture(GL_TEXTURE_2D, ID);
}

/**
 * @brief Sets the filter of all the images in the texture array
 * 
 * @param magFilter GL_LINEAR
 * @param minFilter GL_LINEAR
 */
void Texture::setFilter(unsigned int magFilter, unsigned int minFilter) {
    // Bind the texture
    bind();
    // Set filter
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
}

/**
 * @brief Sets the wrap method for all images in the texture array
 * 
 * @param wrap GL_REPEAT
 */
void Texture::setWrap(unsigned int wrap) {
    // Bind the texture
    bind();
    // Set wrap
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap);	
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap);
}

void Texture::write(const void* data, unsigned int width, unsigned int height, unsigned int xOffset, unsigned int yOffset) {
    // Bind the vbo to start working on it
    glBindBuffer(GL_TEXTURE_2D, ID);
    // Get dimensions
    if (!width) { width = this->width; }
    if (!height) { height = this->height; }
    // Write the data 
    glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
    // Unbind for safety
    glBindBuffer(GL_TEXTURE_2D, 0);
}

}