#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <shell.h>
#include <unistd.h>

#define array_size(a) (sizeof(a) / sizeof((a)[0]))

_Noreturn static void
die(const char *m)
{
    fprintf(stderr, "%s\n", m);
    exit(1);
}

static const char *norstrulde[] = {
    "                                              ",
    "                                              ",
    "                 ##  #          #    #  ##    ",
    "    ### ### ### ### ### ### # # #  ### ###    ",
    "    # # # # #     #  #  #   # # #  # # #      ",
    "    # # ### #   ##   ## #   ### ## ###  ##    ",
    "                                              ",
};

#define BYTES_PER_PIXEL 4

static uint32_t
encode_color_xrgb(uint8_t r, uint8_t g, uint8_t b)
{
    union {
        uint32_t u;
        uint8_t bytes[4];
    } x = {
        .bytes = { b, g, r, 0 },
    };
    return x.u;
}

static void
draw_background(void *mem, uint32_t width, uint32_t height)
{
    uint32_t color = encode_color_xrgb(0xff, 0xff, 0xff);

    uint32_t *pixel = mem;
    for (uint32_t y = 0; y < height; y++)
        for (uint32_t x = 0; x < width; x++)
            *pixel++ = color;
}

static void
draw_norstrulde(void *mem, uint32_t width, uint32_t x, uint32_t y)
{
    uint32_t *pixels = mem;
    uint32_t gray = encode_color_xrgb(0x48, 0x48, 0x48);
    uint32_t white = encode_color_xrgb(0xff, 0xff, 0xff);

    for (int i = 0; i < array_size(norstrulde); i++)
        for (int j = 0; j < strlen(norstrulde[i]); j++)
            for (int m = 0; m < 16; m++)
                for (int n = 0; n < 16; n++)
                    pixels[(120 + y + 16 * i + m) * width + (x + 16 * j + n)] =
                        (norstrulde[i][j] == ' ') ? gray : white;
}

static void
receive_welcome(int fd, struct shell_message_welcome *message)
{
    ssize_t r = read(fd, message, sizeof(*message));
    if (r != sizeof(*message))
        die("Failed to read welcome message.");

    if (message->class != SHELL_MESSAGE_WELCOME)
        die("Failed to receive welcome.");
}

static void
send_show(int fd)
{
    struct shell_message_show message = {
        .class = SHELL_MESSAGE_SHOW,
    };

    ssize_t r = write(fd, &message, sizeof(message));
    if (r != sizeof(message))
        die("Failed to write show message.");
}

int
main(void)
{
    uint32_t width, height;
    {
        struct shell_message_welcome message;
        receive_welcome(3, &message);
        width = message.width;
        height = message.height;
    }

    size_t mem_size = 8 * width * height;
    void *mem = mmap(NULL, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, 4, 0);
    if (mem == MAP_FAILED)
        die("Failed to create draw buffers for child process.");
    close(4);

    uint32_t x = 160;
    uint32_t y = height / 2 - 160;

    void *buffer = mem + 4 * width * height;

    draw_background(buffer, width, height);
    draw_norstrulde(buffer, width, x, y);

    send_show(3);

    sleep(5);

    return 0;
}