#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <grass/gis.h>

struct glyph {
    unsigned int offset : 20;
    unsigned int count : 12;
};

static struct glyph *glyphs;
static int glyphs_alloc;

static unsigned char *xcoords, *ycoords;
static int coords_offset;
static int coords_alloc;

static int fontmap[1024];
static int num_chars;

static char current_font[16];
static int font_loaded;

static struct glyph *glyph_slot(int idx)
{
    if (glyphs_alloc <= idx) {
        int new_alloc = idx + ((glyphs_alloc > 0) ? 1000 : 4000);

        glyphs = G_realloc(glyphs, new_alloc * sizeof(struct glyph));
        memset(&glyphs[glyphs_alloc], 0,
               (new_alloc - glyphs_alloc) * sizeof(struct glyph));
        glyphs_alloc = new_alloc;
    }

    return &glyphs[idx];
}

static int coord_slots(int count)
{
    int n;

    if (coords_alloc < coords_offset + count) {
        coords_alloc =
            coords_offset + count + ((coords_alloc > 0) ? 10000 : 60000);
        xcoords = G_realloc(xcoords, coords_alloc);
        ycoords = G_realloc(ycoords, coords_alloc);
    }

    n = coords_offset;
    coords_offset += count;

    return n;
}

static void read_hersh(const char *filename)
{
    FILE *fp = fopen(filename, "r");

    if (!fp)
        return;

    while (!feof(fp)) {
        char buf[8];
        struct glyph *glyph;
        int coords;
        unsigned int i, idx, count;
        int c;

        switch (c = fgetc(fp)) {
        case '\r':
            fgetc(fp);
            continue;
        case '\n':
            continue;
        default:
            ungetc(c, fp);
            break;
        }

        if (fread(buf, 1, 5, fp) != 5)
            break;

        buf[5] = 0;
        idx = atoi(buf);

        if (fread(buf, 1, 3, fp) != 3)
            break;

        buf[3] = 0;
        count = atoi(buf);

        glyph = glyph_slot(idx);
        coords = coord_slots(count);

        glyph->offset = coords;
        glyph->count = count;

        for (i = 0; i < count; i++) {
            if ((i + 4) % 36 == 0) {
                /* skip newlines? */
                if (fgetc(fp) == '\r')
                    fgetc(fp);
            }

            xcoords[coords + i] = fgetc(fp);
            ycoords[coords + i] = fgetc(fp);
        }

        if (fgetc(fp) == '\r')
            fgetc(fp);
    }

    fclose(fp);
}

static void load_glyphs(void)
{
    int i;

    if (glyphs)
        return;

    for (i = 1; i <= 4; i++) {
        char buf[GPATH_MAX];

        sprintf(buf, "%s/fonts/hersh.oc%d", G_gisbase(), i);
        read_hersh(buf);
    }
}

static void read_fontmap(const char *name)
{
    char buf[GPATH_MAX];
    FILE *fp;

    num_chars = 0;
    memset(fontmap, 0, sizeof(fontmap));

    sprintf(buf, "%s/fonts/%s.hmp", G_gisbase(), name);

    fp = fopen(buf, "r");
    if (!fp) {
        G_warning("Unable to open font map '%s': %s. "
                  "Try running 'g.mkfontcap --overwrite'",
                  buf, strerror(errno));
        return;
    }

    while (fscanf(fp, "%s", buf) == 1) {
        int a, b;

        if (sscanf(buf, "%d-%d", &a, &b) == 2)
            while (a <= b)
                fontmap[num_chars++] = a++;
        else if (sscanf(buf, "%d", &a) == 1)
            fontmap[num_chars++] = a;
    }

    fclose(fp);
}

static void load_font(void)
{
    if (font_loaded)
        return;

    if (!glyphs)
        load_glyphs();

    read_fontmap(current_font);

    font_loaded = 1;
}

int font_init(const char *name)
{
    if (strcmp(name, current_font) == 0)
        return 0;

    strcpy(current_font, name);
    font_loaded = 0;

    return 0;
}

int get_char_vects(unsigned char achar, int *n, unsigned char **X,
                   unsigned char **Y)
{
    struct glyph *glyph;
    int i;

    if (!font_loaded)
        load_font();

    i = (int)achar - 040; /* translate achar to char# in font index */
    if (i <= 0 || i >= num_chars) {
        *n = 0;
        return 1;
    }

    glyph = &glyphs[fontmap[i]];

    *n = glyph->count;
    *X = &xcoords[glyph->offset];
    *Y = &ycoords[glyph->offset];

    return 0;
}
