#include "Font.h"

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

static FT_Library ft_library_handle;
static bool ft_initialized;

bool Initialize_FreeType()
{
    if (ft_initialized)
    {
        return true;
    }

    if (FT_Init_FreeType(&ft_library_handle) != 0)
    {
        return false;
    }

    ft_initialized = true;
    return true;
}

bool Font::load(const char * fname, int size)
{
    if (!Initialize_FreeType())
    {
        return false;
    }

    if (FT_New_Face(ft_library_handle, fname, 0, &m_face) != 0)
    {
        return false;
    }

    FT_Set_Pixel_Sizes(m_face, 0, size);

    return preload_glyphs();
}

bool Font::preload_glyphs()
{
    static const char preload_glyph_list[] = "HMgjqxy_|^";

    int max_top = -9999;
    int min_bottom = 9999;
    m_advance = 0;

    for (size_t i = 0u; i < sizeof(preload_glyph_list) - 1u; i++)
    {
        std::shared_ptr<Glyph> g = get_glyph(preload_glyph_list[i]);
        if (g == NULL)
        {
            return false;
        }
        if (m_face->glyph->bitmap_top > max_top)
        {
            max_top = m_face->glyph->bitmap_top;
        }
        int bitmap_bottom = m_face->glyph->bitmap_top - m_face->glyph->bitmap.rows;
        if (bitmap_bottom < min_bottom)
        {
            min_bottom = bitmap_bottom;
        }
        if (g->get_advance() > m_advance)
        {
            m_advance = g->get_advance();
        }
    }

    m_line_height = round_up_26_6(m_face->size->metrics.height);
    m_baseline_offset = (m_line_height - (max_top - min_bottom)) / 2 - min_bottom;

    return true;
}

std::shared_ptr<Glyph> Font::get_glyph(FT_ULong character)
{
    auto it = m_glyphs.find(character);
    if (it != m_glyphs.end())
    {
        return it->second;
    }
    std::shared_ptr<Glyph> glyph = std::make_shared<Glyph>();
    if (!glyph->load(m_face, character))
    {
        glyph = nullptr;
    }
    m_glyphs[character] = glyph;
    return glyph;
}
