#include "rendering/Texture.h"
#include "io/ImageReader.h"

Texture::Format::Format(const GL::TextureFormat& tf, bool linear, bool depth)
    : format(tf), linear(linear), depth(depth) {
}

Texture::Format Texture::Format::color8(int channels, bool linear) {
    return Format(GL::TextureFormat::color8(channels), linear);
}

Texture::Format Texture::Format::float16(int channels, bool linear) {
    return Format(GL::TextureFormat::float16(channels), linear);
}

Texture::Format Texture::Format::float32(int channels, bool linear) {
    return Format(GL::TextureFormat::float32(channels), linear);
}

Texture::Format Texture::Format::depth16(bool linear) {
    return Format(GL::TextureFormat::depth16(), linear, true);
}

Texture::Format Texture::Format::depth32(bool linear) {
    return Format(GL::TextureFormat::depth32(), linear, true);
}

Texture::Format Texture::Format::unknown() {
    return Format(GL::TextureFormat::unknown(), false, false);
}

Texture::Texture() : format(Format::unknown()), texture(0), maxMipMaps(0) {
}

Texture::~Texture() {
    GL::deleteTexture(texture);
}

void Texture::init(const Format& format, int maxMipMaps) {
    if(texture != 0) {
        return;
    }
    Texture::format = format;
    Texture::maxMipMaps = maxMipMaps;
    texture = GL::genTexture();
    setNearestFilter();
    setRepeatWrap();
}

void Texture::setNearestFilter() {
    bind();
    if(maxMipMaps > 0) {
        GL::setMipMapNearFilter2D();
    } else {
        GL::setNearFilter2D();
    }
}

void Texture::setLinearFilter() {
    bind();
    if(maxMipMaps > 0) {
        GL::setMipMapLinearFilter2D();
    } else {
        GL::setLinearFilter2D();
    }
}

void Texture::setRepeatWrap() {
    bind();
    GL::setRepeatWrap2D();
}

void Texture::setClampWrap() {
    bind();
    GL::setClampWrap2D();
}

void Texture::setData(int width, int height, const void* data) {
    bind();
    GL::texImage2D(format.format, width, height, data);
    if(maxMipMaps > 0) {
        GL::generateMipmap2D(maxMipMaps);
    }
}

void Texture::bind() const {
    GL::bindTexture2D(texture);
}

void Texture::bindTo(int index) const {
    GL::activeTexture(index);
    bind();
}

Error Texture::load(const char* path, int maxMipMaps) {
    ImageReader::Image image;
    Error error = ImageReader::load(image, path);
    if(error.has()) {
        return error;
    }
    if(image.channels < 1 || image.channels > 4) {
        Error error = {"'"};
        error.message.append(path)
            .append("' has unsupported number of channels: ")
            .append(image.channels);
        return error;
    } else if(image.bitdepth != 8) {
        Error error = {"bit depth of '"};
        error.message.append(path).append("' is not 8");
        return error;
    }
    init(Format::color8(image.channels), maxMipMaps);
    setData(image.width, image.height, image.data);
    return {};
}