#include <filein/texture_loader.hpp>

#ifndef STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
#endif

#include <filein/stb_image.h>
#include <utils/assumptions.hpp>

#include <filesystem>
#include <iostream>
#include <vector>

texture_ptr load_texture_2d(const std::filesystem::path filename) 
{
    stbi_set_flip_vertically_on_load(true);

    int width, height, channels;
    unsigned char* data = stbi_load(filename.generic_string().data(), &width, &height, &channels, 4);

    GLuint texture;
    glCreateTextures(GL_TEXTURE_2D, 1, &texture);

    glTextureStorage2D(texture, static_cast<GLsizei>(std::log2(width)), GL_RGBA8, width, height);

    glTextureSubImage2D(texture,
                        0,                         //
                        0, 0,                      //
                        width, height,             //
                        GL_RGBA, GL_UNSIGNED_BYTE, //
                        data);

    stbi_image_free(data);

    glGenerateTextureMipmap(texture);

    glTextureParameteri(texture, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTextureParameteri(texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    return std::make_shared<Texture>(texture);
}

texture_ptr load_texture_cube_map(const std::vector<std::filesystem::path> &paths)
{
    stbi_set_flip_vertically_on_load(false);

    int width, height, channels;
    unsigned char* data = stbi_load(paths[0].generic_string().data(), &width, &height, &channels, 4);
    ASSUMPTION(width == height);

    GLuint texture;
    glCreateTextures(GL_TEXTURE_CUBE_MAP, 1, &texture);

    glTextureStorage2D(texture, static_cast<GLsizei>(std::log2(width)) + 1, GL_RGBA8, width, height);

    assert(paths.size() == 6);
    for (unsigned int i = 0; i < paths.size(); ++i)
    {
        int width, height, channels;
        unsigned char* data = stbi_load(paths[i].generic_string().data(), &width, &height, &channels, 4);
        if (data)
        {
            glTextureSubImage3D(texture,
                                0, // Mipmap level
                                0, 0, i, // (x, y, face) offset
                                width, height, 1, // (w, h, depth)
                                GL_RGBA,
                                GL_UNSIGNED_BYTE,
                                data);

            stbi_image_free(data);
        }
        else
        {
            std::cerr << "Cubemap tex failed to load at path: " << paths[i] << std::endl;
            stbi_image_free(data);
        }
    }
    glGenerateTextureMipmap(texture);

    glTextureParameteri(texture, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    // glTextureParameteri(texture, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTextureParameteri(texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTextureParameteri(texture, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTextureParameteri(texture, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTextureParameteri(texture, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
    return std::make_shared<Texture>(texture);
}  