#include <ctype.h>
#include <stdlib.h>
#include <string.h>

#include "epd_draw.h"

void epd_draw_pixel_ex(uint8_t *buf, int stride, int width, int height, int x, int y, epd_color_t color)
{
    if (x < 0 || x >= width || y < 0 || y >= height) {
        return;
    }
    size_t byte_index = (size_t)y * stride + (size_t)x / 2;
    uint8_t nibble = (uint8_t)color & 0x0F;
    if (x % 2 == 0) {
        buf[byte_index] = (buf[byte_index] & 0x0F) | (nibble << 4);
    } else {
        buf[byte_index] = (buf[byte_index] & 0xF0) | nibble;
    }
}

void epd_draw_pixel(uint8_t *frame, int x, int y, epd_color_t color)
{
    epd_draw_pixel_ex(frame, EPD_BYTES_PER_ROW, EPD_WIDTH, EPD_HEIGHT, x, y, color);
}

/* fonts.h's sFONT tables cover the printable ASCII range starting at ' '
 * (0x20), one glyph per character, MSB-first within each row (see
 * components/epaper_fonts). */
static bool font_pixel_set(const sFONT *font, char c, int row, int col)
{
    if (c < ' ' || (unsigned char)c > 0x7E) {
        return false;
    }
    int bytes_per_row = (font->Width + 7) / 8;
    size_t glyph_index = (size_t)(c - ' ');
    const uint8_t *glyph = font->table + glyph_index * font->Height * bytes_per_row + (size_t)row * bytes_per_row;
    int byte_idx = col / 8;
    int bit_in_byte = 7 - (col % 8);
    return (glyph[byte_idx] >> bit_in_byte) & 0x1;
}

void epd_draw_text_ex(uint8_t *buf, int stride, int width, int height, const sFONT *font, const char *text,
                       int origin_x, int origin_y)
{
    int cursor_x = origin_x;
    for (const char *p = text; *p != '\0'; p++) {
        char c = (char)toupper((unsigned char)*p);
        for (int row = 0; row < font->Height; row++) {
            for (int col = 0; col < font->Width; col++) {
                if (font_pixel_set(font, c, row, col)) {
                    epd_draw_pixel_ex(buf, stride, width, height, cursor_x + col, origin_y + row, EPD_COLOR_BLACK);
                }
            }
        }
        cursor_x += font->Width;
    }
}

void epd_draw_text(uint8_t *frame, const sFONT *font, const char *text, int origin_x, int origin_y)
{
    epd_draw_text_ex(frame, EPD_BYTES_PER_ROW, EPD_WIDTH, EPD_HEIGHT, font, text, origin_x, origin_y);
}

void epd_draw_text_centered_ex(uint8_t *buf, int stride, int width, int height, const sFONT *font, const char *text,
                                int center_x, int y)
{
    int text_width = (int)strlen(text) * font->Width;
    epd_draw_text_ex(buf, stride, width, height, font, text, center_x - text_width / 2, y);
}

void epd_draw_text_centered(uint8_t *frame, const sFONT *font, const char *text, int center_x, int y)
{
    epd_draw_text_centered_ex(frame, EPD_BYTES_PER_ROW, EPD_WIDTH, EPD_HEIGHT, font, text, center_x, y);
}

void epd_draw_line(uint8_t *frame, int x0, int y0, int x1, int y1, int thickness)
{
    int dx = x1 - x0;
    int dy = y1 - y0;
    int steps = abs(dx) > abs(dy) ? abs(dx) : abs(dy);
    if (steps == 0) {
        steps = 1;
    }
    int half = thickness / 2;
    for (int i = 0; i <= steps; i++) {
        int x = x0 + dx * i / steps;
        int y = y0 + dy * i / steps;
        for (int oy = -half; oy <= half; oy++) {
            for (int ox = -half; ox <= half; ox++) {
                epd_draw_pixel(frame, x + ox, y + oy, EPD_COLOR_BLACK);
            }
        }
    }
}

void epd_draw_checkmark(uint8_t *frame, int x, int y, int size)
{
    int thickness = size / 6;
    if (thickness < 2) {
        thickness = 2;
    }
    /* Short down-stroke, then long up-stroke -- classic checkmark proportions. */
    epd_draw_line(frame, x, y + size / 2, x + size / 3, y + size - thickness, thickness);
    epd_draw_line(frame, x + size / 3, y + size - thickness, x + size, y, thickness);
}
