#include "Font.h"
#include <iostream>
#include <stdint.h>

#define round_up_26_6(val) (((val) + 63) >> 6u)

namespace jes
{
    Font::Font()
    {
        m_loaded = false;
    }

    Font::~Font()
    {
        if (m_loaded)
        {
            FT_Done_Face(m_face);
        }
    }

    bool Font::load(FT_Library ft, const char * fname, size_t size)
    {
        if (FT_New_Face(ft, fname, 0, &m_face) != 0)
        {
            std::cerr << "Could not load font " << fname << std::endl;
            return false;
        }

        FT_Set_Pixel_Sizes(m_face, 0, size);

        GlyphRef x_glyph = load_glyph('x');
        if (x_glyph == NULL)
        {
            std::cerr << "Could not load 'x' glyph from font" << fname << std::endl;
            return false;
        }
        m_glyphs['x'] = x_glyph;

        m_advance = x_glyph->get_advance();
        m_line_height = round_up_26_6(m_face->size->metrics.height);
        m_baseline_offset = round_up_26_6((m_face->size->metrics.height - (m_face->size->metrics.ascender - m_face->size->metrics.descender)) / 2 - m_face->size->metrics.descender);

        m_loaded = true;
        return true;
    }

    Font::GlyphRef Font::load_glyph(FT_ULong char_code)
    {
        GlyphRef glyph = new Glyph();
        if (glyph->load(m_face, char_code))
            return glyph;
        return NULL;
    }

    Font::Glyph::Glyph()
    {
        m_loaded = false;
    }

    bool Font::Glyph::load(FT_Face face, FT_ULong char_code)
    {
        if (FT_Load_Char(face, char_code, FT_LOAD_RENDER) != 0)
            return false;

        int width = face->glyph->bitmap.width;
        int height = face->glyph->bitmap.rows;
        int left = face->glyph->bitmap_left;
        int top = face->glyph->bitmap_top;
        m_advance = round_up_26_6(face->glyph->advance.x);
        uint8_t * texture = new uint8_t[width * height];
        for (int i = 0; i < height; i++)
        {
            memcpy(&texture[width * i],
                   &face->glyph->bitmap.buffer[width * (height - i - 1)],
                   width);
        }

        glActiveTexture(GL_TEXTURE0);
        m_texture = new GLTexture();
        m_texture->create(width, height);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_ALPHA, GL_UNSIGNED_BYTE, texture);
        delete[] texture;

        float s_min = 0.5 / m_texture->get_width();
        float s_max = (width - 0.5) / m_texture->get_width();
        float t_min = 0.5 / m_texture->get_height();
        float t_max = (height - 0.5) / m_texture->get_height();
        GLfloat box[4][4] = {
            {(GLfloat)left, (GLfloat)(top - height),
                s_min, t_min},
            {(GLfloat)(left + width - 1), (GLfloat)(top - height),
                s_max, t_min},
            {(GLfloat)left, (GLfloat)(top - 1),
                s_min, t_max},
            {(GLfloat)(left + width - 1), (GLfloat)(top - 1),
                s_max, t_max},
        };
        glGenBuffers(1, &m_vbo_id);
        glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id);
        glBufferData(GL_ARRAY_BUFFER, sizeof(box), box, GL_STATIC_DRAW);

        m_loaded = true;
        return true;
    }
}
