#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

/* ------------------------------------------------------------
 * Basic Types
 * ------------------------------------------------------------ */

typedef struct {
    uint8_t r, g, b, a;   /* 0‑255 per channel */
} Color;

typedef struct {
    uint32_t width;
    uint32_t height;
    Color *pixels;        /* Row‑major order: pixels[y * width + x] */
} Framebuffer;

typedef struct {
    int x;
    int y;
} Point;

/* ------------------------------------------------------------
 * Framebuffer Management
 * ------------------------------------------------------------ */
static inline Framebuffer *fb_create(uint32_t width, uint32_t height, Color clear_color)
{
    Framebuffer *fb = (Framebuffer *)malloc(sizeof(Framebuffer));
    if (!fb) return NULL;
    fb->width  = width;
    fb->height = height;
    fb->pixels = (Color *)malloc(width * height * sizeof(Color));
    if (!fb->pixels) {
        free(fb);
        return NULL;
    }
    /* Initialise with clear colour */
    for (uint32_t i = 0; i < width * height; ++i) {
        fb->pixels[i] = clear_color;
    }
    return fb;
}

static inline void fb_destroy(Framebuffer *fb)
{
    if (fb) {
        free(fb->pixels);
        free(fb);
    }
}

static inline void fb_clear(Framebuffer *fb, Color color)
{
    if (!fb) return;
    for (uint32_t i = 0; i < fb->width * fb->height; ++i) {
        fb->pixels[i] = color;
    }
}

/* ------------------------------------------------------------
 * Pixel Operations
 * ------------------------------------------------------------ */
static inline bool fb_set_pixel(Framebuffer *fb, int x, int y, Color color)
{
    if (!fb) return false;
    if (x < 0 || y < 0 || (uint32_t)x >= fb->width || (uint32_t)y >= fb->height)
        return false;               /* Out of bounds – silently ignore */
    fb->pixels[y * fb->width + x] = color;
    return true;
}

/* ------------------------------------------------------------
 * Line Drawing – Bresenham (integer only, no anti‑aliasing)
 * ------------------------------------------------------------ */
static void draw_line(Framebuffer *fb, Point p0, Point p1, Color color)
{
    int x0 = p0.x, y0 = p0.y;
    int x1 = p1.x, y1 = p1.y;
    int dx = abs(x1 - x0);
    int dy = -abs(y1 - y0);
    int sx = (x0 < x1) ? 1 : -1;
    int sy = (y0 < y1) ? 1 : -1;
    int err = dx + dy;   /* error value e_xy */

    while (true) {
        fb_set_pixel(fb, x0, y0, color);
        if (x0 == x1 && y0 == y1) break;
        int e2 = 2 * err;
        if (e2 >= dy) { err += dy; x0 += sx; }
        if (e2 <= dx) { err += dx; y0 += sy; }
    }
}

/* ------------------------------------------------------------
 * Polygon Stroking – draws edges using draw_line
 * ------------------------------------------------------------ */
static void draw_polygon(Framebuffer *fb, const Point *pts, size_t count, Color color)
{
    if (count < 2) return;
    for (size_t i = 0; i < count; ++i) {
        Point a = pts[i];
        Point b = pts[(i + 1) % count];
        draw_line(fb, a, b, color);
    }
}

/* ------------------------------------------------------------
 * Polygon Fill – simple even‑odd scan‑line algorithm
 * ------------------------------------------------------------ */
static void fill_polygon(Framebuffer *fb, const Point *pts, size_t count, Color color)
{
    if (count < 3) return;   /* Not a polygon */

    /* Find vertical bounds */
    int min_y = pts[0].y, max_y = pts[0].y;
    for (size_t i = 1; i < count; ++i) {
        if (pts[i].y < min_y) min_y = pts[i].y;
        if (pts[i].y > max_y) max_y = pts[i].y;
    }

    /* Clamp to framebuffer */
    if (min_y < 0) min_y = 0;
    if ((uint32_t)max_y >= fb->height) max_y = fb->height - 1;

    /* For each scan line, compute intersections with polygon edges */
    for (int y = min_y; y <= max_y; ++y) {
        /* Collect x intersections */
        int intersections[64];   /* Reasonable static limit; can be expanded */
        size_t n = 0;
        for (size_t i = 0; i < count; ++i) {
            Point v1 = pts[i];
            Point v2 = pts[(i + 1) % count];
            if (v1.y == v2.y) continue;               /* Horizontal edge – ignore */
            if ((y < v1.y && y < v2.y) || (y > v1.y && y > v2.y))
                continue;                             /* Edge not crossing scan line */
            /* Compute intersection X coordinate */
            double x = v1.x + (double)(y - v1.y) * (v2.x - v1.x) / (v2.y - v1.y);
            if (n < sizeof(intersections)/sizeof(intersections[0]))
                intersections[n++] = (int) (x + 0.5); /* Round to nearest pixel */
        }
        if (n < 2) continue;   /* No spans on this line */
        /* Sort intersections (simple insertion sort – n is tiny) */
        for (size_t i = 1; i < n; ++i) {
            int key = intersections[i];
            size_t j = i;
            while (j > 0 && intersections[j-1] > key) {
                intersections[j] = intersections[j-1];
                --j;
            }
            intersections[j] = key;
        }
        /* Fill between pairs */
        for (size_t i = 0; i + 1 < n; i += 2) {
            int x_start = intersections[i];
            int x_end   = intersections[i+1];
            if (x_start > x_end) continue;
            if (x_start < 0) x_start = 0;
            if ((uint32_t)x_end >= fb->width) x_end = fb->width - 1;
            for (int x = x_start; x <= x_end; ++x) {
                fb_set_pixel(fb, x, y, color);
            }
        }
    }
}

/* ------------------------------------------------------------
 * Example usage (commented out – keep library header‑only)
 * ------------------------------------------------------------ */
/*
int main(void)
{
    Color black = {0,0,0,255};
    Color red   = {255,0,0,255};
    Framebuffer *fb = fb_create(640, 480, black);
    if (!fb) return 1;

    Point poly[] = {{100,100},{200,80},{250,200},{150,250}};
    draw_polygon(fb, poly, 4, red);
    fill_polygon(fb, poly, 4, red);

    /* At this point you could write fb->pixels to a BMP/PNG file or
       blit it to a window using your platform‑specific code. */

    fb_destroy(fb);
    return 0;
}
*/