#pragma once
#include "image.h"

class Texture2D
{
    GLuint m_tex = 0;
    int m_width = 0;
    int m_height = 0;
    GLint m_format = 0;
    GLint m_iformat = 0;
public:
    Texture2D() noexcept = default;
    Texture2D(const Texture2D&) = delete;
    void operator=(const Texture2D&) = delete;
    Texture2D(Texture2D&& other) noexcept;
    void operator=(Texture2D&& other) noexcept;
    ~Texture2D();

    bool has_mips = false;
    bool create(int width, int height);
    bool create(int width, int height, GLint internal_format);
    bool create(int width, int height, GLint internal_format, GLint format, const uint8_t* data = nullptr);
    bool create(const Image& img);
    void assign(GLuint tex, int w = -1, int h = -1);
    void assign(GLuint tex, int w, int h, GLuint internal_format, GLuint format);
    bool load(std::string filename);
    bool load_file(std::string filename);
    void destroy();
    void bind() const;
    void unbind() const;
    void update(const uint8_t* data);
    bool ready() const { return m_tex != 0; }
    void create_mipmaps();
    glm::vec2 size() const;
    Image get_image() const noexcept;
};

struct TextureCube
{
    TextureCube() noexcept = default;
    TextureCube(const TextureCube&) = delete;
    void operator=(const TextureCube&) = delete;
    TextureCube(TextureCube&& other) noexcept;
    void operator=(TextureCube&& other) noexcept;
    ~TextureCube() noexcept;
    
    static std::array<int, 6> m_faces_map;

    GLuint m_cubetex_id;
    std::array<GLuint, 6> m_faces{ 0 };
    int m_resolution = 0;

    bool create(int resolution) noexcept;
    void destroy() noexcept;
    void bind() const noexcept;
};

class Sampler
{
    GLuint id = 0;
    mutable GLint current_unit = 0;
public:
    bool create();
    bool create(GLint filter);
    bool create(GLint filter, GLint wrap);
    void set();
    void set(GLint filter);
    void set(GLint filter, GLint wrap);
    void set_filter(GLint filter_min, GLint filter_mag);
    void set_border(glm::vec4 rgba);
    void bind(int unit) const;
    void unbind();
    bool ready() const { return id != 0; }
};

class TextureManager
{
public:
    static std::map<uint16_t, Texture2D> m_textures;
    static bool load(const char* path, bool generate_mipmpas = false);
    static void assign(uint16_t id, GLuint tex, int w = -1, int h = -1);
    static void assign(uint16_t id, GLuint tex, int w, int h, GLuint internal_format, GLuint format);
    static Texture2D& get(uint16_t id);
    static void invalidate();
};
