#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#include <ft2build.h>
#include FT_FREETYPE_H

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

#define FONT "FiraMonoOT-Regular.otf"

static FT_Library freetype;
static FT_Face font_face;

static _Noreturn void
die(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    fprintf(stderr, "Error: ");
    vfprintf(stderr, fmt, args);
    va_end(args);
    fputc('\n', stderr);
    exit(1);
}

enum {
    MODE_C,
    MODE_84,
};

static uint16_t interval_starts[128];
static uint8_t interval_sizes[128];
static int num_intervals;

static uint16_t charcodes[1024];
static uint32_t bitmap_offsets[1024];
static int num_glyphs;

static uint8_t bitmap_bytes[1024 * 1024];
static int num_bitmap_bytes;

void
write_c(void)
{
    int fds[2];
    pipe(fds);

    if (fork() == 0) {
        close(fds[1]);
        dup2(fds[0], 0);
        close(fds[0]);
        execlp("fmt", "fmt", "--prefix=    ", (char *)NULL);
    }
    close(fds[0]);
    dup2(fds[1], 1);
    close(fds[1]);

    printf("static const uint16_t interval_starts[%d] = {\n\n    ", num_intervals);
    for (int i = 0; i < num_intervals; i++) {
        printf("%u, ", (unsigned)interval_starts[i]);
    }
    printf("\n\n};\n\n");

    printf("static const uint8_t interval_sizes[%d] = {\n\n    ", num_intervals);
    for (int i = 0; i < num_intervals; i++) {
        printf("%u, ", (unsigned)interval_sizes[i]);
    }
    printf("\n\n};\n\n");

    printf("static const uint16_t bitmap_offsets[%d] = {\n\n    ", num_glyphs);
    for (int i = 0; i < num_glyphs; i++) {
        printf("%u, ", (unsigned)bitmap_offsets[i]);
    }
    printf("\n\n};\n\n");

    printf("static const uint8_t bitmap_bytes[%d] = {\n\n    ", num_bitmap_bytes);
    for (int i = 0; i < num_bitmap_bytes; i++) {
        printf("%u, ", (unsigned)bitmap_bytes[i]);
    }
    printf("\n\n};\n");
    fflush(stdout);

    close(1);
    wait(NULL);
}

void
write_84(void)
{
    for (int i = 0; i < num_intervals;) {
        int begin = i;
        printf("        (load 0 %5d \"", begin * 2);
        for (; i < num_intervals && i - begin < 16; i++) {
            printf("%02x", (unsigned)(interval_starts[i] & 0xff));
            printf("%02x", (unsigned)(interval_starts[i] >> 8));
        }
        printf("\")\n");
    }
    printf("\n");

    for (int i = 0; i < num_intervals;) {
        int begin = i;
        printf("        (load 1 %5d \"", begin);
        for (; i < num_intervals && i - begin < 32; i++) {
            printf("%02x", (unsigned)interval_sizes[i]);
        }
        printf("\")\n");
    }
    printf("\n");

    for (int i = 0; i < num_glyphs;) {
        int begin = i;
        printf("        (load 2 %5d \"", begin * 2);
        for (; i < num_glyphs && i - begin < 16; i++) {
            printf("%02x", (unsigned)(bitmap_offsets[i] & 0xff));
            printf("%02x", (unsigned)(bitmap_offsets[i] >> 8));
        }
        printf("\")\n");
    }
    printf("\n");

    for (int i = 0; i < num_bitmap_bytes;) {
        int begin = i;
        printf("        (load 3 %5d \"", begin);
        for (; i < num_bitmap_bytes && i - begin < 32; i++) {
            printf("%02x", (unsigned)bitmap_bytes[i]);
        }
        printf("\")\n");
    }
    printf("\n");
}

int
main(int argc, const char *argv[])
{
    int err;
    const char *usage = "usage: build_fira_mono_table <mode>";

    if (argc != 2)
        die(usage);

    int mode;
    if (strcmp(argv[1], "c") == 0)
        mode = MODE_C;
    else if (strcmp(argv[1], "84") == 0)
        mode = MODE_84;
    else
        die(usage);

    err = FT_Init_FreeType(&freetype);
    if (err)
        die("Failed to initialize freetype.");

    err = FT_New_Face(freetype, FONT, 0, &font_face);
    if (err)
        die("Failed to open font.");

    err = FT_Set_Pixel_Sizes(font_face, 16, 16);
    if (err)
        die("Failed to set font size.");

    FT_ULong charcode;
    FT_UInt index;
    FT_GlyphSlot slot = font_face->glyph;

    charcode = FT_Get_First_Char(font_face, &index);
    while (index != 0) {
        bool choose =
            (charcode == 0) ||
            (32 <= charcode && charcode <= 126) ||
            (0x80 <= charcode && charcode <= 0xff) ||
            (0x2000 <= charcode && charcode <= 0x206f) ||
            (0x2500 <= charcode && charcode <= 0x257f) ||
            (charcode == 0x25c9);
        if (choose) {
            charcodes[num_glyphs] = charcode;
            err = FT_Load_Glyph(font_face, index, FT_LOAD_RENDER);
            if (err)
                die("Failed to load glyph.");
            bitmap_offsets[num_glyphs] = num_bitmap_bytes;
            num_glyphs++;
            bitmap_bytes[num_bitmap_bytes++] = (uint8_t)slot->bitmap_top;
            bitmap_bytes[num_bitmap_bytes++] = (uint8_t)slot->bitmap_left;
            bitmap_bytes[num_bitmap_bytes++] = (uint8_t)slot->bitmap.rows;
            bitmap_bytes[num_bitmap_bytes++] = (uint8_t)slot->bitmap.width;
            int num_pixels = slot->bitmap.rows * slot->bitmap.width;
            for (int i = 0; i < num_pixels; i++)
                bitmap_bytes[num_bitmap_bytes++] = slot->bitmap.buffer[i];
        }
        charcode = FT_Get_Next_Char(font_face, charcode, &index);
    }

    if (num_glyphs > 0) {
        interval_starts[0] = charcodes[0];
        interval_sizes[0] = 1;
        for (int i = 1; i < num_glyphs; i++) {
            if (charcodes[i - 1] + 1 != charcodes[i]) {
                num_intervals++;
                interval_starts[num_intervals] = charcodes[i];
            }
            interval_sizes[num_intervals]++;
        }
        num_intervals++;
    }

    if (mode == MODE_C)
        write_c();
    else if (mode == MODE_84)
        write_84();

    return 0;
}