/*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 "Glyph.hpp"

#include <limits>
#include <cstddef>
#include <iostream>

namespace
{
    /// Returns the next power of two after \p number (may be equal)
    /// Does not return a value strictly greater than 2^maxPow2
    std::size_t nextPow2(std::size_t number, std::size_t maxPow2)
    {
        std::size_t i = 0;
        std::size_t buffer = 1;
        while (buffer < number && i < maxPow2)
        {
            buffer *= 2;
            i++;
        }

        return buffer;
    }
}

Glyph::Glyph() : m_width(0), m_height(0)
{

}

void Glyph::load(const FT_BitmapGlyph &data, const Vector &advance)
{
    //Texture
    FT_Bitmap &bitmap = data->bitmap;

    //Maximum allowed power of two to avoid wraparound in pixels[] array size
    //Also ensure less than 30 bits are used to avoid overflowing GLsizei
    //which is at least a signed 32-bit integer
    constexpr std::size_t maxPow2 =
        std::numeric_limits<std::size_t>::digits / 2 - 1 <= 30 ?
        std::numeric_limits<std::size_t>::digits / 2 - 1  : 30;

    const std::size_t width = nextPow2(bitmap.width, maxPow2);
    const std::size_t height = nextPow2(bitmap.rows, maxPow2);

    GLubyte *pixels = new GLubyte[2 * width * height];

    for (std::size_t j = 0; j < height; j++)
    {
        for (std::size_t i = 0; i < width; i++)
        {
            pixels[2 * (i + j * width)] = 255;
            pixels[2 * (i + j * width) + 1] =
                (i >= bitmap.width || j >= bitmap.rows) ? 0 :
                         bitmap.buffer[i + bitmap.width * j];
        }
    }

    m_texture.loadFromMemory(pixels, GL_RGBA, GL_LUMINANCE_ALPHA,
                             static_cast<GLsizei>(width),
                             static_cast<GLsizei>(height),
                             Texture::MinMagFilter::Nearest);
    delete[] pixels;

    m_width = bitmap.width;
    m_height = bitmap.rows;

    m_topLeft = Vector(static_cast<float>(data->left),
                       static_cast<float>(data->top));
    m_advance = advance;
}

void Glyph::draw() const
{
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();

    const GLfloat x2 = (m_texture.getWidth() <= 0) ? 1.f :
        static_cast<GLfloat>(m_width) /
        static_cast<GLfloat>(m_texture.getWidth());

    const GLfloat y2 = (m_texture.getHeight() <= 0) ? 1.f :
        static_cast<GLfloat>(m_height) /
        static_cast<GLfloat>(m_texture.getHeight());

    glTranslatef(static_cast<GLfloat>(m_topLeft.x),
                 static_cast<GLfloat>(-m_topLeft.y), 0.f);
    m_texture.draw(0.f, 0.f, x2, y2);

    glPopMatrix();
}

const Vector &Glyph::getAdvance() const
{
    return m_advance;
}

const Vector &Glyph::getTopLeft() const
{
    return m_topLeft;
}

unsigned int Glyph::getWidth() const
{
    return m_width;
}

unsigned int Glyph::getHeight() const
{
    return m_height;
}
