#include "low_level_render.h"
#include <cmath>
#include <algorithm>
#include <vector>
#include "./fonts/5x5_font.h"
#include "./fonts/BMplain_font.h"
#include "./fonts/Blokus_font.h"
#include "./fonts/HISKYF21_font.h"
#include "./fonts/Minimum_font.h"
#include "./fonts/SUPERDIG_font.h"
#include "./fonts/acme_5_outlines_font.h"
#include "./fonts/aztech_font.h"
#include "./fonts/crackers_font.h"
#include "./fonts/haiku_font.h"
#include "./fonts/sloth_font.h"
#include "./fonts/zxpix_font.h"
#include "./fonts/Commo-Monospaced_font.h"
#include "./fonts/7linedigital_font.h"
#include "./fonts/BMSPA_font.h"
#include "./fonts/HUNTER_font.h"
#include "./fonts/Raumsond_font.h"
#include "./fonts/bubblesstandard_font.h"
#include "./fonts/formplex12_font.h"
#include "./fonts/homespun_font.h"
#include "./fonts/Minimum_1_font.h"
#include "./fonts/m38_font.h"
#include "./fonts/pzim3x5_font.h"
#include "./fonts/renew_font.h"
#include "./fonts/tama_mini02_font.h"

// Font object definitions
Font font_5x5_obj(reinterpret_cast<const unsigned char*>(font_5x5), 96, 6, 8);
Font font_7linedigital_obj(reinterpret_cast<const unsigned char*>(font_7linedigital), 96, 4, 8);
Font font_acme_5_outlines_obj(reinterpret_cast<const unsigned char*>(font_acme_5_outlines), 96, 6, 8);
Font font_aztech_obj(reinterpret_cast<const unsigned char*>(font_aztech), 96, 6, 8);
Font font_BMplain_obj(reinterpret_cast<const unsigned char*>(font_BMplain), 96, 6, 8);
Font font_BMSPA_obj(reinterpret_cast<const unsigned char*>(font_BMSPA), 96, 8, 8);
Font font_Blokus_obj(reinterpret_cast<const unsigned char*>(font_Blokus), 96, 6, 8);
Font font_bubblesstandard_obj(reinterpret_cast<const unsigned char*>(font_bubblesstandard), 96, 7, 8);
Font font_Commo_Monospaced_obj(reinterpret_cast<const unsigned char*>(font_Commo_Monospaced), 96, 8, 8);
Font font_crackers_obj(reinterpret_cast<const unsigned char*>(font_crackers), 96, 6, 8);
Font font_formplex12_obj(reinterpret_cast<const unsigned char*>(font_formplex12), 96, 8, 8);
Font font_haiku_obj(reinterpret_cast<const unsigned char*>(font_haiku), 96, 6, 8);
Font font_HISKYF21_obj(reinterpret_cast<const unsigned char*>(font_HISKYF21), 96, 6, 8);
Font font_homespun_obj(reinterpret_cast<const unsigned char*>(font_homespun), 96, 7, 8);
Font font_HUNTER_obj(reinterpret_cast<const unsigned char*>(font_HUNTER), 96, 8, 8);
Font font_m38_obj(reinterpret_cast<const unsigned char*>(font_m38), 96, 8, 8);
Font font_Minimum_obj(reinterpret_cast<const unsigned char*>(font_Minimum), 96, 6, 8);
Font font_Minimum_1_obj(reinterpret_cast<const unsigned char*>(font_Minimum_1), 96, 7, 8);
Font font_pzim3x5_obj(reinterpret_cast<const unsigned char*>(font_pzim3x5), 96, 3, 8);
Font font_Raumsond_obj(reinterpret_cast<const unsigned char*>(font_Raumsond), 96, 5, 8);
Font font_renew_obj(reinterpret_cast<const unsigned char*>(font_renew), 96, 7, 8);
Font font_sloth_obj(reinterpret_cast<const unsigned char*>(font_sloth), 96, 6, 8);
Font font_SUPERDIG_obj(reinterpret_cast<const unsigned char*>(font_SUPERDIG), 96, 6, 8);
Font font_tama_mini02_obj(reinterpret_cast<const unsigned char*>(font_tama_mini02), 96, 5, 8);
Font font_zxpix_obj(reinterpret_cast<const unsigned char*>(font_zxpix), 96, 6, 8);

LowLevelRenderer::LowLevelRenderer(uint8_t* buffer, int width, int height)
    : bit_buffer(buffer), V_WIDTH(width), V_HEIGHT(height), current_font(nullptr),
      clipping_enabled(false), clip_x(0), clip_y(0), clip_width(width), clip_height(height), text_color(true) {}

void LowLevelRenderer::set_font(const Font* font) {
    current_font = font;
}

void LowLevelRenderer::set_text_color(bool color) {
    text_color = color;
}

// Clipping functions
void LowLevelRenderer::set_clip_rect(int x, int y, int width, int height) {
    clip_x = x;
    clip_y = y;
    clip_width = width;
    clip_height = height;
    clipping_enabled = true;
}

void LowLevelRenderer::reset_clip_rect() {
    clipping_enabled = false;
    clip_x = 0;
    clip_y = 0;
    clip_width = V_WIDTH;
    clip_height = V_HEIGHT;
}

bool LowLevelRenderer::is_clipping_enabled() const {
    return clipping_enabled;
}

bool LowLevelRenderer::is_point_in_clip_rect(int x, int y) {
    if (!clipping_enabled) return true;
    return (x >= clip_x && x < clip_x + clip_width && 
            y >= clip_y && y < clip_y + clip_height);
}

// Buffer operations
void LowLevelRenderer::invert_buffer() {
    int buffer_size = (V_WIDTH * V_HEIGHT + 7) / 8;  // Round up for bit buffer size
    for (int i = 0; i < buffer_size; ++i) {
        bit_buffer[i] = ~bit_buffer[i];  // Bitwise NOT to invert all bits
    }
}

void LowLevelRenderer::clear_buffer() {
    int buffer_size = (V_WIDTH * V_HEIGHT + 7) / 8;
    for (int i = 0; i < buffer_size; ++i) {
        bit_buffer[i] = 0;
    }
}

void LowLevelRenderer::set_pixel(int x, int y, bool on)
{
    if (x < 0 || x >= V_WIDTH || y < 0 || y >= V_HEIGHT)
        return;
    
    // Check clipping
    if (!is_point_in_clip_rect(x, y))
        return;
        
    int bit_pos = y * V_WIDTH + x;
    if (on)
        bit_buffer[bit_pos / 8] |= (1 << (7 - (bit_pos % 8)));
    else
        bit_buffer[bit_pos / 8] &= ~(1 << (7 - (bit_pos % 8)));
}

void LowLevelRenderer::draw_line(int x0, int y0, int x1, int y1, bool on, int width)
{
    int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
    int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
    int err = dx + dy, e2;
    while (true)
    {
        // Draw a vertical line for the specified width
        for (int w = -(width / 2); w <= (width / 2); w++)
        {
            set_pixel(x0 + w, y0, on);
        }
        for (int w = -(width / 2); w <= (width / 2); w++)
        {
            set_pixel(x0, y0 + w, on);
        }
        set_pixel(x0, y0, on);

        if (x0 == x1 && y0 == y1)
            break;
        e2 = 2 * err;
        if (e2 >= dy)
        {
            err += dy;
            x0 += sx;
        }
        if (e2 <= dx)
        {
            err += dx;
            y0 += sy;
        }
    }
}

void LowLevelRenderer::draw_rectangle(int x, int y, int width, int height, bool on, int line_width)
{
    // Draw top line
    draw_line(x, y, x + width - 1, y, on, line_width);
    // Draw bottom line
    draw_line(x, y + height - 1, x + width - 1, y + height - 1, on, line_width);
    // Draw left line
    draw_line(x, y, x, y + height - 1, on, line_width);
    // Draw right line
    draw_line(x + width - 1, y, x + width - 1, y + height - 1, on, line_width);
}

void LowLevelRenderer::draw_filled_rectangle(int x, int y, int width, int height, bool on, int line_width)
{
    for (int i = 0; i < height; i++)
    {
        draw_line(x, y + i, x + width - 1, y + i, on, line_width);
    }
}

void LowLevelRenderer::draw_rounded_rectangle(int x, int y, int width, int height, int radius, bool on, bool filled)
{
    // Ensure radius doesn't exceed half the smaller dimension
    int max_radius = std::min(width, height) / 2;
    if (radius > max_radius) radius = max_radius;
    if (radius < 0) radius = 0;

    if (!filled) {
        // --- Outline Logic ---
        if (width > 2 * radius) {
            draw_line(x + radius, y, x + width - radius - 1, y, on);
            draw_line(x + radius, y + height - 1, x + width - radius - 1, y + height - 1, on);
        }
        if (height > 2 * radius) {
            draw_line(x, y + radius, x, y + height - radius - 1, on);
            draw_line(x + width - 1, y + radius, x + width - 1, y + height - radius - 1, on);
        }
        if (radius > 0) {
            draw_corner_arc(x + radius, y + radius, radius, 2, on);
            draw_corner_arc(x + width - radius - 1, y + radius, radius, 1, on);
            draw_corner_arc(x + radius, y + height - radius - 1, radius, 3, on);
            draw_corner_arc(x + width - radius - 1, y + height - radius - 1, radius, 0, on);
        }
    } else {
        // --- Filling Logic ---
        // 1. Fill the central rectangular body (excluding the top and bottom radius areas)
        draw_filled_rectangle(x, y + radius, width, height - 2 * radius, on, 1);

        // 2. Fill the top and bottom sections with horizontal lines of varying widths
        for (int i = 0; i < radius; i++) {
            // Calculate horizontal offset using Pythagorean theorem: offset = r - sqrt(r^2 - (r-i)^2)
            int offset = radius - (int)sqrt((double)radius * radius - (double)(radius - i) * (radius - i));
            
            // Top radius row
            draw_line(x + offset, y + i, x + width - offset - 1, y + i, on);
            
            // Bottom radius row
            int bottom_y = y + height - radius + i;
            // Mirroring the offset logic for the bottom
            int b_offset = radius - (int)sqrt((double)radius * radius - (double)(i + 1) * (i + 1)); 
            draw_line(x + b_offset, bottom_y, x + width - b_offset - 1, bottom_y, on);
        }
    }
}

void LowLevelRenderer::draw_corner_arc(int center_x, int center_y, int radius, int quadrant, bool on)
{
    int x = radius;
    int y = 0;
    int err = 0;

    while (x >= y)
    {
        // Depending on quadrant, set pixels in the appropriate octants
        switch (quadrant)
        {
        case 0: // Bottom-right
            set_pixel(center_x + x, center_y + y, on);
            set_pixel(center_x + y, center_y + x, on);
            break;
        case 1: // Top-right
            set_pixel(center_x + x, center_y - y, on);
            set_pixel(center_x + y, center_y - x, on);
            break;
        case 2: // Top-left
            set_pixel(center_x - x, center_y - y, on);
            set_pixel(center_x - y, center_y - x, on);
            break;
        case 3: // Bottom-left
            set_pixel(center_x - x, center_y + y, on);
            set_pixel(center_x - y, center_y + x, on);
            break;
        }

        if (err <= 0)
        {
            y += 1;
            err += 2 * y + 1;
        }
        if (err > 0)
        {
            x -= 1;
            err -= 2 * x + 1;
        }
    }
}

void LowLevelRenderer::draw_triangle(int x1, int y1, int x2, int y2, int x3, int y3, bool on)
{
    draw_line(x1, y1, x2, y2, on);
    draw_line(x2, y2, x3, y3, on);
    draw_line(x3, y3, x1, y1, on);
}

void LowLevelRenderer::draw_filled_triangle(int x1, int y1, int x2, int y2, int x3, int y3, bool on)
{
    // Sort points by y-coordinate
    if (y1 > y2) { std::swap(x1, x2); std::swap(y1, y2); }
    if (y1 > y3) { std::swap(x1, x3); std::swap(y1, y3); }
    if (y2 > y3) { std::swap(x2, x3); std::swap(y2, y3); }

    // Flat bottom triangle
    if (y2 == y3) {
        fill_bottom_flat_triangle(x1, y1, x2, y2, x3, y3, on);
    }
    // Flat top triangle
    else if (y1 == y2) {
        fill_top_flat_triangle(x1, y1, x2, y2, x3, y3, on);
    }
    // General triangle - split into flat bottom and flat top
    else {
        int x4 = x1 + ((y2 - y1) * (x3 - x1)) / (y3 - y1);
        int y4 = y2;
        fill_bottom_flat_triangle(x1, y1, x2, y2, x4, y4, on);
        fill_top_flat_triangle(x2, y2, x4, y4, x3, y3, on);
    }
}

void LowLevelRenderer::fill_bottom_flat_triangle(int x1, int y1, int x2, int y2, int x3, int y3, bool on)
{
    float invslope1 = (float)(x2 - x1) / (y2 - y1);
    float invslope2 = (float)(x3 - x1) / (y3 - y1);

    float curx1 = x1;
    float curx2 = x1;

    for (int scanlineY = y1; scanlineY <= y2; scanlineY++) {
        draw_line((int)curx1, scanlineY, (int)curx2, scanlineY, on);
        curx1 += invslope1;
        curx2 += invslope2;
    }
}

void LowLevelRenderer::fill_top_flat_triangle(int x1, int y1, int x2, int y2, int x3, int y3, bool on)
{
    float invslope1 = (float)(x3 - x1) / (y3 - y1);
    float invslope2 = (float)(x3 - x2) / (y3 - y2);

    float curx1 = x3;
    float curx2 = x3;

    for (int scanlineY = y3; scanlineY > y1; scanlineY--) {
        draw_line((int)curx1, scanlineY, (int)curx2, scanlineY, on);
        curx1 -= invslope1;
        curx2 -= invslope2;
    }
}

void LowLevelRenderer::draw_ellipse(int center_x, int center_y, int radius_x, int radius_y, bool on)
{
    int x = 0;
    int y = radius_y;
    
    // Decision parameter for region 1
    long long a2 = radius_x * radius_x;
    long long b2 = radius_y * radius_y;
    long long fa2 = 4 * a2, fb2 = 4 * b2;
    long long sigma = 2 * b2 + a2 * (1 - 2 * radius_y);
    
    // Region 1
    while (b2 * x <= a2 * y) {
        set_pixel(center_x + x, center_y + y, on);
        set_pixel(center_x - x, center_y + y, on);
        set_pixel(center_x + x, center_y - y, on);
        set_pixel(center_x - x, center_y - y, on);
        
        if (sigma >= 0) {
            sigma += fa2 * (1 - y);
            y--;
        }
        sigma += b2 * ((4 * x) + 6);
        x++;
    }
    
    // Region 2
    x = radius_x;
    y = 0;
    sigma = 2 * a2 + b2 * (1 - 2 * radius_x);
    
    while (a2 * y <= b2 * x) {
        set_pixel(center_x + x, center_y + y, on);
        set_pixel(center_x - x, center_y + y, on);
        set_pixel(center_x + x, center_y - y, on);
        set_pixel(center_x - x, center_y - y, on);
        
        if (sigma >= 0) {
            sigma += fb2 * (1 - x);
            x--;
        }
        sigma += a2 * ((4 * y) + 6);
        y++;
    }
}

void LowLevelRenderer::draw_filled_ellipse(int center_x, int center_y, int radius_x, int radius_y, bool on)
{
    int hh = radius_y * radius_y;
    int ww = radius_x * radius_x;
    int hhww = hh * ww;
    int x0 = radius_x;
    int dx = 0;

    // Do the horizontal diameter
    draw_line(center_x - radius_x, center_y, center_x + radius_x, center_y, on);

    // Now do both halves at the same time, away from the diameter
    for (int y = 1; y <= radius_y; y++) {
        int x1 = x0 - (dx - 1);  // Try slopes of dx - 1 or more
        for ( ; x1 > 0; x1--) {
            if (x1*x1*hh + y*y*ww <= hhww)
                break;
        }
        dx = x0 - x1;  // Current approximation of the slope
        x0 = x1;

        draw_line(center_x - x0, center_y - y, center_x + x0, center_y - y, on);
        draw_line(center_x - x0, center_y + y, center_x + x0, center_y + y, on);
    }
}

void LowLevelRenderer::draw_polygon(const std::vector<std::pair<int, int>>& points, bool on)
{
    if (points.size() < 3) return;
    
    for (size_t i = 0; i < points.size(); ++i) {
        size_t next = (i + 1) % points.size();
        draw_line(points[i].first, points[i].second, 
                 points[next].first, points[next].second, on);
    }
}

void LowLevelRenderer::draw_filled_polygon(const std::vector<std::pair<int, int>>& points, bool on)
{
    if (points.size() < 3) return;
    
    // Simple triangulation: fan from first vertex
    // This works for convex polygons
    for (size_t i = 1; i < points.size() - 1; ++i) {
        draw_filled_triangle(points[0].first, points[0].second,
                           points[i].first, points[i].second,
                           points[i+1].first, points[i+1].second, on);
    }
}

void LowLevelRenderer::draw_arc(int center_x, int center_y, int radius, int start_angle, int end_angle, bool on)
{
    // Normalize angles to 0-360 range
    start_angle = start_angle % 360;
    end_angle = end_angle % 360;
    if (start_angle < 0) start_angle += 360;
    if (end_angle < 0) end_angle += 360;
    
    // Handle wrap-around
    if (start_angle > end_angle) {
        draw_arc(center_x, center_y, radius, start_angle, 360, on);
        draw_arc(center_x, center_y, radius, 0, end_angle, on);
        return;
    }
    
    int x = radius;
    int y = 0;
    int err = 0;
    
    // Convert angles to radians for comparison
    double start_rad = start_angle * M_PI / 180.0;
    double end_rad = end_angle * M_PI / 180.0;
    
    while (x >= y) {
        // Check each octant point against angle range
        double angles[8] = {
            atan2(y, x),      // 0-45 deg
            atan2(x, y),      // 45-90 deg  
            atan2(x, -y),     // 90-135 deg
            atan2(y, -x),     // 135-180 deg
            atan2(-y, -x),    // 180-225 deg
            atan2(-x, -y),    // 225-270 deg
            atan2(-x, y),     // 270-315 deg
            atan2(-y, x)      // 315-360 deg
        };
        
        int dx[8] = {x, y, -y, -x, -x, -y, y, x};
        int dy[8] = {y, x, x, y, -y, -x, -x, -y};
        
        for (int i = 0; i < 8; ++i) {
            double angle = angles[i];
            if (angle < 0) angle += 2 * M_PI;
            
            if (angle >= start_rad && angle <= end_rad) {
                set_pixel(center_x + dx[i], center_y + dy[i], on);
            }
        }
        
        if (err <= 0) {
            y += 1;
            err += 2 * y + 1;
        }
        if (err > 0) {
            x -= 1;
            err -= 2 * x + 1;
        }
    }
}

void LowLevelRenderer::draw_bitmap(const unsigned char* bitmap, int x, int y, int width, int height, bool invert)
{
    for (int py = 0; py < height; ++py) {
        for (int px = 0; px < width; ++px) {
            int bit_index = py * width + px;
            int byte_index = bit_index / 8;
            int bit_offset = 7 - (bit_index % 8);  // MSB first
            bool pixel_on = (bitmap[byte_index] & (1 << bit_offset)) != 0;
            if (invert) {
                pixel_on = !pixel_on;
            }
            if (pixel_on) {
                set_pixel(x + px, y + py, text_color);
            }
        }
    }
}

void LowLevelRenderer::draw_circle(int x, int y, int radius, bool on)
{
    int x_pos = radius;
    int y_pos = 0;
    int err = 0;

    while (x_pos >= y_pos)
    {
        set_pixel(x + x_pos, y + y_pos, on);
        set_pixel(x + y_pos, y + x_pos, on);
        set_pixel(x - y_pos, y + x_pos, on);
        set_pixel(x - x_pos, y + y_pos, on);
        set_pixel(x - x_pos, y - y_pos, on);
        set_pixel(x - y_pos, y - x_pos, on);
        set_pixel(x + y_pos, y - x_pos, on);
        set_pixel(x + x_pos, y - y_pos, on);

        if (err <= 0)
        {
            y_pos += 1;
            err += 2 * y_pos + 1;
        }
        if (err > 0)
        {
            x_pos -= 1;
            err -= 2 * x_pos + 1;
        }
    }
}

void LowLevelRenderer::draw_filled_circle(int x, int y, int radius, bool on)
{
    int radius_squared = radius * radius;
    for (int dy = -radius; dy <= radius; dy++)
    {
        for (int dx = -radius; dx <= radius; dx++)
        {
            if (dx * dx + dy * dy <= radius_squared)
            {
                set_pixel(x + dx, y + dy, on);
            }
        }
    }
}

int LowLevelRenderer::draw_char_vcol(int x, int y, char c)
{
    if (!current_font) return 0;
    
    // The font table starts at space (ASCII 32)
    if (c < 32 || c > 127)
        return 0;
    int font_idx = c - 32;

    const unsigned char* char_data = current_font->get_char_data(font_idx);
    if (!char_data) return 0;

    int bytes_per_char = current_font->get_bytes_per_char();
    int char_height = current_font->get_char_height();

    // Find the actual width by skipping trailing empty columns
    int actual_width = 0;
    for (int col = bytes_per_char - 1; col >= 0; col--)
    {
        if (char_data[col] != 0 && c != ' ')
        {
            actual_width = col + 1;
            break;
        }
    }

    // Draw only up to the actual width
    for (int col = 0; col < actual_width; col++)
    {
        unsigned char column_byte = char_data[col];

        for (int row = 0; row < char_height; row++)
        {
            // Check if the bit for this row is set
            if (column_byte & (1 << row))
            {
                set_pixel(x + col, y + row, text_color);
            }
        }
    }

    return actual_width;
}

void LowLevelRenderer::draw_string(int x, int y, const std::string &text, int spacing)
{
    if (!current_font) return;
    int current_x = x;
    for (size_t i = 0; i < text.length(); i++)
    {
        int char_width = draw_char_vcol(current_x, y, text[i]);
        current_x += char_width + spacing;
    }
}

int LowLevelRenderer::draw_char_scaled(int x, int y, char c, int scale)
{
    if (!current_font) return 0;
    
    if (c < 32 || c > 127)
        return 0;
    if (scale < 1)
        scale = 1; // Safety check

    int font_idx = c - 32;
    const unsigned char* char_data = current_font->get_char_data(font_idx);
    if (!char_data) return 0;

    int bytes_per_char = current_font->get_bytes_per_char();
    int char_height = current_font->get_char_height();

    // Find the actual width by skipping trailing empty columns
    int actual_width = 0;
    for (int col = bytes_per_char - 1; col >= 0; col--)
    {
        if (char_data[col] != 0)
        {
            actual_width = col + 1;
            break;
        }
    }

    // Draw only up to the actual width, scaled
    for (int col = 0; col < actual_width; col++)
    {
        unsigned char column_byte = char_data[col];

        for (int row = 0; row < char_height; row++)
        {
            if (column_byte & (1 << row))
            {
                // Draw a square of size [scale x scale]
                for (int sy = 0; sy < scale; sy++)
                {
                    for (int sx = 0; sx < scale; sx++)
                    {
                        set_pixel(x + (col * scale) + sx,
                                  y + (row * scale) + sy,
                                  text_color);
                    }
                }
            }
        }
    }

    return actual_width * scale;
}

int LowLevelRenderer::draw_string_scaled(int x, int y, const char* text, int scale, int spacing)
{
    if (!current_font) return 0;
    int current_x = x;
    int i = 0;
    while(text[i] != '\0')
    {
        int char_width = draw_char_scaled(current_x, y, text[i], scale);
        current_x += char_width + (spacing * scale);
        i++;
    }
    return current_x;
}
