/**
 ** fdv_win.c -- driver for Windows resource font file format
 **
 ** Copyright (C) 2003 Dimitar Zhekov
 ** [e-mail: jimmy@is-vn.bg]
 **
 ** This file is part of the GRX graphics library.
 **
 ** The GRX graphics library is free software; you can redistribute it
 ** and/or modify it under some conditions; see the "copying.grx" file
 ** for details.
 **
 ** This library is distributed in the hope that it will be useful,
 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 **
 **/

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "libgrx.h"
#include "grfontdv.h"
#include "arith.h"
#include "fonts/fdv_win.h"

#ifndef SEEK_SET
#define SEEK_SET 0
#endif

static FILE *fontfp = NULL;
static int offset = 0;
static GrResourceFileHeaderWIN rhdr;
static GrFontFileHeaderWIN fhdr;
static GrCharHeaderWIN *ctable = NULL;

#if BYTE_ORDER==BIG_ENDIAN
#include "ordswap.h"
static void swap_resource(void)
{
    GRX_ENTER();
    _GR_swap16u(&rhdr.type_id);
    _GR_swap16u(&rhdr.name_id);
    _GR_swap16u(&rhdr.flags);
    _GR_swap32u(&rhdr.size);
    GRX_LEAVE();
}

static void swap_header(void)
{
    GRX_ENTER();
    _GR_swap16u(&fhdr.version);
    _GR_swap32u(&fhdr.size);
    _GR_swap16u(&fhdr.type);
    _GR_swap16u(&fhdr.points);
    _GR_swap16u(&fhdr.vert_res);
    _GR_swap16u(&fhdr.horiz_res);
    _GR_swap16u(&fhdr.ascent);
    _GR_swap16u(&fhdr.internal_leading);
    _GR_swap16u(&fhdr.external_leading);
    _GR_swap16u(&fhdr.weight);
    _GR_swap16u(&fhdr.pix_width);
    _GR_swap16u(&fhdr.pix_height);
    _GR_swap16u(&fhdr.avg_width);
    _GR_swap16u(&fhdr.max_width);
    _GR_swap16u(&fhdr.width_bytes);
    _GR_swap32u(&fhdr.device);
    _GR_swap32u(&fhdr.face);
    _GR_swap32u(&fhdr.bits_pointer);
    _GR_swap32u(&fhdr.bits_offset);
    GRX_LEAVE();
}

static void swap_ctable(void)
{
    int i;

    GRX_ENTER();
    for(i = 0; i < fhdr.last_char - fhdr.first_char + 2; i++) {
        _GR_swap16u(&ctable[i].width);
        _GR_swap16u(&ctable[i].offset);
    }
    GRX_LEAVE();
}
#endif

static void cleanup(void)
{
    GRX_ENTER();
    if(fontfp != NULL) fclose(fontfp);
    if(ctable != NULL) free(ctable);
    fontfp = NULL;
    ctable = NULL;
    offset = 0;
    GRX_LEAVE();
}

static int openfile(char *fname)
{
    int res;
    unsigned size;

    GRX_ENTER();
    res = FALSE;
    cleanup();
    /* open and test the file */
    fontfp = fopen(fname, "rb");
    if(fontfp == NULL) {
        DBGPRINTF(DBG_FONT, ("fopen(\"%s\") failed\n", fname));
        goto done;
    }
    if(fread(&rhdr, 1, sizeof rhdr, fontfp) != sizeof rhdr) {
        DBGPRINTF(DBG_FONT, ("read resource failed\n", fname));
        goto done;
    }
    if(rhdr.type_ff == 0xFF) {
        #if BYTE_ORDER==BIG_ENDIAN
        DBGPRINTF(DBG_FONT, ("swapping resource byte order\n"));
        swap_resource();
        #endif
        if(rhdr.type_id != 0x0008 || rhdr.name_ff != 0xFF) {
            DBGPRINTF(DBG_FONT, ("invalid or unsupported resource header\n"));
            goto done;
        }
        offset = sizeof rhdr;
    }
    else if(fseek(fontfp, 0, SEEK_SET < 0)) {
        DBGPRINTF(DBG_FONT, ("rewind failed"));
        goto done;
    }
    if(fread(&fhdr, 1, sizeof fhdr, fontfp) != sizeof fhdr) {
        DBGPRINTF(DBG_FONT, ("read header failed\n"));
        goto done;
    }
    #if BYTE_ORDER==BIG_ENDIAN
    DBGPRINTF(DBG_FONT, ("swapping header byte order\n"));
    swap_header();
    #endif
    if(fhdr.version != 0x0200 || fhdr.type != 0) {
        DBGPRINTF(DBG_FONT, ("unrecognized font header\n"));
        goto done;
    }
    /* allocate and read the ctable */
    size = (fhdr.last_char - fhdr.first_char + 2) * sizeof *ctable;
    if((ctable = malloc(size)) == NULL) {
        DBGPRINTF(DBG_FONT, ("allocate ctable failed\n"));
        goto done;
    }
    if(fread(ctable, 1, size, fontfp) != size) {
        DBGPRINTF(DBG_FONT, ("read ctable failed\n"));
        goto done;
    }
    #if BYTE_ORDER==BIG_ENDIAN
    DBGPRINTF(DBG_FONT, ("swapping ctable byte order\n"));
    swap_ctable();
    #endif
    res = TRUE;

done:
    if(!res) cleanup();
    GRX_RETURN(res);
}

static char *families[] = { "Unknown", "Roman", "Swiss", "Modern", "Script", "Decorative" };

static int header(GrFontHeader *hdr)
{
    int res;
    int c;
    char *s;

    GRX_ENTER();
    res = FALSE;
    if(fontfp != NULL) {
        if((c = fhdr.pitch_and_family >> 4) <= 5) strcpy(hdr->family, families[c]);
        else sprintf(hdr->family, "0x%x", fhdr.pitch_and_family);
        if(fhdr.face) {
            s = hdr->name;
            if(fseek(fontfp, offset + fhdr.face, SEEK_SET) < 0) goto done;
            do {
                if((c = fgetc(fontfp)) == EOF) goto done;
                *(s++) = c;
            } while(c && s - hdr->name < 99);
            if(c) *s = '\0';
        }
        else sprintf(hdr->name, "%s-%d", hdr->family, fhdr.pix_height);
        hdr->proportional = fhdr.pix_width == 0;
        hdr->scalable = FALSE;
        hdr->preloaded = FALSE;
        hdr->modified = GR_FONTCVT_NONE;
        hdr->width = hdr->proportional ? fhdr.avg_width : fhdr.pix_width;
        hdr->height = fhdr.pix_height;
        hdr->baseline = fhdr.ascent;
        hdr->ulheight = imax(1, hdr->height / 15);
        hdr->ulpos = hdr->height - hdr->ulheight;
        hdr->minchar = fhdr.first_char;
        hdr->numchars = fhdr.last_char - fhdr.first_char + 1;
        hdr->encoding = 0; /* GR_FONTENC_UNKNOWN */
        res = TRUE;
    }

done:
    GRX_RETURN(res);
}

static int charwdt(int chr)
{
    int res;

    GRX_ENTER();
    res = -1;
    if(fontfp != NULL && chr >= fhdr.first_char && chr <= fhdr.last_char) res = ctable[chr - fhdr.first_char].width;
    GRX_RETURN(res);
}

static int bitmap(int chr, int w, int h, char *buffer)
{
    int res;
    int i, y;
    int bytes;

    GRX_ENTER();
    res = FALSE;
    if(w != charwdt(chr) || h != fhdr.pix_height) goto done;
    bytes = (w - 1) / 8 + 1;
    if(fseek(fontfp, offset + ctable[chr - fhdr.first_char].offset, SEEK_SET) < 0) goto done;
    for(i = 0; i < bytes; i++)
        for(y = 0; y < h; y++)
            if(fread(buffer + bytes * y + i, 1, 1, fontfp) != 1) goto done;
    res = TRUE;

done:
    GRX_RETURN(res);
}

GrFontDriver _GrFontDriverWIN = {
    "WIN",                              /* driver name (doc only) */
    ".res",                             /* font file extension */
    FALSE,                              /* scalable */
    openfile,                           /* file open and check routine */
    header,                             /* font header reader routine */
    charwdt,                            /* character width reader routine */
    bitmap,                             /* character bitmap reader routine */
    cleanup                             /* cleanup routine */
};
