/*Copyright (C) 2010-2023 Duncan Deveaux

This file is part of Hikou no mizu.

Hikou no mizu is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Hikou no mizu is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Hikou no mizu.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "Texture.hpp"

#include "Tools/Log.hpp"
#include "Tools/Format.hpp"
#include "Tools/BuildValues.hpp" ///< Generated at build time
#include <SDL2/SDL_image.h>

Texture::Texture() : m_glTexture(0), m_width(0), m_height(0)
{

}

Texture::~Texture()
{
    glDeleteTextures(1, &m_glTexture);
}

void Texture::draw(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2) const
{
    if (m_glTexture == 0)
        return;

    //Enable needed modules
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, m_glTexture);

    //Draw
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    GLfloat width = (x2 - x1) * static_cast<GLfloat>(m_width);
    if (width < 0.f) width *= -1.f;

    GLfloat height = (y2 - y1) * static_cast<GLfloat>(m_height);
    if (height < 0.f) height *= -1.f;

    const float vertices[8] = {0.f,0.f, width,0.f, width,height, 0.f,height};
    const float textCoords[8] = {x1,y1, x2,y1, x2,y2, x1,y2};

    glVertexPointer(2, GL_FLOAT, 0, vertices);
    glTexCoordPointer(2, GL_FLOAT, 0, textCoords);

    glDrawArrays(GL_QUADS, 0, 4);

    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    glDisableClientState(GL_VERTEX_ARRAY);

    //Disable loaded modules
    glDisable(GL_TEXTURE_2D);
    glDisable(GL_BLEND);
}

GLsizei Texture::getWidth() const
{
    return m_width;
}

GLsizei Texture::getHeight() const
{
    return m_height;
}

bool Texture::loadFromFile(const std::string &relPath, MinMagFilter filter)
{
    //Load surface
    std::string path = BuildValues::data(relPath);

    SDL_Surface *surface = IMG_Load(path.c_str());
    if (!surface)
    {
        Log::err(Format::format("The texture file: {} could not be opened, "
                                "reason: {}", path, IMG_GetError()));
        return false;
    }

    //Read info
    GLenum format;
    if (surface->format->BytesPerPixel == 3)
        format = GL_RGB;
    else if (surface->format->BytesPerPixel == 4)
        format = GL_RGBA;
    else
    {
        Log::err(Format::format("The texture file: {} has unsupported bpp ({})",
                                path, +surface->format->BytesPerPixel));
        SDL_FreeSurface(surface);
        return false;
    }

    if (surface->w <= 0 || surface->h <= 0)
    {
        Log::err(Format::format("The texture file: {} yields invalid surface "
                                "size ({}x{})", path, surface->w, surface->h));
        SDL_FreeSurface(surface);
        return false;
    }

    //Load the actual GL texture
    if (!loadFromMemory(static_cast<GLvoid*>(surface->pixels),
                        static_cast<GLint>(format), format,
                        static_cast<GLsizei>(surface->w),
                        static_cast<GLsizei>(surface->h), filter))
    {
        Log::err(Format::format(
                    "The texture file: {} could not be handled by OpenGL",
                    path));
        SDL_FreeSurface(surface);
        return false;
    }

    //Clear surface
    SDL_FreeSurface(surface);
    return true;
}

bool Texture::loadFromMemory(const GLvoid *pixels, GLint format,
                             GLenum dataFormat, GLsizei width, GLsizei height,
                             MinMagFilter filter)
{
    //Delete previous resources
    glDeleteTextures(1, &m_glTexture);

    glGetError(); //Clear errors

    glGenTextures(1, &m_glTexture);
    glBindTexture(GL_TEXTURE_2D, m_glTexture);
    glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0,
                 dataFormat, GL_UNSIGNED_BYTE, pixels);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
        (filter == Texture::MinMagFilter::Linear) ? GL_LINEAR : GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
        (filter == Texture::MinMagFilter::Linear) ? GL_LINEAR : GL_NEAREST);

    if (glGetError() == GL_NO_ERROR)
    {
        m_width = width;
        m_height = height;

        return true;
    }

    //Delete broken resources
    glDeleteTextures(1, &m_glTexture);
    m_glTexture = 0;

    return false;
}
