/* Framebuffer Graphics Libary for Linux, Copyright 1993 Harm Hanemaayer */
/* text.c       Text writing and fonts */


#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <vga.h>
#include "inlstring.h"		/* include inline string operations */

#include "vgagl.h"
#include "def.h"



/* Text/font functions */

static int font_width = 8;
static int font_height = 8;
static char *font_address;
static int font_charactersize = 64;
static int font_writemode = WRITEMODE_OVERWRITE;
static int compressed_font_bg = 0;
static int compressed_font_fg = 15;

static void writecompressed(int x, int y, int n, unsigned char *s);


void gl_colorfont(int fw, int fh, int fg, void *_dp)
{
    uchar *dp = _dp;
    int i;
    i = fw * fh * 256;
    switch (BYTESPERPIXEL) {
    case 1:
	while (i > 0) {
	    if (*dp)
		*dp = fg;
	    dp++;
	    i--;
	}
	break;
    case 2:
	while (i > 0) {
	    if (*(ushort *) dp)
		*(ushort *) dp = fg;
	    dp += 2;
	    i--;
	}
	break;
    case 3:
	while (i > 0) {
	    if (*(ushort *) dp || *(dp + 2)) {
		*(ushort *) dp = fg;
		*(dp + 2) = fg >> 16;
	    }
	    dp += 3;
	    i--;
	}
	break;
    case 4:
	while (i > 0) {
	    if (*(int *) dp)
		*(int *) dp = fg;
	    dp += 4;
	    i--;
	}
	break;

    }
}

void gl_setfont(int fw, int fh, void *font)
{
    font_width = fw;
    font_height = fh;
    font_charactersize = font_width * font_height * BYTESPERPIXEL;
    font_address = font;
}

void gl_setwritemode(int m)
{
    font_writemode = m;
}

void gl_writen(int x, int y, int n, char *s)
{
/* clipping in putbox */
    int i;
    if (font_writemode & FONT_COMPRESSED) {
	writecompressed(x, y, n, s);
	return;
    }
    if (!(font_writemode & WRITEMODE_MASKED)) {
        for (i=0; i<n; i++) {
	    gl_putbox(x + i * font_width, y, font_width,
		      font_height, font_address +
		      (unsigned char) s[i] * font_charactersize);
	}
    } else {			/* masked write */
        for (i=0; i<n; i++) {
	    gl_putboxmask(x + i * font_width, y, font_width,
			  font_height, font_address +
			  (unsigned char) s[i] * font_charactersize);
	}
    }
}

void gl_write(int x, int y, char *s)
{
    int n;
    for (n=0; s[n]; n++);
    gl_writen(x, y, n, s);
}

#ifndef SVGA_AOUT

static int gl_nprintf(int sx, int sy, size_t bufs, const char *fmt, va_list args)
{
    unsigned char *buf;
    static int x = 0, y = 0, x_start = 0;
    int n;

    buf = alloca(bufs);
    n = vsnprintf(buf, bufs, fmt, args);
    if (n < 0)
	return n; /* buffer did not suffice, return and retry */

    if ((sx >= 0) && (sy >= 0)) {
	x = x_start = sx;
	y = sy;
    }
	
    for (; *buf; buf++)
	switch (*buf) {
	case '\a':		/* badly implemented */
	    fputc('\a', stdout);
	    fflush(stdout);
	    break;
	case '\b':
	    x -= font_width;
	    if (x < x_start) {
		x = WIDTH + (x_start % font_width);
		while(x + font_width > WIDTH)
		    x -= font_width;
		if (y >= font_height)
		    y -= font_height;
	    }
	    break;
	case '\n':
	  newline:
	    y += font_height;
	    if (y + font_height > HEIGHT)
		y %= font_height;
	case '\r':
	    x = x_start;
	    break;
	case '\t':
	    x += ((TEXT_TABSIZE - ((x - x_start) / font_width) % TEXT_TABSIZE) * font_width);
	    goto chk_wrap;
	    break;
	case '\v':
	    y += font_height;
	    if (y + font_height > HEIGHT)
		y %= font_height;
	    break;
	default:
	    gl_writen(x, y, 1, buf);
	    x += font_width;
	  chk_wrap:
	    if (x + font_width > WIDTH)
		goto newline;
	}
    return n;
}

int gl_printf(int x, int y, const char *fmt, ...)
{
    size_t bufs = BUFSIZ;
    int result;
    va_list args;

    va_start(args, fmt);

    /* Loop until buffer size suffices */
    do {
	result = gl_nprintf(x, y, bufs, fmt, args);
	bufs <<= 1;
    } while(result < 0);
	
    va_end(args);
    return result;
}

#endif
void gl_expandfont(int fw, int fh, int fg, void *_f1, void *_f2)
{
/* Convert bit-per-pixel font to byte(s)-per-pixel font */
    uchar *f1 = _f1;
    uchar *f2 = _f2;
    int i, x, y, b = 0;		/* keep gcc happy with b = 0 - MW */

    for (i = 0; i < 256; i++) {
	for (y = 0; y < fh; y++)
	    for (x = 0; x < fw; x++) {
		if (x % 8 == 0)
		    b = *f1++;
		if (b & (128 >> (x % 8)))	/* pixel */
		    switch (BYTESPERPIXEL) {
		    case 1:
			*f2 = fg;
			f2++;
			break;
		    case 2:
			*(ushort *) f2 = fg;
			f2 += 2;
			break;
		    case 3:
			*(ushort *) f2 = fg;
			*(f2 + 2) = fg >> 16;
			f2 += 3;
			break;
		    case 4:
			*(uint *) f2 = fg;
			f2 += 4;
		} else		/* no pixel */
		    switch (BYTESPERPIXEL) {
		    case 1:
			*f2 = 0;
			f2++;
			break;
		    case 2:
			*(ushort *) f2 = 0;
			f2 += 2;
			break;
		    case 3:
			*(ushort *) f2 = 0;
			*(f2 + 2) = 0;
			f2 += 3;
			break;
		    case 4:
			*(uint *) f2 = 0;
			f2 += 4;
		    }
	    }
    }
}

static void expandcharacter(int bg, int fg, int c, unsigned char *bitmap)
{
    int x, y;
    unsigned char *font;
    int b = 0;			/* keep gcc happy with b = 0 - MW */

    font = font_address + c * (font_height * ((font_width + 7) / 8));

    for (y = 0; y < font_height; y++)
	for (x = 0; x < font_width; x++) {
	    if (x % 8 == 0)
		b = *font++;
	    if (b & (128 >> (x % 8)))	/* pixel */
		switch (BYTESPERPIXEL) {
		case 1:
		    *bitmap = fg;
		    bitmap++;
		    break;
		case 2:
		    *(ushort *) bitmap = fg;
		    bitmap += 2;
		    break;
		case 3:
		    *(ushort *) bitmap = fg;
		    *(bitmap + 2) = fg >> 16;
		    bitmap += 3;
		    break;
		case 4:
		    *(uint *) bitmap = fg;
		    bitmap += 4;
	    } else		/* background pixel */
		switch (BYTESPERPIXEL) {
		case 1:
		    *bitmap = bg;
		    bitmap++;
		    break;
		case 2:
		    *(ushort *) bitmap = bg;
		    bitmap += 2;
		    break;
		case 3:
		    *(ushort *) bitmap = bg;
		    *(bitmap + 2) = bg >> 16;
		    bitmap += 3;
		    break;
		case 4:
		    *(uint *) bitmap = bg;
		    bitmap += 4;
		}
	}
}

/* Write using compressed font. */

static void writecompressed(int x, int y, int n, unsigned char *s)
{
    unsigned char *bitmap;
    int i;
    bitmap = alloca(font_width * font_height * BYTESPERPIXEL);
    if (!(font_writemode & WRITEMODE_MASKED)) {
        for (i=0; i<n; i++) {
	    expandcharacter(compressed_font_bg,
			    compressed_font_fg, s[i], bitmap);
	    gl_putbox(x + i * font_width, y, font_width,
		      font_height, bitmap);
	}
    } else {			/* masked write */
        for (i=0; i<n; i++) {
	    expandcharacter(0, compressed_font_fg, s[i], bitmap);
	    gl_putboxmask(x + i * font_width, y, font_width,
			  font_height, bitmap);
	}
    }
}

void gl_setfontcolors(int bg, int fg)
{
    compressed_font_bg = bg;
    compressed_font_fg = fg;
}
