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

#ifdef  UNIX
#include <sys/stat.h>
#include <unistd.h>
#else
#include <sys\stat.h>
#include <io.h>
#ifdef MSVC
#include "msvcdir.h"
#include <memory.h>
#else
#include <dir.h>
#include <mem.h>
#endif
#endif

#include "dd.h"
#include "err.h"
#include "buffer.h"
#include "vfont.h"

#define	VIR_CASH	1		/* make the location table of characters */
#define	ONDEMAND_FONT	1
#undef	MAX_CODE

BOOL f_use_virtualfont = TRUE;
struct STACK {
	SCALED_PT h, v, w, x, y, z;
};
#define LOCALSTACK_SIZE 16
#define LONG_CHAR	242


BUFFER *get_virtualfont(FONT_INFO *, int, int *);
void local_pkbuf_flush(FONT_INFO *);
void local_font_flush(FONT_INFO *);
void local_charinfo_flush(FONT_INFO *);
long virtualfont_tfm_width(BUFFER *, int, int);
BOOL load_character_packet(BUFFER *, int, PREAMBLE *);
void pret_vf_packet(FONT_INFO *, BUFFER *, int,
					SCALED_PT, SCALED_PT, PIXEL, PIXEL);
static void make_localfont_list(FONT_INFO *);
static long get_nbyte(BUFFER **, int);
static long signed_get_nbyte(BUFFER **, int);
static SCALED_PT vf_cput(uint, FONT_INFO *, SCALED_PT, SCALED_PT, 
		PIXEL, PIXEL);
static FONT_INFO *set_localfont(FONT_INFO *, int);

/* prtinit.c */
extern DIMENSION dviout_dimension;

/* buffer.c */
FONT_INFO *font_info_alloc(void);
void *marea(unsigned int);

/* misc.c */
SCALED_PT mult20(SCALED_PT, SCALED_PT);

/* fontdef.c */
char *font_name_check(char *, uint, uint);
char *genfont_main(char *, ulong, ulong, uint, uint, uint);
void resolve_font(FONT_INFO *);
void SetFontDPI(FONT_INFO *);
extern int texpk_pos;
extern BOOL f_ondemand;
#ifdef	LBP
extern int f_use_lfont;
#endif

/* raster.c */
void raster_buf_alloc(PREAMBLE **, long);

/* getfont */
PREAMBLE *get_font(uint*, FONT_INFO *);

/* pret.c */
extern char ptex_d;	/* ptex direction: 0 = yoko, 1 = tate */
void rewrite_preamble_ptex(PREAMBLE *, uint, FONT_INFO *);
extern BOOL WriteTTFont(PIXEL, PIXEL, FONT_INFO *, uint, PREAMBLE*);


/* bitmap.c */
void write_font(PIXEL, PIXEL, PREAMBLE *);
void write_rule(PIXEL, PIXEL, PIXEL, PIXEL);

/* size.c, prtsize.c */
PIXEL sptopixel(SCALED_PT);
PIXEL vtopixel(SCALED_PT);

extern long sp_per_h, v_per_h;

static int *vir_buf;

void vir_buf_flush(void)
{
	Free0(vir_buf);
	vir_buf = NULL;
}

/* seek to the objective character packet */
BUFFER *get_virtualfont(FONT_INFO *font, int char_code, int *flag_ptr)
{
	BUFFER *pk, *pkt;
	int cmd;
	unsigned long packet_length;

#define	MAX_CODE	0x10000			/* should be a power of 2 */
#define	V_SKIP		3
#define	vp(x)		((x)&(MAX_CODE-1))

	if (font->ext.local_font == NULL)
		make_localfont_list(font);
#if	VIR_CASH
	{
		int skip, pos;
		uint code, top, last, *buf;

		if(font->vjfm == NULL){
			pk = font->pk;
			move_ptr(pk, font->last);	/* skip preamble & font defs */

			if(vir_buf == NULL){
				vir_buf = (int *)marea(MAX_CODE*sizeof(int *));
				memset(vir_buf, 0, MAX_CODE*sizeof(int *));
			}
			top = last = 0xffffffff;
			skip = 0;

			while (pos = pk - font->pk, (cmd = get_ubyte(pk)) <= LONG_CHAR) {
				if (cmd == LONG_CHAR) {					/* long form packet */
					packet_length = get_long(pk) + 4;
					code = get_long(pk);
				} else {								/* short form packet */
					packet_length = cmd + 3;
					code = get_ubyte(pk);
				}
				move_ptr(pk, (int)packet_length);	/* skip DVI commands */
				if(code > last){
					if(code - top < MAX_CODE)
						vir_buf[vp(last = code)] = pos;
					else if(!skip)
						skip = pos;
				}else if(code <= top){
					if(top == last)			/* the first character_code */
						vir_buf[vp(top = last = code)] = pos;
					else if(last - code < MAX_CODE)
						vir_buf[vp(top = code)] = pos;
					else if(!skip)
						skip = pos;
				}else
					vir_buf[vp(code)] = pos;
			}
			if(top <= last  && (top != 0xffffffff || vir_buf[MAX_CODE-1])){
				buf = (uint *)marea((last - top + (V_SKIP+1))*sizeof(uint *));
				font->vjfm = (void *)buf;
				buf[0] = top;
				buf[1] = last;
				buf[2] = skip;
				if((top & MAX_CODE) == (last & MAX_CODE)){
					memcpy(buf+V_SKIP, vir_buf+vp(top), 
						(last - top + 1)*sizeof(uint *));
					memset(vir_buf+vp(top), 0, 
						(last - top + 1)*sizeof(uint*));
				}else{
					skip = MAX_CODE - vp(top);
					memcpy(buf+V_SKIP, vir_buf+vp(top), skip*sizeof(uint *));
					memset(vir_buf+vp(top), 0, skip*sizeof(uint*));
					memcpy(buf+skip+V_SKIP, vir_buf, 
						(vp(last) + 1)*sizeof(uint *));
					memset(vir_buf, 0,
						(vp(last) + 1)*sizeof(uint *));
				}
			}else
				return NULL;
		}
		buf = (uint *)font->vjfm;
		cmd = ((uint)char_code < buf[0] || (uint)char_code > buf[1])?
			buf[2]:buf[(uint)char_code - buf[0] + V_SKIP];
		if(!cmd)
			return NULL;
		pk = font->pk + cmd;
	}
#else
	pk = font->pk;
	move_ptr(pk, font->last);	/* skip preamble & font defs */
#endif

	/* character packets */
	while ((cmd = get_ubyte(pk)) <= LONG_CHAR) {
		pkt = pk;
		if (cmd == LONG_CHAR) {					/* long form packet */
			packet_length = get_long(pk);
			if (char_code == get_long(pk)) {	/* found! */
				*flag_ptr = cmd;
				RETURN(pkt);
			}
			move_ptr(pk, 4);					/* tfm */
		} else {								/* short form packet */
			packet_length = cmd;
			if (char_code == get_ubyte(pk)) {	/* found! */
				*flag_ptr = cmd;
				RETURN(pkt);
			}
			move_ptr(pk, 3);					/* tfm */
		}
		move_ptr(pk, (int)packet_length);	/* skip DVI commands */
	}
	/* failure */
	return (NULL);
}

BUFFER *MakeLocalFontList(FONT_INFO *font, BUFFER *pk)
{
	FONT_INFO	*local_font;
	char *p;
	int cmd;

	/* font definitions */
	for (cmd = get_ubyte(pk); cmd >= FNT_DEF_1 && cmd < PRE;) {
		if (font->ext.local_font == NULL) {
			font->ext.local_font = local_font = font_info_alloc();
		} else {
			local_font->next_font = font_info_alloc();
			local_font = local_font->next_font;
		}
		local_font->font_code = get_nbyte(&pk, cmd - FNT_DEF_1 + 1);
		local_font->c = get_long(pk);
		local_font->size_para = mult20(font->size_para, get_long(pk));
		local_font->des_para = get_long(pk) >> 4;
		cmd = get_ubyte(pk);
		cmd += get_ubyte(pk);
		for (p = local_font->n; cmd > 0; cmd--)
			*p++ = get_ubyte(pk);
		*p = '\0';
		local_font->f_goth = 0;
		local_font->ext.local_font = NULL;
		local_font->font_type = ONDEMAND2;
#ifdef	USE_SUBFONT
		local_font->sfd_id = -1;
#endif
		SetFontDPI(local_font);
		if(!f_ondemand)
			resolve_font(local_font);
		cmd = get_ubyte(pk);
	}
	return pk;
}

void make_localfont_list(FONT_INFO *font)
{
	BUFFER *pk;
	int	cmd;

	pk = font->pk;
	/* preamble */
	move_ptr(pk, 2);	/* skip pre(247), id(202) */
	cmd = get_ubyte(pk);	/* size of a comment */
	move_ptr(pk, cmd + 8);	/* skip comment, check sum, design size */

	font->last = MakeLocalFontList(font, pk) - font->pk - 1;
									/* size of preamble & fon defs */
	return;
}

void local_pkbuf_flush(FONT_INFO *local_font)
{
	FONT_INFO *font;

	if (local_font == NULL) return;
	for (font = local_font; font; font = font->next_font) {
		if (font->font_type == VIRTUAL_FONT)
			local_pkbuf_flush(font->ext.local_font);
		if(font->name != NULL && font->name[0] != '<')
			font->pk = NULL;
	}
}

void local_font_flush(FONT_INFO *local_font)
{
	FONT_INFO *font, *nfont;

	if (local_font == NULL) return;
	for (font = local_font; font; ) {
		nfont = font->next_font;
		if (font->font_type == VIRTUAL_FONT){ 
			local_font_flush(font->ext.local_font);
			Free0(font->vjfm);
			font->vjfm = NULL;
		}
#ifdef	USE_ETF
		else if(font->font_type == ETF_FONT && font->etf_type == VIRTUAL_FONT)
			local_font_flush(font->ext.local_font);
#endif
		if (font->name != NULL){
			Free(font->name);
			font->name = NULL;
		}
		Free(font);
		font = nfont;
	}
}

void local_charinfo_flush(FONT_INFO *local_font)
{
	FONT_INFO *font;
	int i;

	if (local_font == NULL) return;
	for (font = local_font; font; font = font->next_font) {
		if (font->font_type == VIRTUAL_FONT
#ifdef	USE_ETF
		  || (font->font_type == ETF_FONT && font->etf_type == VIRTUAL_FONT)
#endif
		) 
			local_charinfo_flush(font->ext.local_font);
		for (i = 0; i < CHAR_ROOT; i++) 
			font->char_info[i] = NULL;
	}
}

/* read tfm width in virtual font */
long virtualfont_tfm_width(BUFFER *pk, int cmd, int cc)
{
	long tfm_width;

	if (pk == NULL) {
		tfm_width = 0;
	} else {
		if (cmd == LONG_CHAR) {
			move_ptr(pk, 8);		/* skip pl & cc */
			tfm_width = get_long(pk);
		} else {
			move_ptr(pk, 1);		/* skip cc */
			tfm_width = get_mid(pk);
		}
	}
	return (tfm_width);
}

BOOL load_character_packet(BUFFER *pk, int cmd, PREAMBLE *preamble)
{
	long packet_length;
	BUFFER *raster;

	if (pk == NULL) return (FALSE);
	if (cmd == LONG_CHAR) {				/* pl[4], cc[4], tfm[4] */
		packet_length = get_long(pk);
		move_ptr(pk, 8);			/* skip cc & tfm */
	} else {
		packet_length = cmd;			/* cc[1], tfm[3] */
		move_ptr(pk, 4);			/* skip cc & tfm */
	}
	raster_buf_alloc(&preamble, packet_length);
	preamble->byte_width = packet_length;
	for (raster = preamble->raster; packet_length > 0; packet_length--) {
		*raster++ = get_ubyte(pk);
	}
	return (TRUE);
}

void pret_vf_packet(FONT_INFO *vf, BUFFER *pkt, int pl,
					SCALED_PT h, SCALED_PT v, PIXEL H0, PIXEL V0)
{
	FONT_INFO *font;
	BUFFER *end_pkt;
	SCALED_PT w, x, y, z;
	SCALED_PT a, b;
	PIXEL A, B;
	uint cmd, c, stackp;
	struct STACK local_stack[LOCALSTACK_SIZE];
#define VF_SCALE(s) mult20(vf->size_para, s)
#define MOVE_H(d) {if (!ptex_d) h += d; else v += d;}
#define MOVE_V(d) {if (!ptex_d) v += d; else h -= d;}

	w = x = y = z = 0;
	stackp = 0;
	font = set_localfont(vf->ext.local_font, 0);			/* default font */
	for (end_pkt = pkt + pl; pkt < end_pkt;) {
		cmd = get_ubyte(pkt);
		if (cmd < SET1) {									/* set_char */
			b = vf_cput(cmd, font, h, v, H0, V0);
			MOVE_H(b);
		} else if (cmd < SET_RULE) {						/* set(n) */
			c = get_nbyte(&pkt, cmd - SET1 + 1);
			b = vf_cput(c, font, h, v, H0, V0);
			MOVE_H(b);
		} else if (cmd == SET_RULE || cmd == PUT_RULE) {	/* set/put_rule */
			a = VF_SCALE(get_long(pkt));
			b = VF_SCALE(get_long(pkt));
			if (a > 0 && b > 0){
				if (!ptex_d) {
					A = vtopixel(a + v_per_h);
					B = sptopixel(b + sp_per_h);
					write_rule(sptopixel(h) - H0, vtopixel(v) - V0, A, B);
				} else {
					A = sptopixel(a + sp_per_h);
					B = vtopixel(b + v_per_h);
					write_rule(sptopixel(h) - H0, vtopixel(v) + B - V0,
						   B, A);
				}
			}
			if (cmd == SET_RULE) {
				MOVE_H(b);
			}
		} else if (cmd < PUT_RULE) {						/* put(n) */
			c = get_nbyte(&pkt, cmd - PUT1 + 1);
			vf_cput(c, font, h, v, H0, V0);
		} else if (cmd < PUSH) {							/* nop, bop, eop */
			return;
		} else if (cmd == PUSH) {							/* push */
			if (stackp < LOCALSTACK_SIZE) {
				local_stack[stackp].h = h;
				local_stack[stackp].v = v;
				local_stack[stackp].w = w;
				local_stack[stackp].x = x;
				local_stack[stackp].y = y;
				local_stack[stackp].z = z;
				stackp++;
			}
		} else if (cmd == POP) {							/* pop */
			if (stackp > 0) {
				stackp--;
				h = local_stack[stackp].h;
				v = local_stack[stackp].v;
				w = local_stack[stackp].w;
				x = local_stack[stackp].x;
				y = local_stack[stackp].y;
				z = local_stack[stackp].z;
			}
		} else if (cmd < W0) {								/* right(n) */
			b = VF_SCALE(signed_get_nbyte(&pkt, cmd - RIGHT1 + 1));
			MOVE_H(b);
		} else if (cmd == W0) {								/* w0 */
			MOVE_H(w);
		} else if (cmd < X0) {								/* w(n) */
			w = VF_SCALE(signed_get_nbyte(&pkt, cmd - W0));
			MOVE_H(w);
		} else if (cmd == X0) {								/* x0 */
			MOVE_H(x);
		} else if (cmd < DOWN1) {							/* x(n) */
			x = VF_SCALE(signed_get_nbyte(&pkt, cmd - X0));
			MOVE_H(x);
		} else if (cmd < Y0) {								/* down(n) */
			a = VF_SCALE(signed_get_nbyte(&pkt, cmd - DOWN1 + 1));
			MOVE_V(a);
		} else if (cmd == Y0) {								/* y0 */
			MOVE_V(y);
		} else if (cmd < Z0) {								/* y(n) */
			y = VF_SCALE(signed_get_nbyte(&pkt, cmd - Y0));
			MOVE_V(y);
		} else if (cmd == Z0) {								/* z0 */
			MOVE_V(z);
		} else if (cmd < FNT_NUM_0) {						/* z(n) */
			z = VF_SCALE(signed_get_nbyte(&pkt, cmd - Z0));
			MOVE_V(z);
		} else if (cmd < FNT1) {							/* fnt_num_n */
			cmd -= FNT_NUM_0;
			font = set_localfont(vf->ext.local_font, cmd);
		} else if (cmd < XXX1) {							/* fnt(n) */
			c = get_nbyte(&pkt, cmd - FNT1 + 1);
			font = set_localfont(vf->ext.local_font, c);
		} else if (cmd < FNT_DEF_1) {						/* xxx(n) */
			c = get_nbyte(&pkt, cmd - XXX1 + 1);
			move_ptr(pkt, c);								/* skip! */
		}
	}
}

long get_nbyte(BUFFER **p, int n)
{
	long d;

	if(n--)
		d = (unsigned char)get_ubyte(*p);
	else
		return 0;
	while (n-- > 0)
		d = (d<<8) +  (unsigned char)get_ubyte(*p);
	return d;
}

long signed_get_nbyte(BUFFER **p, int n)
{
	long d;

	if(n--)
		d = (signed char)get_byte(*p);
	else
		return 0;
	while (n-- > 0)
		d = (d<<8) +  (unsigned char)get_ubyte(*p);
	return d;
}

SCALED_PT vf_cput(uint code, FONT_INFO *font,
				  SCALED_PT h, SCALED_PT v, PIXEL H0, PIXEL V0)
{
	PREAMBLE *preamble;
	PIXEL hh, vv;

	if (font == NULL) return (0);
	if (font->font_type == UNRESOLVE) return (0);
#ifdef	LBP
	{
		int f_tmp;

		f_tmp = f_use_lfont;
		f_use_lfont = FALSE;
		preamble = get_font(&code, font);
		f_use_lfont = f_tmp;
		if(preamble == NULL) return (0);
	}
#else
	if ((preamble = get_font(&code, font)) == NULL) return (0);
#endif
	if (font->font_type == VIRTUAL_FONT
#ifdef	USE_ETF
		  || (font->font_type == ETF_FONT && font->etf_type == VIRTUAL_FONT)
#endif
	) {
		pret_vf_packet(font, preamble->raster, preamble->byte_width,
					   h, v, H0, V0);
	} else{
		rewrite_preamble_ptex(preamble, code, font);
		hh = sptopixel(h) - H0;
		vv = vtopixel(v) - V0;
#ifdef	WIN32G
		if(!WriteTTFont(hh, vv, font, code, preamble))
#endif
			write_font(hh, vv, preamble);
	}
end:
	return (preamble->tfm_width);
}

FONT_INFO *set_localfont(FONT_INFO *local_font, int num)
{
	FONT_INFO *font;

	for (font = local_font; font; font = font->next_font) {
		if (font->font_code == num) {
			if(font->font_type == ONDEMAND2 || font->font_type == ONDEMAND)
				resolve_font(font);
			return (font);
		}
	}
	return (local_font);	/* ???? */
}
