/**
 * * fdv_bgi.c -- driver for Borland BGI font file format
 **
 ** Copyright (c) 1995 Csaba Biegl, 820 Stirrup Dr, Nashville, TN 37221
 ** [e-mail: csaba@vuse.vanderbilt.edu]
 **
 ** 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.
 **
 ** Contrib by Christian Domp (alma.student.uni-kl.de) See "doc/contrib.doc"
 ** Contributions by: (See "doc/credits.doc" for details)
 ** Hartmut Schirmer (hsc@techfak.uni-kiel.de)
 **
 **/

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

#include "libgrx.h"
#include "grfontdv.h"
#include "arith.h"
#include "memfill.h"
#include "ordswap.h"
#include "fonts/fdv_bgi.h"

/* This is based on the font code in Hartmut Schirmer's BCC2GRX package */

/* This code requires packed structs. Should be revised for **
 ** better portability                           hsc, 980427 */

#ifndef SEEK_SET
#define SEEK_SET        0
#endif
#ifndef SEEK_END
#define SEEK_END        2
#endif

static GR_int8   *fdata   = NULL;
static GR_int8u  *wtable  = NULL;
static GR_int16u *offsets = NULL;
static GR_int8u  *vecdata = NULL;
static int       *realwdt = NULL;
static int       *xoffset = NULL;
static int       realhgt = 0;
static int       yoffset = 0;
static BGIfontFileHeader *fhdr = NULL;
static BGIfontHeaderType *fhtp = NULL;

static void cleanup(void)
{
    if(fdata)   free(fdata);
    if(realwdt) free(realwdt);
    fdata   = NULL;
    realwdt = NULL;
}

/* fix up up sizes to account for out of box characters */
static void fixlimits(void)
{
    int i,ymin  = 32000,ymax = -32000;
    int orighgt = fhtp->org_to_cap - fhtp->org_to_dec;

    for(i = 0; i < fhtp->nchrs; i++) {
        int xpos = 0,ypos = 0,xend,yend;
        int xmin = 32000,xmax = -32000;
        GR_int16u *vp = (GR_int16u *)(vecdata + offsets[i]);
        for( ; ; vp++) {
            switch(SV_COMMAND(*vp)) {
                case SVC_END:
                    break;
                case SVC_MOVE:
                    xpos = SV_XCOORD(*vp);
                    ypos = fhtp->org_to_cap - SV_YCOORD(*vp);
                    continue;
                case SVC_SCAN:
                    /* what to do here ? */
                    continue;
                case SVC_DRAW:
                    xend = SV_XCOORD(*vp);
                    yend = fhtp->org_to_cap - SV_YCOORD(*vp);
                    xmin = imin(xmin,imin(xpos,xend));
                    ymin = imin(ymin,imin(ypos,yend));
                    xmax = imax(xmax,imax(xpos,xend));
                    ymax = imax(ymax,imax(ypos,yend));
                    xpos = xend;
                    ypos = yend;
                    continue;
            }
            break;
        }
        xoffset[i] = imax(0,(-xmin));
        realwdt[i] = imax(1,(imax((xmax + 1),wtable[i]) + xoffset[i]));
        DBGPRINTF(DBG_FONT,(
            "character %3d: origwdt=%-3d realwdt=%-3d xoffset=%-3d %c\n",
            (i + fhtp->firstch), wtable[i], realwdt[i],
            xoffset[i], (realwdt[i] > wtable[i]) ? '*' : ' '));
    }
    yoffset = imax(0,(-ymin));
    realhgt = imax((ymax + 1),orighgt) + yoffset;
    DBGPRINTF(DBG_FONT,(
        "FONT orighgt=%-3d realhgt=%-3d yoffset=%-3d\n",
        orighgt, realhgt, yoffset));
}

static int openfile(char *fname)
{
    GR_int8 *p;
    FILE *fp;
    long flen;
    int res;
    GRX_ENTER();
    res = FALSE;

    do {
        cleanup();
        fp = fopen(fname,"rb");
        if(!fp) break; /* FALSE */
        fseek(fp,0L,SEEK_END);
        flen = ftell(fp);
        fseek(fp,0L,SEEK_SET);
        if((flen <= (signed long)(sizeof(*fhdr) + sizeof(*fhtp))) ||
            (flen != (signed long)((size_t)flen))) break; /* FALSE */
        fdata = malloc((size_t)flen);
        if (!fdata) break; /* FALSE */
        if (fread(fdata,1,(size_t)flen,fp) != (size_t)flen) break; /* FALSE */
        fclose(fp); fp = NULL;
        if (strncmp(fdata,FILEMARKER,strlen(FILEMARKER)) != 0) break; /*FALSE, magic code not found */
        p = strchr(fdata,MARKEREND); /* skip copyright text */
        if (!p || ((++p) > &fdata[128 - sizeof(*fhdr)])) break; /* FALSE */
        fhdr = (BGIfontFileHeader *)p;
        #if BYTE_ORDER==BIG_ENDIAN
        _GR_swap16u(&fhdr->header_size);
        _GR_swap16u(&fhdr->font_size);
        #endif
        fhtp = (BGIfontHeaderType *)(fdata + fhdr->header_size);
        #if BYTE_ORDER==BIG_ENDIAN
        _GR_swap16u(&fhtp->nchrs);
        _GR_swap16u(&fhtp->cdefs);
        #endif
        if (fhtp->sig != SIGBYTE) break; /* FALSE */
        offsets = (GR_int16u *)(fhtp + 1);
        wtable  = (GR_int8u  *)(offsets + fhtp->nchrs);
        vecdata = (GR_int8u *)((GR_int8u *)fhtp + fhtp->cdefs);
        realwdt = malloc(sizeof(int) * fhtp->nchrs * 2);
        xoffset = realwdt + fhtp->nchrs;
        if(!realwdt) break; /* FALSE */
        fixlimits();
        res = TRUE;
    } while (0);

    if (!res) {
        cleanup();
        if (fp) fclose(fp);
    }
    GRX_RETURN(res);
}

static unsigned int avgwidth(void)
{
    unsigned int i,total = 0;

    for(i = 0; i < fhtp->nchrs; i++) total += realwdt[i];
    return((total + (fhtp->nchrs >> 1)) / fhtp->nchrs);
}

static int header(GrFontHeader *hdr)
{
    if(!fdata) return(FALSE);
    strcpy(hdr->family,"BGI");
    memcpy(hdr->name,fhdr->font_name,sizeof(fhdr->font_name));
    hdr->name[sizeof(fhdr->font_name)] = '\0';
    hdr->proportional = TRUE;
    hdr->scalable     = TRUE;
    hdr->preloaded    = FALSE;
    hdr->modified     = GR_FONTCVT_NONE;
    hdr->width        = avgwidth();
    hdr->height       = realhgt;
    hdr->baseline     = fhtp->org_to_cap - fhtp->org_to_base + yoffset;
    hdr->ulheight     = imax(1,(hdr->height / 15));
    hdr->ulpos        = hdr->height - hdr->ulheight;
    hdr->minchar      = fhtp->firstch;
    hdr->numchars     = fhtp->nchrs;
    hdr->encoding     = 0; /* GR_FONTENC_UNKNOWN */
    hdr->sparse       = FALSE;
    hdr->usedefg      = FALSE;
    hdr->defglyph     = 0;
    DBGPRINTF(DBG_FONT,(
        "Font header:\n"
        "  name         = %s\n"
        "  family       = %s\n"
        "  proportional = %d\n"
        "  scalable     = %d\n"
        "  preloaded    = %d\n"
        "  modified     = %d\n"
        "  width        = %d\n"
        "  height       = %d\n"
        "  baseline     = %d\n"
        "  ulheight     = %d\n"
        "  ulpos        = %d\n"
        "  minchar      = %d\n"
        "  numchars     = %d\n",
        hdr->name,
        hdr->family,
        hdr->proportional,
        hdr->scalable,
        hdr->preloaded,
        hdr->modified,
        hdr->width,
        hdr->height,
        hdr->baseline,
        hdr->ulheight,
        hdr->ulpos,
        hdr->minchar,
        hdr->numchars
    ));
    return(TRUE);
}

static int charwdt(int chr)
{
    chr -= fhtp->firstch;

    if(!fdata) return(-1);
    if((unsigned int)chr >= (unsigned int)fhtp->nchrs) return(-1);
    DBGPRINTF(DBG_FONT,("charwdt for %d is %d\n",(chr + fhtp->firstch),realwdt[chr]));
    return(realwdt[chr]);
}

static void bitline(int x1,int y1,int x2,int y2,char *buffer,int pitch)
{
    int  dx    = x2 - x1;
    int  dy    = y2 - y1;
    int  xstep = (dx < 0) ? ((dx = -dx),(-1)) : 1;
    int  ystep = ((dy < 0) ? ((dy = -dy),(-pitch)) : pitch) << 3;
    unsigned int addr  = ((y1 * pitch) << 3) + x1;
    int  count,error,errsub,erradd;
    unsigned int step1,step2;

    if(dy > dx) {
        count  = dy + 1;
        error  = dy >> 1;
        erradd = dy;
        errsub = dx;
        step1  = ystep;
        step2  = xstep + ystep;
    }
    else {
        count  = dx + 1;
        error  = dx >> 1;
        erradd = dx;
        errsub = dy;
        step1  = xstep;
        step2  = xstep + ystep;
    }
    do {
        buffer[addr >> 3] |= (0x80U >> (addr & 7));
        if((error -= errsub) < 0) {
            error += erradd;
            addr  += step2;
        }
        else {
            addr  += step1;
        }
    } while(--count);
}

static int bitmap(int chr,int w,int h,char *buffer)
{
    int xmul,xdiv,ymul,ydiv;
    int xpos,ypos,xend,yend;
    int offs;
    GR_int16u *vp;
    chr -= fhtp->firstch;

    if(!fdata) return(FALSE);
    if((unsigned int)chr >= (unsigned int)fhtp->nchrs) return(FALSE);
    if((w <= 0) || (h <= 0)) return(FALSE);
    xmul = w - 1;
    xdiv = realwdt[chr] - 1;
    ymul = h - 1;
    ydiv = realhgt - 1;
    DBGPRINTF(DBG_FONT,(
        "bitmap for %d, origsize = %d %d, rendered = %d %d\n",
        (chr  + fhtp->firstch), (xdiv + 1),(ydiv + 1), (xmul + 1),(ymul + 1)));
    vp   = (GR_int16u *)(vecdata + offsets[chr]);
    offs = (w + 7) >> 3;
    memfill_b(buffer,0,(offs * h));
    if(xdiv <= 0) return(TRUE);
    for(xpos = ypos = 0; ; vp++) {
        switch(SV_COMMAND(*vp)) {
            case SVC_END:
                break;
            case SVC_MOVE:
                xpos = SV_XCOORD(*vp) + xoffset[chr];
                ypos = fhtp->org_to_cap - SV_YCOORD(*vp) + yoffset;
                DBGPRINTF(DBG_FONT,(
                    "  cmd=0x%04x, move   %-3d %d\n", *vp, xpos, ypos));
                continue;
            case SVC_SCAN:
                /* what to do here ? */
                DBGPRINTF(DBG_FONT,(
                    "  cmd=0x%04x **** SCAN COMMAND FOUND ****\n",*vp));
                continue;
            case SVC_DRAW:
                xend = SV_XCOORD(*vp) + xoffset[chr];
                yend = fhtp->org_to_cap - SV_YCOORD(*vp) + yoffset;
                DBGPRINTF(DBG_FONT,(
                    "  cmd=0x%04x, vector %-3d %-3d [ %-3d %-3d ] %c\n",
                    *vp,xend,yend,xpos,ypos,
                    ((((unsigned int)xend > (unsigned int)xdiv) ||
                    ((unsigned int)yend > (unsigned int)ydiv) ||
                    ((unsigned int)xpos > (unsigned int)xdiv) ||
                    ((unsigned int)ypos > (unsigned int)ydiv)) ? '*' : ' ')));
                bitline(
                    urscale(xpos,xmul,xdiv),urscale(ypos,ymul,ydiv),
                    urscale(xend,xmul,xdiv),urscale(yend,ymul,ydiv),
                    buffer, offs);
                xpos = xend;
                ypos = yend;
                continue;
        }
        break;
    }
    return(TRUE);
}

GrFontDriver _GrFontDriverBGI = {
    "BGI",                              /* driver name (doc only) */
    ".chr",                             /* font file extension */
    TRUE,                               /* 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 */
};
