#include "AssetManager.h"
#include "Log.h"
#include "Profiling.h"
#define STBI_NO_STDIO
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define STB_TRUETYPE_IMPLEMENTATION
#include "stb_truetype.h"

AssetManager* AssetManager::ptr = nullptr;

constexpr int PADDING = 3;

bool AssetManager::loadTexture(const char* path, Texture* p_texture)
{
    ZoneScoped;
    FileBuffer texture_data = {};
    if (!loadFile(path, &texture_data))
        return false;
    int width, height, channels;
    const auto* raw_data = reinterpret_cast<const stbi_uc*>(texture_data.data);
    stbi_uc* data = stbi_load_from_memory(raw_data, static_cast<int>(texture_data.size), &width, &height, &channels, 4);
    if (!data) {
        ALOGE("Failed to parse texture file %s", path);
        releaseFileBuffer(texture_data);
        return false;
    }
    *p_texture = Texture(width, height, data);
    return true;
}

/* Calculate required bitmap size for a given font bounding box, scale factor and glyph count */
static size_t getBitmapSize(int x0, int y0, int x1, int y1, float scale_factor, int glyph_count)
{

    float w = scale_factor * (x1 - x0);
    float h = scale_factor * (y1 - y0);
    float glyph_size = ceilf((w > h) ? w : h);
    float glyphs_per_row = ceilf(sqrtf((float)glyph_count));

    size_t size = static_cast<size_t>((glyph_size + PADDING) * glyphs_per_row);

    /* Round up to nearest power of two, iff it's smaller than 4096 */
    for (size_t bit = 0; bit < 12 /* => 4096 */; ++bit) {
        if ((1uLL << bit) >= size) {
            return 1uLL << bit;
        }
    }
    return size;
}

bool AssetManager::loadFontData(const char* path, float char_height, FontData* p_font)
{
    ZoneScoped;
    auto lookup_pair = std::make_pair(StringRepository::global->internString(path), char_height);
    if (m_font_cache.count(lookup_pair) != 0) {
        *p_font = m_font_cache[lookup_pair];
        return true;
    }
    FileBuffer ttf_data = {};
    if (!loadFile(path, &ttf_data))
        return false;

    FontData font_data = {};
    auto* data = reinterpret_cast<const unsigned char*>(ttf_data.data);

    font_data.char_height = char_height;
    int off = stbtt_GetFontOffsetForIndex(data, 0);
    if (off < 0) {
        releaseFileBuffer(ttf_data);
        return false;
    }

    stbtt_fontinfo info;
    if (!stbtt_InitFont(&info, data, off)) {
        releaseFileBuffer(ttf_data);
        return false;
    }
    font_data.scale_factor = stbtt_ScaleForPixelHeight(&info, char_height);
    stbtt_GetFontVMetrics(&info, &font_data.ascent, &font_data.descent, &font_data.line_gap);
    int x0, y0, x1, y1;
    stbtt_GetFontBoundingBox(&info, &x0, &y0, &x1, &y1);
    font_data.baseline_adjust = font_data.scale_factor * static_cast<float>(-y0);

    const int GLYPH_COUNT = (225 - 32) + 1;
    font_data.bitmap_size = getBitmapSize(x0, y0, x1, y1, font_data.scale_factor, GLYPH_COUNT);

    unsigned char* bitmap_memory = (unsigned char*)malloc(font_data.bitmap_size * font_data.bitmap_size);

    stbtt_pack_context context;
    stbtt_PackBegin(&context, bitmap_memory, font_data.bitmap_size, font_data.bitmap_size, 0, PADDING, nullptr);
    stbtt_PackSetOversampling(&context, 1, 1);
    stbtt_PackFontRange(&context,
                        reinterpret_cast<const unsigned char*>(ttf_data.data),
                        0,
                        STBTT_POINT_SIZE(char_height),
                        32,
                        225,
                        font_data.char_data);
    stbtt_PackFontRange(&context,
                        reinterpret_cast<const unsigned char*>(ttf_data.data),
                        0,
                        STBTT_POINT_SIZE(char_height),
                        8364,
                        1,
                        font_data.char_data + 225);
    stbtt_PackEnd(&context);
    free(bitmap_memory);

    *p_font = font_data;
    releaseFileBuffer(ttf_data);
    m_font_cache[lookup_pair] = font_data;
    return true;
}
bool AssetManager::loadFontBitmap(const char* path, float char_height, Texture* p_texture)
{
    ZoneScoped;

    FontData font_data;
    if (!loadFontData(path, char_height, &font_data))
        return false;

    FileBuffer ttf_data = {};
    if (!loadFile(path, &ttf_data))
        return false;

    unsigned char* bitmap_memory = (unsigned char *)malloc(font_data.bitmap_size * font_data.bitmap_size);
    stbtt_packedchar char_data[226];
  
    stbtt_pack_context context;
    stbtt_PackBegin(&context, bitmap_memory, font_data.bitmap_size, font_data.bitmap_size, 0, PADDING, nullptr);
    stbtt_PackSetOversampling(&context, 1, 1);
    stbtt_PackFontRange(&context, reinterpret_cast<const unsigned char*>(ttf_data.data),
        0, STBTT_POINT_SIZE(char_height), 32, 225, char_data);
    stbtt_PackFontRange(&context, reinterpret_cast<const unsigned char*>(ttf_data.data),
        0, STBTT_POINT_SIZE(char_height), 8364, 1, char_data + 225);
    stbtt_PackEnd(&context);
    *p_texture = Texture(font_data.bitmap_size, font_data.bitmap_size, bitmap_memory, GL_R8);
    releaseFileBuffer(ttf_data);

    free(bitmap_memory);

    return true;
}
