/*
 *	jFontSys.c
 *	H.Ohkubo Oct. 19, 1992
 */
#include	<exec/types.h>
#include	<exec/ports.h>
#include	<graphics/text.h>
#include	<functions.h>
#include	<stdio.h>
#include	<stdlib.h>
#include	<limits.h>
#include	<string.h>
#include	<ctype.h>
#include	"jFont.h"
#include	"jFontSys.h"
#include	"openlib.h"

struct	MsgPort	*jFontPort = NULL;

struct	TextAttr	textattr = {
	NULL,	/* set font name, later */
	FONT_SIZE,	/* font height */
	FS_NORMAL,	/* style is normal. not bold, not italic, not... */ 
	FPF_DISKFONT,	/* this is Disk font. */
	};

struct	font_cache	cache[FONT_CACHE_MAX], cache_list;

int	cache_size = FONT_CACHE_SIZE;
BOOL	denec = FALSE;
char	jfontname[FNAMELEN];

#ifdef	DEBUG
int	cache_hit = 0, cache_missed = 0;	/* for font cache effect check */
#endif


/* prototypes */
struct	TextFont	*open_font(USHORT);
struct	TextFont	*open_font_doit(char *);
VOID	insert_font(struct font_cache *, struct font_cache *);
struct	font_cache	*remove_font(struct font_cache *);
VOID	init_font_cache(VOID);
VOID	free_font_cache(VOID);
struct	MsgPort	*set_jfont(VOID);
VOID	remove_jfont(struct MsgPort *, BOOL);
VOID	get_startup(VOID);
VOID	init_default(VOID);
int	set_startup_value(FILE *);
char	*my_getline(FILE *);
int	set_cache_size(int);
VOID	clean_exit(char *, int);


VOID	main(int argc, char *argv[])
{
	struct	jFontMsg	*msg;
	int	done = FALSE;
	BOOL	fromwb = TRUE;

	if (argc != 0) {
		puts(COPYRIGHT);
		fromwb = FALSE;
	}

	if ((jFontPort = FindPort(JFONTPORT)) != NULL)
		remove_jfont(jFontPort, fromwb);
	else {
		jFontPort = set_jfont();
		if (!OpenLib())
			clean_exit("No Library !", RETURN_FAIL);
	}

	get_startup();
	init_font_cache();
	while (!done) {
		WaitPort(jFontPort);
		while (msg = (struct jFontMsg *)GetMsg(jFontPort)) {
			switch (msg->kanji) {
			case REMOVE_JFONT:
				while (msg = (struct jFontMsg *)GetMsg(jFontPort)) {
					msg->font = (struct TextFont *)NULL;
					ReplyMsg((struct Message *)msg);
				}
				done = TRUE;
				break;
			default:
				msg->font = (struct TextFont *)open_font(msg->kanji);
				ReplyMsg((struct Message *)msg);
				break;
			}
		}
	}
	free_font_cache();

	clean_exit(END_MESSAGE, RETURN_OK);
}


/* for font cache routine */
struct	TextFont	*open_font(USHORT kanji_code)
{
	int	fontnum;
	char	fontname[FNAMELEN];

	/* kanji_code is JIS kanji-code. but this is illegal case. */
	if (kanji_code == ANK_FONT_CODE)
		return cache[ANK].font;

	fontnum = ((kanji_code>>CHAR_BIT)&0xff) - ' ';	/* get font file number */
	if (fontnum >= SYM1 && fontnum <= KATA)
		return cache[fontnum].font;

	if (denec && (fontnum > KEISEN && fontnum < JIS1))	/* NEC local? or not */
		return (struct TextFont *)NULL;

	sprintf(fontname, KNJ_FONT, KANJIDIR, jfontname, fontnum);
	return open_font_doit(fontname);
}


struct	TextFont	*open_font_doit(char *fontname)
{
	struct	font_cache	*p = &cache_list;
	struct	TextFont	*font;

	for (p = p->next; p != &cache_list; p = p->next) {
		if (strcmp(fontname, p->fontname) == 0) {
#ifdef	DEBUG
			cache_hit++;
#endif
			remove_font(p);
			insert_font(&cache_list, p);
			return p->font;
		}
	}

	p = remove_font(cache_list.next);
	insert_font(&cache_list, p);
	if (p->font) {
		CloseFont(p->font);
		p->font = (struct TextFont *)NULL;
		p->fontname[0] = '\0';
	}

	textattr.ta_Name = (STRPTR)fontname;
	if ((font = OpenDiskFont(&textattr)) == NULL)
		return (struct TextFont *)NULL;
	p->font = font;
	strcpy(p->fontname, fontname);
#ifdef	DEBUG
	cache_missed++;
#endif
	return font;
}


VOID	insert_font(struct font_cache *position, struct font_cache *x)
{
	position->prev->next = x;
	x->prev = position->prev;
	x->next = position;
	position->prev = x;
}


struct	font_cache	*remove_font(struct font_cache *x)
{
	x->prev->next = x->next;
	x->next->prev = x->prev;

	return x;
}


VOID	init_font_cache(VOID)
{
	int	i;

	for (i = ANK; i <= KATA; i++) {
		if (i == ANK && *(cache[i].fontname) == '\0')
			sprintf(cache[i].fontname, ANK_FONT);
		else if (*(cache[i].fontname) == '\0')
			sprintf(cache[i].fontname, KNJ_FONT, KANJIDIR, jfontname, i);
		textattr.ta_Name = (STRPTR)cache[i].fontname;
		if ((cache[i].font = OpenDiskFont(&textattr)) == NULL) {
			free_font_cache();
			clean_exit("No Font !", RETURN_FAIL);
		}
		cache[i].next = cache[i].prev = &cache[i];
	}

	cache_list.next = cache_list.prev = &cache_list;
	for (; i < cache_size; i++) {
		cache[i].font = (struct TextFont *)NULL;
		insert_font(&cache_list, &cache[i]);
	}
}


VOID	free_font_cache(VOID)
{
	int	i;

	for (i = 0; i < cache_size; i++) {
		if (cache[i].font)
			CloseFont(cache[i].font);
	}

#ifdef	DEBUG
	printf("Cache Hit = %d\tCache missed = %d\n", cache_hit, cache_missed);
#endif
}


/* for Message port routine */
struct	MsgPort	*set_jfont(VOID)
{
	struct	MsgPort	*port;

	if ((port = CreatePort(JFONTPORT, NORMAL_PRI)) == NULL)
		clean_exit("No Port !", RETURN_FAIL);

	return port;
}


VOID	remove_jfont(struct MsgPort *jFontPort, BOOL fromwb)
{
	struct	jFontMsg	sendmsg;

	sendmsg.msg.mn_Length = sizeof(struct jFontMsg);
	sendmsg.kanji = REMOVE_JFONT;

	PutMsg(jFontPort, (struct Message *)&sendmsg);
	if (!fromwb)
		puts(REMOVE_MESSAGE);

	exit(EXIT_SUCCESS);
}


/* for startup file routine */
VOID	get_startup(VOID)
{
	FILE	*fp;

	init_default();
	if ((fp = fopen(STARTUP, READ_BINARY)) != NULL) {
		while (set_startup_value(fp) != EOF)
			;
		fclose(fp);
	}
}


int	set_startup_value(FILE *fp)
{
	char	*lp, *name_end, *value_head;
	int	i, c;

	lp = my_getline(fp);
	if ((name_end = strchr(lp, '=')) != NULL) {
		value_head = name_end + 1;
		for (i = CACHE_SIZE; i < TOTAL_STARTUP; i++) {
			if (strncmp(startup[i], lp, name_end - lp) == 0) {
				switch (i) {
				case CACHE_SIZE:
					cache_size = set_cache_size(atoi(value_head));
					break;
				case FONT_NAME:
					sprintf(cache[ANK].fontname,
						"%s.font", value_head);
					break;
				case JFONT_NAME:
					strcpy(jfontname, value_head);
					break;
				case SYM1_NAME:
				case SYM2_NAME:
				case JANK_NAME:
				case HIRA_NAME:
				case KATA_NAME:
					sprintf(cache[i - SYM1_NAME + 1].fontname,
						"%s%s.font", KANJIDIR, value_head);
					break;
				default:
					break;
				}
			}
		}
	}
	else if (strlen(lp) > 0) {
		if (strcmp(startup[DENEC], lp) == 0)
			denec = TRUE;
	}
	c = fgetc(fp);
	ungetc(c, fp);	/* for check EOF */

	return c;
}


#define	LINEBUFLEN	80
#define	LF	'\n'	/* line feed */

char	*my_getline(FILE *fp)
{
	int	c, iscomment = FALSE;
	static	char	linebuf[LINEBUFLEN], *lp;

	lp = linebuf;
	while ((c = fgetc(fp)) != LF && c != EOF) {
		if (!iscomment && isgraph(c)) {
			switch (c) {
			case COMMENT_MARK:
				iscomment = TRUE;
				break;
			default:
				if (islower(c))
					c = toupper(c);
				*lp++ = (char)c;
				break;
			}
		}
	}
	*lp = '\0';	/* terminate */

	return linebuf;
}


VOID	init_default(VOID)
{
	int	i;

	denec = FALSE;
	cache_size = FONT_CACHE_SIZE;
	strcpy(jfontname, JFONT);	/* set default kanji font name */

	for (i = ANK; i <= KATA; i++)
		*cache[i].fontname = '\0';	/* set no name */
}


int	set_cache_size(int cache_size)
{
	if (cache_size < FONT_CACHE_MIN)
		cache_size = FONT_CACHE_MIN;
	else if (cache_size > FONT_CACHE_MAX)
		cache_size = FONT_CACHE_MAX;

	return cache_size;
}


/* free all resources, and exit this program. */
VOID	clean_exit(char *mes, int exit_val)
{
	if (jFontPort)
		DeletePort(jFontPort);

	CloseLib();

	if (*mes)
		fprintf(stderr, "%s\n", mes);

	exit(exit_val);
}
