#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <math.h>

#define PNG_DEBUG 3
#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

/* --- Globalne zmienne struktury PNG --- */
int width, height;
png_byte color_type;
png_byte bit_depth;
png_structp png_ptr;
png_infop info_ptr;
png_bytep * row_pointers;

/* --- Funkcje pomocnicze i algorytmy graficzne --- */

// Funkcja stawiająca pojedynczy piksel w pamięci
void put_pixel(int x, int y, png_byte r, png_byte g, png_byte b) {
    if (x >= 0 && x < WIDTH && y >= 0 && y < HEIGHT) {
        png_byte* row = row_pointers[y];
        png_byte* ptr = &(row[x * 3]);
        ptr[0] = r;
        ptr[1] = g;
        ptr[2] = b;
    }
}

// Pobieranie koloru piksela (potrzebne do flood_fill)
void get_pixel(int x, int y, png_byte color[3]) {
    if (x >= 0 && x < WIDTH && y >= 0 && y < HEIGHT) {
        png_byte* ptr = &(row_pointers[y][x * 3]);
        color[0] = ptr[0];
        color[1] = ptr[1];
        color[2] = ptr[2];
    }
}

// Algorytm Bresenhama dla odcinka (inicjały)
void draw_line(int x0, int y0, int x1, int y1, png_byte r, png_byte g, png_byte b) {
    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 (1) {
        put_pixel(x0, y0, r, g, b);
        if (x0 == x1 && y0 == y1) break;
        e2 = 2 * err;
        if (e2 >= dy) { err += dy; x0 += sx; }
        if (e2 <= dx) { err += dx; y0 += sy; }
    }
}

// Algorytm punktu środkowego dla okręgu
void draw_circle(int xm, int ym, int r, png_byte red, png_byte green, png_byte blue) {
    int x = -r, y = 0, err = 2 - 2 * r;
    do {
        put_pixel(xm - x, ym + y, red, green, blue);
        put_pixel(xm - y, ym - x, red, green, blue);
        put_pixel(xm + x, ym - y, red, green, blue);
        put_pixel(xm + y, ym + x, red, green, blue);
        r = err;
        if (r <= y) err += ++y * 2 + 1;
        if (r > x || err > y) err += ++x * 2 + 1;
    } while (x < 0);
}

// Algorytm wypełniania 4-spójnego (flood fill)
// Iteracyjny Flood Fill (4-spójny) - bezpieczny na duże obszary
void flood_fill(int start_x, int start_y, png_byte target[3], png_byte fill[3]) {
    if (start_x < 0 || start_x >= WIDTH || start_y < 0 || start_y >= HEIGHT) return;

    png_byte current[3];
    get_pixel(start_x, start_y, current);

    if (current[0] != target[0] || current[1] != target[1] || current[2] != target[2]) 
        return;

    int stack[50000][2];   // mniejszy stos
    int top = 0;

    stack[top][0] = start_x;
    stack[top][1] = start_y;
    top++;

    while (top > 0 && top < 49990) {   // ochrona przed przepełnieniem
        top--;
        int x = stack[top][0];
        int y = stack[top][1];

        if (x < 0 || x >= WIDTH || y < 0 || y >= HEIGHT) continue;

        get_pixel(x, y, current);
        if (current[0] != target[0] || current[1] != target[1] || current[2] != target[2]) 
            continue;

        put_pixel(x, y, fill[0], fill[1], fill[2]);

        // 4 kierunki
        if (top < 49980) {
            stack[top][0] = x+1; stack[top][1] = y; top++;
            stack[top][0] = x-1; stack[top][1] = y; top++;
            stack[top][0] = x;   stack[top][1] = y+1; top++;
            stack[top][0] = x;   stack[top][1] = y-1; top++;
        }
    }
}
/* --- Główna logika rysowania --- */

void process_file(void) {
    // 1. Czyszczenie tła na czarno (0, 0, 0)
    for (int py = 0; py < HEIGHT; py++) {
        for (int px = 0; px < WIDTH; px++) {
            put_pixel(px, py, 0, 0, 0);
        }
    }

    png_byte white[3] = {255, 255, 255};
    png_byte red[3] = {255, 0, 0};
    png_byte green[3] = {0, 255, 0};
    png_byte yellow[3] = {255, 255, 0};
    png_byte blue[3] = {0, 0, 150};
    png_byte black[3] = {0, 0, 0};

    // 2. Rysowanie inicjału 'J' jako wieloboku
    // Górna belka
    draw_line(100, 150, 250, 150, white[0], white[1], white[2]);
    draw_line(250, 150, 250, 190, white[0], white[1], white[2]);
    draw_line(250, 190, 210, 190, white[0], white[1], white[2]);
    // Pionowa kreska
    draw_line(210, 190, 210, 350, white[0], white[1], white[2]);
    // Zakręt (uproszczony wielobok)
    draw_line(210, 350, 180, 390, white[0], white[1], white[2]);
    draw_line(180, 390, 120, 390, white[0], white[1], white[2]);
    draw_line(120, 390, 90, 360, white[0], white[1], white[2]);
    draw_line(90, 360, 90, 320, white[0], white[1], white[2]);
    draw_line(90, 320, 130, 320, white[0], white[1], white[2]);
    draw_line(130, 320, 130, 350, white[0], white[1], white[2]);
    draw_line(130, 350, 175, 350, white[0], white[1], white[2]);
    // Powrót do góry
    draw_line(175, 350, 175, 190, white[0], white[1], white[2]);
    draw_line(175, 190, 100, 190, white[0], white[1], white[2]);
    draw_line(100, 190, 100, 150, white[0], white[1], white[2]);

    // 3. Rysowanie inicjału 'N' jako wieloboku
    draw_line(350, 400, 350, 150, white[0], white[1], white[2]); // Lewa krawędź
    draw_line(350, 150, 390, 150, white[0], white[1], white[2]); // Góra-lewo
    draw_line(390, 150, 470, 330, white[0], white[1], white[2]); // Skos w dół
    draw_line(470, 330, 470, 150, white[0], white[1], white[2]); // Góra-prawa krawędź
    draw_line(470, 150, 510, 150, white[0], white[1], white[2]); // Góra-prawo
    draw_line(510, 150, 510, 400, white[0], white[1], white[2]); // Prawa krawędź
    draw_line(510, 400, 470, 400, white[0], white[1], white[2]); // Dół-prawo
    draw_line(470, 400, 390, 220, white[0], white[1], white[2]); // Skos w górę
    draw_line(390, 220, 390, 400, white[0], white[1], white[2]); // Dół-lewa krawędź
    draw_line(390, 400, 350, 400, white[0], white[1], white[2]); // Zamknięcie N

    // 4. Dodanie czerwonego okręgu dookoła inicjałów
    draw_circle(300, 300, 285, red[0], red[1], red[2]);
    // Wypełnianie liter (zielone)
    flood_fill(180, 200, black, green);   // J
    flood_fill(420, 250, black, green);   // N - dostosuj punkt jeśli trzeba

    // Wypełnianie tła wewnątrz okręgu, ale na zewnątrz liter (niebieskie)
    flood_fill(300, 100, black, blue);    // tło wewnątrz okręgu
}

/* --- Standardowa obsługa pliku PNG (kod biblioteki) --- */

void abort_(const char * s, ...) {
    va_list args;
    va_start(args, s);
    vfprintf(stderr, "[BŁĄD] ", NULL);
    vfprintf(stderr, s, args);
    fprintf(stderr, "\n");
    va_end(args);
    // abort();   // zakomentowane
    exit(1);
}

void create_png_file() {
    width = WIDTH;
    height = HEIGHT;
    bit_depth = BIT_DEPTH;
    color_type = COLOR_TYPE;

    row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * height);
    for (int y=0; y<height; y++)
        row_pointers[y] = (png_byte*) malloc(width*bit_depth*3);
}

void write_png_file(char* file_name) {
    FILE *fp = fopen(file_name, "wb");
    if (!fp) abort_("[write_png_file] File %s could not be opened", file_name);

    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    if (!png_ptr) abort_("[write_png_file] png_create_write_struct failed");

    info_ptr = png_create_info_struct(png_ptr);
    if (!info_ptr) abort_("[write_png_file] png_create_info_struct failed");

    if (setjmp(png_jmpbuf(png_ptr))) abort_("[write_png_file] Error during init_io");
    png_init_io(png_ptr, fp);

    if (setjmp(png_jmpbuf(png_ptr))) abort_("[write_png_file] Error writing header");
    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);
    if (setjmp(png_jmpbuf(png_ptr))) abort_("[write_png_file] Error writing bytes");
    png_write_image(png_ptr, row_pointers);

    if (setjmp(png_jmpbuf(png_ptr))) abort_("[write_png_file] Error end of write");
    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) {
    printf("1. Tworzenie struktury PNG...\n");
    create_png_file();
    
    printf("2. Rysowanie grafiki...\n");
    process_file();
    
    printf("3. Zapisywanie pliku %s...\n", OUT_FILE);
    write_png_file(OUT_FILE);
    
    printf("Gotowe! Plik powinien być zapisany.\n");
    return 0;
}
