#include "font.h"
#include "display.h"
#include "lvgl.h"
#include "stdbool.h"
#include <stdint.h>

static uint16_t find_glyph_id_in_cmap(const lv_font_fmt_txt_cmap_t * cmap, uint32_t character)
{
    uint16_t cmap_last_character = cmap->range_start + cmap->range_length - 1;
    bool character_exists_in_cmap = character >= cmap->range_start && character <= cmap_last_character;
    if (character_exists_in_cmap)
    {
        uint16_t glyph_id = cmap->glyph_id_start - cmap->range_start + character;
        return glyph_id;
    }

    return 0;
}


bool lv_font_get_glyph_dsc_fmt_txt(const lv_font_t * font, lv_font_fmt_txt_glyph_dsc_t * dsc_out, uint32_t character)
{
    lv_font_fmt_txt_dsc_t *font_dsc = (lv_font_fmt_txt_dsc_t *) font->dsc;
    for (uint16_t i = 0; i < font_dsc->cmap_num; i++)
    {
        uint16_t glyph_id = find_glyph_id_in_cmap(font_dsc->cmaps + i, character);

        if (glyph_id == 0) continue;

        *dsc_out = font_dsc->glyph_dsc[glyph_id];
        return true;
    }
    return false;
}

// Left as a dummy definition for LVGL font compatibility.
// We're not processing the bitmaps with draw buffers like LVGL does.
const void * lv_font_get_bitmap_fmt_txt(lv_font_fmt_txt_glyph_dsc_t * g_dsc, lv_draw_buf_t * draw_buf)
{
}

/**
 * @brief Draws a single character to the framebuffer using the specified font.
 *
 * This function renders a character glyph from an LVGL font to the framebuffer at the
 * specified location. The character is drawn in the provided color using a 1-bit bitmap
 * format where set bits are drawn as colored pixels and unset bits are left transparent.
 *
 * @param framebuffer Pointer to the framebuffer array where the character will be drawn.
 *                    Must be large enough to accommodate the character at the specified
 *                    location (typically DISPLAY_HEIGHT * DISPLAY_WIDTH pixels).
 * @param font Pointer to the LVGL font structure containing glyph data and metrics.
 *             The font must be a valid LVGL formatted text font (lv_font_fmt_txt_dsc_t).
 * @param x_loc X coordinate (horizontal position) where the top-left of the character
 *              bounding box will be drawn. Must be within framebuffer bounds.
 * @param y_loc Y coordinate (vertical position) where the top-left of the character
 *              bounding box will be drawn. Must be within framebuffer bounds.
 * @param character The ASCII character code (0-255) to be drawn.
 * @param color The RGB565 color value to use when drawing the character pixels.
 *              Pixels corresponding to set bits in the glyph bitmap will be drawn
 *              in this color.
 *
 * @return The advance width of the drawn glyph in pixels (lower 16 bits of glyph_dsc.adv_w).
 *         This value can be used to calculate the x position for the next character when
 *         drawing text. Returns 0 if the character glyph was not found in the font.
 *
 * @note The function performs no bounds checking. Ensure x_loc and y_loc are within
 *       the framebuffer dimensions to prevent buffer overruns.
 * @note The glyph bitmap is stored in a packed 1-bit format where each bit represents
 *       one pixel (1 = draw pixel, 0 = skip pixel).
 * @note If the character is not found in the font's character map, the function returns
 *       0 without drawing anything.
 */
uint16_t draw_character(rgb565_pixel_t *framebuffer, const lv_font_t *font, const uint16_t x_loc, const uint16_t y_loc, const uint8_t character, rgb565_pixel_t color)
{
    const lv_font_fmt_txt_dsc_t *font_dsc = (lv_font_fmt_txt_dsc_t *)font->dsc;
    lv_font_fmt_txt_glyph_dsc_t glyph_dsc;
    bool glyph_found = lv_font_get_glyph_dsc_fmt_txt(font, &glyph_dsc, character);
    if (!glyph_found) return 0;

    const uint16_t glyph_width = glyph_dsc.box_w;
    const uint16_t glyph_height = glyph_dsc.box_h;
    const int8_t glyph_ofs_x = glyph_dsc.ofs_x;
    const int8_t glyph_ofs_y = glyph_dsc.ofs_y;
    const uint32_t num_bitmap_bits = glyph_height * glyph_width;

    for (uint32_t bitmap_pixel_index = 0; bitmap_pixel_index < num_bitmap_bits; bitmap_pixel_index++)
    {
        const uint8_t current_bit_in_byte = bitmap_pixel_index % 8;
        const uint16_t bitmap_byte_index = glyph_dsc.bitmap_index + (bitmap_pixel_index / 8);
        const uint32_t fb_pixel_x = x_loc + (bitmap_pixel_index % glyph_width) + glyph_ofs_x;
        const uint32_t line_top = y_loc + font->base_line - glyph_height;
        const uint32_t fb_pixel_y = line_top + (bitmap_pixel_index / glyph_width) - glyph_ofs_y;
        const uint32_t fb_pixel_index = (fb_pixel_y * DISPLAY_WIDTH) + fb_pixel_x;

        const bool bit_on = font_dsc->glyph_bitmap[bitmap_byte_index] & (0x80 >> current_bit_in_byte);
        if (bit_on)
        {
            framebuffer[fb_pixel_index] = color;
        }
    }

    return (glyph_dsc.adv_w & 0x0000FFFF);
}

/**
 * @brief Draws a null-terminated string to the framebuffer using the specified font.
 *
 * This function renders a complete string of characters to the framebuffer by iteratively
 * calling draw_character() for each character in the string. Characters are drawn horizontally
 * from left to right, with each character's advance width determining the spacing for the
 * next character. All characters are drawn on the same baseline (y_loc remains constant).
 *
 * @param framebuffer Pointer to the framebuffer array where the string will be drawn.
 *                    Must be large enough to accommodate the entire string at the specified
 *                    location (typically DISPLAY_HEIGHT * DISPLAY_WIDTH pixels).
 * @param font Pointer to the LVGL font structure containing glyph data and metrics.
 *             The font must be a valid LVGL formatted text font (lv_font_fmt_txt_dsc_t).
 * @param x_loc X coordinate (horizontal position) where the first character's top-left
 *              bounding box will be drawn. Must be within framebuffer bounds.
 * @param y_loc Y coordinate (vertical position) where all characters' top-left bounding
 *              boxes will be drawn. Must be within framebuffer bounds.
 * @param string Pointer to a null-terminated C string (array of uint8_t) containing
 *               the characters to be drawn. The function will stop drawing when it
 *               encounters a null terminator ('\0').
 * @param color The RGB565 color value to use when drawing all character pixels.
 *              Pixels corresponding to set bits in each glyph bitmap will be drawn
 *              in this color.
 *
 * @note The function performs no bounds checking. Ensure x_loc and y_loc are within
 *       the framebuffer dimensions, and that the entire string will fit within the
 *       framebuffer bounds to prevent buffer overruns.
 * @note Characters not found in the font (draw_character returns 0) will be skipped
 *       without advancing the x position, which may cause subsequent characters to
 *       overlap with missing characters.
 * @note The string must be null-terminated. The function will continue drawing until
 *       it encounters a '\0' character.
 * @note All characters in the string are drawn on the same horizontal baseline (y_loc
 *       remains constant for all characters).
 */
void draw_string(rgb565_pixel_t *framebuffer, const lv_font_t *font, const uint16_t x_loc, const uint16_t y_loc, const uint8_t *string, rgb565_pixel_t color)
{
    uint32_t current_x = x_loc << 4;

    for (const uint8_t *char_ptr = string; *char_ptr != '\0'; char_ptr++)
    {
        uint16_t advance_width = draw_character(framebuffer, font, current_x >> 4, y_loc, *char_ptr, color);
        current_x += advance_width;
    }
}
