#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <png.h>

#define OUT_FILE "initials.png"
#define WIDTH 600
#define HEIGHT 600
#define COLOR_TYPE PNG_COLOR_TYPE_RGB
#define BIT_DEPTH 8

png_bytep *row_pointers;

typedef struct {
    png_byte r, g, b;
} Color;

void abort_(const char * s, ...) {
    va_list args;
    va_start(args, s);
    vfprintf(stderr, s, args);
    fprintf(stderr, "\n");
    va_end(args);
    abort();
}

void create_png_file() {
    row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * HEIGHT);
    for (int y = 0; y < HEIGHT; y++) {
        row_pointers[y] = (png_byte*) malloc(3 * WIDTH);
    }
}

void set_pixel(int x, int y, png_byte r, png_byte g, png_byte b) {
    if (x >= 0 && x < WIDTH && y >= 0 && y < HEIGHT) {
        png_bytep row = row_pointers[y];
        png_bytep ptr = &(row[x * 3]);
        ptr[0] = r; ptr[1] = g; ptr[2] = b;
    }
}

Color get_pixel(int x, int y) {
    Color c = {0, 0, 0};
    if (x >= 0 && x < WIDTH && y >= 0 && y < HEIGHT) {
        png_bytep row = row_pointers[y];
        png_bytep ptr = &(row[x * 3]);
        c.r = ptr[0]; c.g = ptr[1]; c.b = ptr[2];
    }
    return c;
}

// 1. Algorytm Bresenhama dla odcinka
void draw_line(int x0, int y0, int x1, int y1, Color c) {
    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;
    for (;;) {
        set_pixel(x0, y0, c.r, c.g, c.b);
        if (x0 == x1 && y0 == y1) break;
        e2 = 2 * err;
        if (e2 >= dy) { err += dy; x0 += sx; }
        if (e2 <= dx) { err += dx; y0 += sy; }
    }
}

// 2. Algorytm rasteryzacji okręgu
void draw_circle(int xm, int ym, int r, Color c) {
    int x = -r, y = 0, err = 2 - 2 * r;
    do {
        set_pixel(xm - x, ym + y, c.r, c.g, c.b);
        set_pixel(xm - y, ym - x, c.r, c.g, c.b);
        set_pixel(xm + x, ym - y, c.r, c.g, c.b);
        set_pixel(xm + y, ym + x, c.r, c.g, c.b);
        r = err;
        if (r <= y) err += ++y * 2 + 1;
        if (r > x || err > y) err += ++x * 2 + 1;
    } while (x < 0);
}

// 3. Algorytm wypełnienia 4-spójnego
void flood_fill(int x, int y, Color target, Color fill) {
    if (x < 0 || x >= WIDTH || y < 0 || y >= HEIGHT) return;
    Color current = get_pixel(x, y);
    if (current.r == target.r && current.g == target.g && current.b == target.b) {
        if (current.r == fill.r && current.g == fill.g && current.b == fill.b) return;
        set_pixel(x, y, fill.r, fill.g, fill.b);
        flood_fill(x + 1, y, target, fill);
        flood_fill(x - 1, y, target, fill);
        flood_fill(x, y + 1, target, fill);
        flood_fill(x, y - 1, target, fill);
    }
}

void process_file(void) {
    Color black = {0, 0, 0};
    Color green = {0, 255, 0};
    Color magenta = {255, 0, 255};
    Color blue = {0, 255, 255};
    Color yellow = {255, 255, 0};

    // Tło
    for(int y=0; y<HEIGHT; y++) 
        for(int x=0; x<WIDTH; x++) 
            set_pixel(x, y, magenta.r, magenta.g, magenta.b);

    // Zielone koło
    int cx = WIDTH/2, cy = HEIGHT/2, r = 250;
    for (int y = 0; y < HEIGHT; y++) {
        for (int x = 0; x < WIDTH; x++) {
            if ((x-cx)*(x-cx) + (y-cy)*(y-cy) <= r*r) 
                set_pixel(x, y, green.r, green.g, green.b);
        }
    }

    // Wypełnienie liter przed obrysem
    // Ł
    for(int y=200; y<=360; y++) for(int x=150; x<=170; x++) set_pixel(x, y, blue.r, blue.g, blue.b);
    for(int y=340; y<=360; y++) for(int x=170; x<=230; x++) set_pixel(x, y, blue.r, blue.g, blue.b);
    for(int i=-5; i<=5; i++) draw_line(140, 280+i, 205, 255+i, blue);
    // P
    for(int y=200; y<=360; y++) for(int x=320; x<=340; x++) set_pixel(x, y, blue.r, blue.g, blue.b);
    for(int y=200; y<=220; y++) for(int x=340; x<=400; x++) set_pixel(x, y, blue.r, blue.g, blue.b);
    for(int y=280; y<=300; y++) for(int x=340; x<=400; x++) set_pixel(x, y, blue.r, blue.g, blue.b);
    for(int y=200; y<=300; y++) for(int x=380; x<=400; x++) set_pixel(x, y, blue.r, blue.g, blue.b);
    // Żółte kółko w P
    for(int y=221; y<=279; y++) for(int x=341; x<=379; x++) set_pixel(x, y, yellow.r, yellow.g, yellow.b);

    // Obrysy (Bresenham)
    draw_line(150, 200, 170, 200, black); draw_line(170, 200, 170, 340, black);
    draw_line(170, 340, 230, 340, black); draw_line(230, 340, 230, 360, black);
    draw_line(230, 360, 150, 360, black); draw_line(150, 360, 150, 200, black);
    draw_line(140, 275, 205, 250, black); draw_line(205, 250, 210, 260, black);
    draw_line(210, 260, 145, 285, black); draw_line(145, 285, 140, 275, black);
    draw_line(320, 200, 400, 200, black); draw_line(400, 200, 400, 300, black);
    draw_line(400, 300, 340, 300, black); draw_line(340, 300, 340, 360, black);
    draw_line(340, 360, 320, 360, black); draw_line(320, 360, 320, 200, black);
    draw_line(340, 220, 380, 220, black); draw_line(380, 220, 380, 280, black);
    draw_line(380, 280, 340, 280, black); draw_line(340, 280, 340, 220, black);

    draw_circle(cx, cy, r, black);
}

void write_png_file(char* file_name) {
    FILE *fp = fopen(file_name, "wb");
    png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    png_infop info_ptr = png_create_info_struct(png_ptr);
    png_init_io(png_ptr, fp);
    png_set_IHDR(png_ptr, info_ptr, WIDTH, HEIGHT, BIT_DEPTH, COLOR_TYPE, 
                 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
    png_write_info(png_ptr, info_ptr);
    png_write_image(png_ptr, row_pointers);
    png_write_end(png_ptr, NULL);
    for (int y = 0; y < HEIGHT; y++) free(row_pointers[y]);
    free(row_pointers);
    fclose(fp);
}

int main(int argc, char **argv) {
    create_png_file();
    process_file();
    write_png_file(OUT_FILE);
    return 0;
}