#include <GL/glew.h>
#include "raw.h"
#include "texture.h"
#include <render/render.h>

namespace render{

texture::texture()
{
    type = t_ubyte;
    format = f_rgba;
    mode = m_texture2d;
}

texture::texture(const std::string &path, bool smooth, bool flip):texture()
{
    load(path, smooth, flip);
}

texture::~texture()
{
    free();
}

bool texture::load(const std::string &path, bool smooth, bool flip)
{
    _name = path;
    rawImage img;

	if(!img.load(path, flip))
        return 0;

    _size = img.size;
    _alloc();

    format = f_rgba;
    type = t_ubyte;

    _obj->mode = mode;
    _obj->format = format;
    _obj->type = type;

    bind();
    glTexImage2D(mode, 0, format, _size.x, _size.y, 0, GL_BGRA, type, img.pixeles);
    if(smooth)
    {
        parameteri(GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        parameteri(GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
        glGenerateMipmap(GL_TEXTURE_2D);
    }
    else
    {
        parameteri(GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        parameteri(GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    }
	return 1;
}

void texture::data(const vec2u &size, void *data)
{
    _alloc();

    format = f_rgba;
    type = t_ubyte;

    _obj->mode = mode;
    _obj->format = format;
    _obj->type = type;

    bind();
    _size = size;
    glTexImage2D(mode, 0, GL_RGBA, size.x, size.y, 0, GL_RGBA, type, data);
}

void texture::create(const vec2u &size, bool smooth)
{
    _alloc();
    _obj->mode = mode;
    _obj->format = format;
    _obj->type = type;
    bind();
	glTexImage2D(_obj->mode, 0, format, size.x, size.y, 0, format, type, 0);
	if(smooth)
    {
        parameteri(GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        parameteri(GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    }
    else
    {
        parameteri(GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        parameteri(GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    }
    _size = size;
}

void texture::_alloc()
{
    if(_obj.use_count())
        return;
    _obj = std::make_shared<__glTexture>(_name);
    _addToList();
}

void texture::free()
{
    if(!_obj.use_count())
        return;
    unbind();
    _obj = nullptr;
    printf("free %s\n", _name.c_str());
    _removeFromList();
}

void texture::bind(uint s) const
{
    if(!_obj)
        return;
    static uint slot = 0;
    if(render::getActiveTexture(s) == this)
        return;
    render::bindTexture(this, s);
    if(slot != s)
    {
        glActiveTexture(GL_TEXTURE0+s);
        slot = s;
    }
    glBindTexture(_obj->mode, _obj->obj);
}

void texture::unbind(int s)
{
    //if(s < 0)
    /*render::bindTexture(s, 0);
    glActiveTexture(GL_TEXTURE0+s);
    glBindTexture(_obj->mode, 0);*/

}

void texture::parameteri(uint param, uint value)
{
    bind();
    glTexParameteri(_obj->mode, param, value);
}

const vec2u &texture::size() const noexcept
{
    return _size;
}

uint texture::getGLTexture()
{
    return _obj->obj;
}

uint texture::getMode()
{
    return _obj->mode;
}

}
