#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <math.h>

#define PI 3.14159265358979323846
#define W 800
#define H 800
#define BYTES_PER_PIXEL 3

static unsigned char img[H][W][BYTES_PER_PIXEL];

typedef struct { int x, y; } Point;
typedef struct { unsigned char r, g, b; } Color;

static void set_px(int x, int y, Color c) {
    if (x >= 0 && x < W && y >= 0 && y < H) {
        img[y][x][0] = c.r;
        img[y][x][1] = c.g;
        img[y][x][2] = c.b;
    }
}

static void clear(Color c) {
    for (int y = 0; y < H; y++)
        for (int x = 0; x < W; x++)
            set_px(x, y, c);
}

static void 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;

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

static void thick_line(int x0, int y0, int x1, int y1, int t, Color c) {
    for (int oy = -t; oy <= t; oy++)
        for (int ox = -t; ox <= t; ox++)
            if (ox * ox + oy * oy <= t * t)
                line(x0 + ox, y0 + oy, x1 + ox, y1 + oy, c);
}

static void polygon_outline(Point *p, int n, int t, Color c) {
    for (int i = 0; i < n; i++) {
        Point a = p[i];
        Point b = p[(i + 1) % n];
        thick_line(a.x, a.y, b.x, b.y, t, c);
    }
}

static int point_in_polygon(int x, int y, Point *p, int n) {
    int inside = 0;
    for (int i = 0, j = n - 1; i < n; j = i++) {
        if (((p[i].y > y) != (p[j].y > y)) &&
            (x < (p[j].x - p[i].x) * (y - p[i].y) / (double)(p[j].y - p[i].y) + p[i].x)) {
            inside = !inside;
        }
    }
    return inside;
}

static void fill_polygon(Point *p, int n, Color c) {
    int minx = W - 1, miny = H - 1, maxx = 0, maxy = 0;

    for (int i = 0; i < n; i++) {
        if (p[i].x < minx) minx = p[i].x;
        if (p[i].x > maxx) maxx = p[i].x;
        if (p[i].y < miny) miny = p[i].y;
        if (p[i].y > maxy) maxy = p[i].y;
    }

    if (minx < 0) minx = 0;
    if (maxx >= W) maxx = W - 1;
    if (miny < 0) miny = 0;
    if (maxy >= H) maxy = H - 1;

    for (int y = miny; y <= maxy; y++)
        for (int x = minx; x <= maxx; x++)
            if (point_in_polygon(x, y, p, n))
                set_px(x, y, c);
}

static void draw_poly(Point *p, int n, Color fill, Color outline) {
    fill_polygon(p, n, fill);
    polygon_outline(p, n, 2, outline);
}

static void ellipse_outline(int cx, int cy, int rx, int ry, Color c) {
    for (int a = 0; a < 3600; a++) {
        double t = a * PI / 1800.0;
        int x = cx + (int)round(rx * cos(t));
        int y = cy + (int)round(ry * sin(t));
        set_px(x, y, c);
        set_px(x + 1, y, c);
        set_px(x - 1, y, c);
        set_px(x, y + 1, c);
        set_px(x, y - 1, c);
    }
}

static void fill_ellipse(int cx, int cy, int rx, int ry, Color c) {
    for (int y = cy - ry; y <= cy + ry; y++) {
        for (int x = cx - rx; x <= cx + rx; x++) {
            double v = ((x - cx) * (x - cx)) / (double)(rx * rx) +
                       ((y - cy) * (y - cy)) / (double)(ry * ry);
            if (v <= 1.0) set_px(x, y, c);
        }
    }
}

static uint32_t crc_table[256];

static void make_crc_table(void) {
    for (uint32_t n = 0; n < 256; n++) {
        uint32_t c = n;
        for (int k = 0; k < 8; k++)
            c = c & 1 ? 0xedb88320L ^ (c >> 1) : c >> 1;
        crc_table[n] = c;
    }
}

static uint32_t update_crc(uint32_t crc, const unsigned char *buf, size_t len) {
    uint32_t c = crc;
    for (size_t n = 0; n < len; n++)
        c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8);
    return c;
}

static uint32_t crc(const unsigned char *type, const unsigned char *data, size_t len) {
    uint32_t c = update_crc(0xffffffffL, type, 4);
    c = update_crc(c, data, len);
    return c ^ 0xffffffffL;
}

static void put32(FILE *f, uint32_t v) {
    fputc((v >> 24) & 255, f);
    fputc((v >> 16) & 255, f);
    fputc((v >> 8) & 255, f);
    fputc(v & 255, f);
}

static void chunk(FILE *f, const char type[4], const unsigned char *data, uint32_t len) {
    put32(f, len);
    fwrite(type, 1, 4, f);
    if (len) fwrite(data, 1, len, f);
    put32(f, crc((const unsigned char*)type, data, len));
}

static uint32_t adler32(const unsigned char *data, size_t len) {
    uint32_t a = 1, b = 0;
    for (size_t i = 0; i < len; i++) {
        a = (a + data[i]) % 65521;
        b = (b + a) % 65521;
    }
    return (b << 16) | a;
}

static void write_png(const char *filename) {
    make_crc_table();

    size_t raw_len = H * (1 + W * 3);
    unsigned char *raw = malloc(raw_len);
    size_t k = 0;

    for (int y = 0; y < H; y++) {
        raw[k++] = 0;
        for (int x = 0; x < W; x++) {
            raw[k++] = img[y][x][0];
            raw[k++] = img[y][x][1];
            raw[k++] = img[y][x][2];
        }
    }

    size_t blocks = (raw_len + 65534) / 65535;
    size_t zlen = 2 + raw_len + blocks * 5 + 4;
    unsigned char *z = malloc(zlen);

    k = 0;
    z[k++] = 0x78;
    z[k++] = 0x01;

    size_t pos = 0;
    while (pos < raw_len) {
        uint16_t len = (raw_len - pos > 65535) ? 65535 : (uint16_t)(raw_len - pos);
        int final = (pos + len == raw_len);

        z[k++] = final ? 1 : 0;
        z[k++] = len & 255;
        z[k++] = (len >> 8) & 255;

        uint16_t nlen = ~len;
        z[k++] = nlen & 255;
        z[k++] = (nlen >> 8) & 255;

        memcpy(z + k, raw + pos, len);
        k += len;
        pos += len;
    }

    uint32_t ad = adler32(raw, raw_len);
    z[k++] = (ad >> 24) & 255;
    z[k++] = (ad >> 16) & 255;
    z[k++] = (ad >> 8) & 255;
    z[k++] = ad & 255;

    FILE *f = fopen(filename, "wb");
    if (!f) {
        printf("Nie mozna utworzyc pliku PNG.\n");
        free(raw);
        free(z);
        return;
    }

    unsigned char sig[8] = {137, 80, 78, 71, 13, 10, 26, 10};
    fwrite(sig, 1, 8, f);

    unsigned char ihdr[13];
    ihdr[0] = W >> 24; ihdr[1] = W >> 16; ihdr[2] = W >> 8; ihdr[3] = W;
    ihdr[4] = H >> 24; ihdr[5] = H >> 16; ihdr[6] = H >> 8; ihdr[7] = H;
    ihdr[8] = 8;
    ihdr[9] = 2;
    ihdr[10] = 0;
    ihdr[11] = 0;
    ihdr[12] = 0;

    chunk(f, "IHDR", ihdr, 13);
    chunk(f, "IDAT", z, (uint32_t)k);
    chunk(f, "IEND", NULL, 0);

    fclose(f);
    free(raw);
    free(z);
}

static void draw_initials_DB(void) {
    Color background = {255, 0, 230};
    Color circle = {0, 240, 0};
    Color letter = {0, 180, 255};
    Color outline = {0, 130, 150};

    clear(background);
    fill_ellipse(400, 400, 310, 310, circle);

    Point d[] = {{210,190},{350,190},{455,245},{505,365},{505,435},{455,555},{350,610},{210,610}};
    Point d_hole[] = {{285,285},{345,285},{410,330},{435,400},{410,470},{345,515},{285,515}};

    draw_poly(d, 8, letter, outline);
    draw_poly(d_hole, 7, circle, outline);

    Point b_stem[] = {{500,190},{580,190},{580,610},{500,610}};
    Point b_top[] = {{580,190},{650,205},{690,255},{690,330},{650,380},{580,390}};
    Point b_bot[] = {{580,390},{660,405},{700,465},{700,540},{660,595},{580,610}};
    Point b_hole1[] = {{580,270},{625,280},{650,310},{625,340},{580,345}};
    Point b_hole2[] = {{580,465},{635,475},{665,515},{635,555},{580,560}};

    draw_poly(b_stem, 4, letter, outline);
    draw_poly(b_top, 6, letter, outline);
    draw_poly(b_bot, 6, letter, outline);
    draw_poly(b_hole1, 5, circle, outline);
    draw_poly(b_hole2, 5, circle, outline);

    ellipse_outline(400, 400, 310, 310, outline);
}

int main(void) {
    draw_initials_DB();
    write_png("initial_db.png");
    printf("Zapisano plik initial_db.png\n");
    return 0;
}
