/*
 * Copyright (c) 1997 Hong Hunsoo of (KAIST)
 * The Korea Advanced Institute of Science and Technology
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Neither the name of the KAIST nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include <memory.h>
#include <unistd.h>

#include "common.h"
#include "defs.h"

int     step_mask[32] = { 
	0x80000000, 0x40000000, 0x20000000, 0x10000000,
	0x08000000, 0x04000000, 0x02000000, 0x01000000,
	0x00800000, 0x00400000, 0x00200000, 0x00100000,
	0x00080000, 0x00040000, 0x00020000, 0x00010000,
	0x00008000, 0x00004000, 0x00002000, 0x00001000,
	0x00000800, 0x00000400, 0x00000200, 0x00000100,
	0x00000080, 0x00000040, 0x00000020, 0x00000010,
	0x00000008, 0x00000004, 0x00000002, 0x00000001};


int     font_ascent;
int     linecnt;
int     encode_of_firstchar;
struct character **allroot;

char    outbuf[LINE];
int	outbufoffont;
char    inbuf[LINE];

char *nametolower(char *name);
static void  get_dwidth(FILE *fin, struct character *cp);
static void  get_swidth(FILE *fin, struct character *cp);
static int   get_encode(FILE *fin);
static char *get_name(FILE *fin);
static void  get_bbox(FILE *fin, struct bbox *bx);
static char *tospace(char *line);
static void  font_mkprops(struct fontset *font);
void	put_pattern(unsigned int a);

void
err(const char *fmt, ...)
{
	va_list cp;
	va_start(cp, fmt);

	vfprintf(stdout, fmt, cp);
	fprintf(stdout, "\n");
	va_end(cp);
	exit(1);
}

void
get_winsize(int *row, int *col)
{
	struct winsize a;
	int     ret;

	ret =  ioctl(2, TIOCGWINSZ, &a);
	if (ret)
		err("%s(): ioctl():", __F);

	*row = a.ws_row;
	*col = a.ws_col;
	return;
}


char *
fill_buf(FILE *fin)
{
	char *p;

	p = fgets(inbuf, LINE, fin);
	if (p == NULL)
		return NULL;
	linecnt++;
	inbuf[strlen(inbuf)-1] = 0;
	return inbuf;
}

struct character *tchar; /* temporary structure */

/*
 * fonts/bdf/misc/cursor.bdf makes me create following mask
 */
static unsigned int clear_mask[33] = {0x00000000,
	0x80000000, 0xc0000000, 0xe0000000, 0xf0000000,
	0xf8000000, 0xfc000000, 0xfe000000, 0xff000000,
	0xff800000, 0xffc00000, 0xffe00000, 0xfff00000,
	0xfff80000, 0xfffc0000, 0xfffe0000, 0xffff0000,
	0xffff8000, 0xffffc000, 0xffffe000, 0xfffff000,
	0xfffff800, 0xfffffc00, 0xfffffe00, 0xffffff00,
	0xffffff80, 0xffffffc0, 0xffffffe0, 0xfffffff0,
	0xfffffff8, 0xfffffffc, 0xfffffffe, 0xffffffff
};

struct fontset *
initial(const char *fname)
{
	FILE	*fin;
	int	found, size, shift;
	int	order;
	int	i, j, k, here;
	struct character *cp, **root;
	struct fontset	*font;
	struct bbox *bbp;
	char	*p;
	unsigned int q, maxheight, mask_index;

	fprintf(stderr, "%s(): start of processing of %s\n", __F, fname);
	fin = fopen(fname, "r");
	if (fin == NULL)
		err("%s(%s): can not fopen %s", __F, fname, fname);

	font = malloc(sizeof(struct fontset));
	if (font == NULL)
		err("%s(): malloc() failed: set == NULL", __F);
	memset(font, 0x00, sizeof(struct fontset));

	/* I selected 100 arbitrarily */
	font->f_comments = (char **) malloc(sizeof(char *) * 100);
	if (font->f_comments == NULL)
		err("%s(): malloc() failed: font->f_comments == NULL", __F);

	font->f_props = (char **) malloc(sizeof(char *) * 100); /* arbitra */
	if (font->f_props == NULL)
		err("%s(): malloc() failed: font->f_props == NULL", __F);

	linecnt = 0;

	while (fill_buf(fin)) {
		found = strncmp(inbuf, "STARTFONT ", 10);
		if (found == 0) {
			font->f_rev    = atoi(&inbuf[10]);
			font->f_subrev = atoi(&inbuf[12]);
			if (font->f_rev < 0 || font->f_subrev < 0)
				err("%s(): at line \"%s\"", __F, inbuf);
			continue;
		}

		found = strncmp(inbuf, "FONT ", 5);
		if (found == 0) {
			p = strdup(&inbuf[5]);
			font->f_fontname = nametolower(p);
			continue;
		}

		found = strncmp(inbuf, "COMMENT", 7);
		if (found == 0) {
			font->f_comments[font->f_ncomments] = strdup(inbuf);
			font->f_ncomments++;
			continue;
		}

		found = strncmp(inbuf, "SIZE ", 5);
		if (found == 0) {
			p = inbuf;
			p = tospace(p); font->f_sizes[0] = atoi(p);
			p = tospace(p); font->f_sizes[1] = atoi(p);
			p = tospace(p); font->f_sizes[2] = atoi(p);
			continue;
		}
		found = strncmp(inbuf, "FONTBOUNDINGBOX", 15);
		if (found == 0) {
			p = inbuf;
			p = tospace(p); font->f_boundingbox.bb_width = atoi(p);
			p = tospace(p); font->f_boundingbox.bb_height= atoi(p);
			p = tospace(p); font->f_boundingbox.bb_offx = atoi(p);
			p = tospace(p); font->f_boundingbox.bb_offy = atoi(p);
			font->f_maxbbox = font->f_boundingbox; /* XXX */
			continue;
		}

		found = strncmp(inbuf, "STARTPROPERTIES", 15);
		if (found == 0) {
			unsigned int tmpcnt;

			font->f_nprops = atoi(&inbuf[16]);
			if (font->f_nprops < 0)
				err("%s(): at %dth line", __F, __L);
			tmpcnt = 0;
			for (i=0; i < font->f_nprops; i++) {
				fill_buf(fin);
				font->f_props[tmpcnt++] = strdup(inbuf);
			}
			font->f_props[tmpcnt] = NULL;
			continue;
		}

		found = strncmp(inbuf, "CHARS ", 6);
		if (found == 0) {
			font->f_nchars = atoi(&inbuf[6]);
			break;
		}
	}


	root = (struct character **)
		malloc(sizeof(struct character *) * (font->f_nchars+1));
	if (root == NULL)
		err("%s() : malloc() failed: root == NULL ", __F);

	cp = (struct character *)
		malloc(sizeof(struct character) * (font->f_nchars+1));
	if (cp == NULL)
		err("%s(): malloc failed : cp == NULL", __F);

	font_mkprops(font);
	maxheight = font->f_boundingbox.bb_height;


	order = 0;
	for (i=0; i < font->f_nchars; i++, cp++) {
		root[i] = cp;
		size = sizeof(unsigned int) * maxheight;
		cp->c_rows = (int *) malloc(size);
		if (cp->c_rows == NULL)
			err("%s(): malloc failed: cp->c_rows == NULL", __F);
		memset(cp->c_rows, 0x00, size);

		cp->c_fontset  = font;
		cp->c_order    = order++;
		cp->c_charname = get_name(fin);
		cp->c_encoding = get_encode(fin);

		get_swidth(fin, cp); /* SWIDTH */
		get_dwidth(fin, cp); /* DWIDTH */

		get_bbox(fin, &cp->c_bbox);
		fill_buf(fin); /* BITMAP */

		bbp = &cp->c_bbox;
		mask_index = -font->f_boundingbox.bb_offx
			+ bbp->bb_offx + bbp->bb_width;
		for (j=0; j< bbp->bb_height; j++) {
			fill_buf(fin);
			here = j + font->f_boundingbox.bb_height
				+ font->f_boundingbox.bb_offy
				- bbp->bb_height - bbp->bb_offy;
			if (here < 0 || here >= font->f_boundingbox.bb_height)
				continue;


			p = inbuf;
			for (k = 0, q = 0; k < strlen(inbuf); k++, p++) {
				q *= 16;
				q +=  hextoint(p);
			}
			for (; k < 8;k++)
				q *= 16;

			cp->c_rows[here] = q;

			shift = bbp->bb_offx - font->f_boundingbox.bb_offx;

			if (shift > 0)
				cp->c_rows[here] >>= shift;
			else if (shift < 0)
				err("%s(): shift = %d ERROR at %d line of %s",
					__F, shift, __LINE__, __FILE__);
			cp->c_rows[here] &= clear_mask[mask_index];
		}

		fgets(inbuf, LINE, fin); /* ENDCHAR */
	}
	fclose(fin);
	root[i] = cp++;
	cp->c_charname = NULL;

	/*
	 * tchar, the temporary  character structure
	 */
	tchar = (struct character *) malloc(sizeof(struct character));
	if (tchar == NULL)
		err("%s(): malloc failed : tchar == NULL", __F);
	tchar->c_encoding = -1;
	tchar->c_rows = (unsigned int *) malloc(sizeof(int) * maxheight);
	if (tchar->c_rows == NULL)
		err("%s(): malloc failed : tchar->c_rows == NULL", __F);
	memset(tchar->c_rows, 0x00, sizeof(int) * maxheight);

	encode_of_firstchar = root[0]->c_encoding;


	font->f_characters = root;
	font->f_modified = 0;
#if COMMENTED
	if (strstr(font->f_fontname, "-p-")) /* XXX??? */
		font->f_defdwidth = 0;
	else
		font->f_defdwidth = def_dwidth;
#endif

	fprintf(stderr, "%s(): end   of processing of %s\n", __F, fname);
	return font;
}

int
bbox_differ(const struct bbox *bp0, const struct bbox *bp1)
{
	int	ret;

	ret = (bp0->bb_width == bp1->bb_width)
		* (bp0->bb_height == bp1->bb_height)
		* (bp0->bb_offx == bp1->bb_offx)
		* (bp0->bb_offy == bp1->bb_offy);
	return (ret == 0);
}

int
rows_differ(const struct fontset *fs,
	const unsigned int *intp0, const unsigned int *intp1)
{
	int	i, bound;

	bound = fs->f_boundingbox.bb_height;
	for (i=0; i < bound; i++)
		if (intp0[i] != intp1[i])
			return 1;
	return 0;
}



char *
nametolower(char *name)
{
	char	*p;

	for (p = name; *p; p++)
		if (isupper(*p))
			*p += 32;
	return name;
}


static void
get_dwidth(FILE *fin, struct character *cp)
{
	char	*p;

	fill_buf(fin);
	if (strncmp(inbuf, "DWIDTH", 6))
		err("%s(): error at %s %dth line", __F, inbuf, linecnt);
	p = inbuf;
	p = tospace(p); cp->c_dwidth[0] =  atoi(p);
	p = tospace(p); cp->c_dwidth[1] =  atoi(p);
	return;
}

static void
font_mkprops(struct fontset *font)
{
	char	**pp, *p;
	int	found;

	pp = font->f_props;
	while ((p = *pp++)) {
		found = strncmp(p, "FONT_ASCENT",  11);
		if (found == 0)
			font->f_ascent = atoi(&p[12]);
		found = strncmp(p, "FONT_DESCENT",  12);
		if (found == 0)
			font->f_descent = atoi(&p[13]);
	}
	return;
}
		

static void
get_swidth(FILE *fin, struct character *cp)
{
	char	*p;

	fill_buf(fin);
	if (strncmp(inbuf, "SWIDTH", 6))
		err("%s(): error %s %dth line", __F, inbuf, linecnt);
	p = inbuf;
	p = tospace(p); cp->c_swidth[0] =  atoi(p);
	p = tospace(p); cp->c_swidth[1] =  atoi(p);
	return;
}

static int
get_encode(FILE *fin)
{
	fill_buf(fin);
	if (strncmp(inbuf, "ENCODING", 8))
		err("%s(): error %s %dth line", __F, inbuf, linecnt);
	return atoi(&inbuf[8]);
}

static char *
get_name(FILE *fin)
{
	fill_buf(fin);
	if (strncmp(inbuf, "STARTCHAR", 9))
		err("%s(): error %s %dth line", __F, inbuf, linecnt);
	return strdup(&inbuf[10]);
}

static void
get_bbox(FILE *fin, struct bbox *bx)
{
	char *p;

	fill_buf(fin);
	p = inbuf;
	p = tospace(p); bx->bb_width  = atoi(p);
	p = tospace(p); bx->bb_height = atoi(p);
	p = tospace(p); bx->bb_offx   = atoi(p);
	p = tospace(p); bx->bb_offy   = atoi(p);
	return;
}

static char *
tospace(char *line)
{
	char *p;
	p = line;
	while (isspace(*p))
		p++;
	while (!isspace(*p))
		p++;
	return p;
}

char
hextoint(char *p)
{
	char	ch;

	if (!isxdigit(*p))
		err("%s(): error at %dth line\n", __F, __L);

	if (*p >= 'a')
		ch = p[0] - 'a' + 10;
	else if (*p >= 'A')
		ch = p[0] - 'A' + 10;
	else
		ch = p[0] - '0';

	return ch;
}

struct character *
bsearch4char(struct fontset *font, const int target)
{
	struct character *base, *p;
	int lim, cmp;
	unsigned int ret;
	int	tot;

	base = font->f_characters[0];
	tot = font->f_nchars;
	for (lim = tot; lim != 0; lim >>= 1) {
		p = base + (lim >> 1);
		ret = p->c_encoding;
		cmp = ret < target ? 1 : ret > target ? -1 : 0;
		if (cmp == 0)
			return p;
		if (cmp > 0) {  /* key > p: move right */
			base = (p + 1);
			lim--;
		} /* else move left */
	}
	fprintf(stderr, "%s(): can not find a character whose encoding is %d\n",
		__F, target);
	return NULL;
}

struct character *
search4charbyname(struct fontset *font, const char *myname)
{
	struct character *p;
	int	i, tot;

	tot = font->f_nchars;

	for (i=0; i<tot; i++) {
		p = font->f_characters[i];
		if (p->c_charname[0] != myname[0])
			continue;
		if (strcmp(p->c_charname, myname) == 0)
			return p;
	}
	return NULL;
}

/*
 * find the character structure whose encoding is indentical to the atoi(line).
 */ 
char *
get_hancode(struct fontset *fs, char *line, struct character **returned)
{
	char	*p;
	int	coding;
	struct character *tmp;

	coding = atoi(line);
	tmp = bsearch4char(fs, coding);
	if (tmp) {
		for (p = line; p[0] && isdigit(p[0]); p++) 
			/* void */;
		if (p[0] == ';') /* optional terminator */
			p++;
		*returned = tmp;
		return p;
	}
	*returned = NULL;
	return line;
}

#define IS_GOOD(c) (isalnum(c) || c == '_')
/*
 * find the character structure whose name is indentical to the line.
 */ 
char *
get_engcode(struct fontset *fs, char *line, struct character **returned)
{
	char	*p, *last, backup;
	struct character *tmp;

	if (!isalpha(line[0]))
		err("%s(): name %s should start as a alpha", __F, line);
	for (p = line+1; p[0] && IS_GOOD(p[0]); p++) 
		/* void */;
	last = p;
	backup = p[0];
	p[0] = 0;

	tmp = search4charbyname(fs, line);
	if (tmp) {
		if (backup == ';')
			last++;
		else
			*last = backup;
		*returned = tmp;
		return last;
	}
	*returned = NULL;

	return line;
}
