#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <string.h>

#include "netpbm/mallocvar.h"
#include "pm.h"
#include "ppmdfont.h"


extern struct ppmd_font ppmd_standardfont;

static const struct ppmd_font * currentFontP = &ppmd_standardfont;

static void
readGlyphHeader(FILE *                    const ifP,
                struct ppmd_glyphHeader * const glyphHeaderP) {

    glyphHeaderP->commandCount = fgetc(ifP);
    glyphHeaderP->skipBefore   = fgetc(ifP);
    glyphHeaderP->skipAfter    = fgetc(ifP);
}



static void
readGlyphCommand(FILE *                     const ifP,
                 struct ppmd_glyphCommand * const glyphCommandP) {

    glyphCommandP->verb = fgetc(ifP);
    glyphCommandP->x    = fgetc(ifP);
    glyphCommandP->y    = fgetc(ifP);
}



static void
readCharacter(FILE *              const ifP,
              struct ppmd_glyph * const glyphP) {

    unsigned int commandNum;
    struct ppmd_glyphCommand * commandList;

    readGlyphHeader(ifP, &glyphP->header);

    MALLOCARRAY(commandList, glyphP->header.commandCount);

    if (commandList == NULL)
        pm_error("Insufficient memory to create a %u-command "
                 "command list.", glyphP->header.commandCount);

    for (commandNum = 0;
         commandNum < glyphP->header.commandCount;
         ++commandNum) {

        readGlyphCommand(ifP, &commandList[commandNum]);
    }
    glyphP->commandList = commandList;
}



static void
readFontHeader(FILE *                   const ifP,
               struct ppmd_fontHeader * const fontHeaderP) {

    size_t rc;

    rc = fread(&fontHeaderP->signature, 1, sizeof(fontHeaderP->signature),
               ifP);

    if (rc != sizeof(fontHeaderP->signature))
        pm_error("Unable to read the header from the font file.  "
                 "errno=%d (%s)", errno, strerror(errno));

    fontHeaderP->format         = fgetc(ifP);
    fontHeaderP->characterCount = fgetc(ifP);
    fontHeaderP->firstCodePoint = fgetc(ifP);
}



void
ppmd_set_font(const struct ppmd_font * const newFontP) {

    currentFontP = newFontP;
}



const struct ppmd_font *
ppmd_get_font(void) {

    return currentFontP;
}



void
ppmd_read_font(FILE *                        const ifP,
               const struct ppmd_font **     const fontPP) {

    unsigned int relativeCodePoint;
    struct ppmd_glyph * glyphTable;
    struct ppmd_font * fontP;

    MALLOCVAR(fontP);
    if (fontP == NULL)
        pm_error("Insufficient memory for font header");

    readFontHeader(ifP, &fontP->header);

    MALLOCARRAY(glyphTable, fontP->header.characterCount);
    if (glyphTable == NULL)
        pm_error("Insufficient memory to store %u characters",
                 fontP->header.characterCount);

    for (relativeCodePoint = 0;
         relativeCodePoint < fontP->header.characterCount;
         ++relativeCodePoint) {

        readCharacter(ifP, &glyphTable[relativeCodePoint]);
    }
    fontP->glyphTable = glyphTable;
    *fontPP = fontP;
}



void
ppmd_free_font(const struct ppmd_font * const fontP) {

    unsigned int relativeCodePoint;

    for (relativeCodePoint = 0;
         relativeCodePoint < fontP->header.characterCount;
         ++relativeCodePoint) {

        free((void*)fontP->glyphTable[relativeCodePoint].commandList);
    }
    free((void*)fontP->glyphTable);
    free((void*)fontP);
}
