/*
 *	jmore.c
 *	H.Ohkubo Oct. 4, 1992
 *
 *	Modified Oct. 17, 1992 (Ver.0.02)
 *	Modified 0ct. 22, 1992 (Ver.0.03)
 */

#include	<exec/types.h>
#include	<exec/ports.h>
#include	<intuition/intuitionbase.h>
#include	<graphics/gfxbase.h>
#include	<workbench/startup.h>
#include	<functions.h>
#include	<stdio.h>
#include	<stdlib.h>
#include	<limits.h>
#include	<string.h>
#include	<ctype.h>
#include	"jctype.h"
#include	"openlib.h"
#include	"jFont.h"
#include	"jmore.h"

struct	Screen	*Scrn = NULL;
struct	Window	*Wind = NULL;
struct	RastPort	*rport = NULL;
int	current_x, current_y;

/* for backcscroll */
USHORT	current_line, top_line;
SHORT	count[USHRT_MAX];

FILE	*infile = NULL;
char	in_fname[FNAMELEN];

struct	TextFont	*current_font = NULL;
struct	TextFont	*resident_font[KATA + 1];

BPTR	olddir = NULL;

int	tabspc = TABSPC;
BOOL	regular = FALSE;

struct	MsgPort	*jMorePort = NULL;
struct	jFontMsg	fontMsg;

/* prototypes */
VOID	print_file(VOID);
int	print_file_doit(VOID);
VOID	handle_ank_code(char *);
VOID	handle_kanji_code(char *);
VOID	handle_ctrl_code(int);
char	*strchr_r(char *, int);
int	keyin_loop(LONG *, int, char *);
int	print_message(char *);
int	handle_rawkey(VOID);
int	count_bytes(LONG *, int);
VOID	fseek_back(int);
VOID	init_myMsg(VOID);
struct	MsgPort	*init_myport(VOID);
struct	TextFont	*get_font(USHORT);
struct	TextFont	*init_resident_font(VOID);
VOID	OpenScrnWind(VOID);
FILE	*fset(char *, char *);
VOID	clean_exit(char *, int);
VOID	usage(VOID);
VOID	get_startup(VOID);
int	set_startup_value(FILE *);
char	*my_getline(FILE *);


VOID	main(int argc, char *argv[])
{
	if (argc == 0) {
		struct	WBStartup	*argmsg = (struct WBStartup *)argv;
		struct	WBArg	*wb_arg = argmsg->sm_ArgList;

		if (argmsg->sm_NumArgs < 2)
			exit(EXIT_FAILURE);
		if (wb_arg[1].wa_Lock != NULL)
			olddir = CurrentDir(wb_arg[1].wa_Lock);
		strcpy(in_fname, wb_arg[1].wa_Name);
	}
	else {
		if (argc < 2)
			usage();
		puts(COPYRIGHT);
		strcpy(in_fname, argv[1]);
	}
	infile = (FILE *)fset(in_fname, READ_BINARY);

	get_startup();
	init_myMsg();
	if (!OpenLib())
		clean_exit("No Library !", RETURN_FAIL);
	OpenScrnWind();	/* initialize jmore's screen */

	print_file();

	clean_exit(END_MESSAGE, RETURN_OK);
}


#define	tabwidth(w)	(tabspc*(w))
#define	tabalign(x, w)	(((x)/tabwidth(w))*tabwidth(w) + tabwidth(w))

char	pool[BUFFSIZE + 1];
char	strbuff[STRBUFSIZE];

VOID	print_file(VOID)
{
	current_font = init_resident_font();
	SetFont(rport, current_font);
	SetAPen(rport, 1);
	SetDrMd(rport, JAM2);

	current_x = Wind->BorderLeft;
	current_y = Wind->BorderTop + rport->TxBaseline;
	Move(rport, current_x, current_y);

	current_line = top_line = 0;
	count[current_line] = 0;

	while (print_file_doit())
		;
}


int	print_file_doit(VOID)
{
	int	len, remaining_size = 0, n;
	char	*buff, *buff_tail, *head, *next_head, *p;
#if	0
	char	*line_head, *line_tail;
#endif

	pool[0] = pool[BUFFSIZE] = LF;
	buff = pool + 1;
	buff_tail = pool + BUFFSIZE;

	head = buff;
	while ((len = fread(head, sizeof(char), buff_tail - head, infile)) > 0) {
		next_head = strchr_r(head + len - 1, LF) + 1;
		p = buff;
		while (p < next_head) {
			count[current_line]++;
			switch (nthmsctype(buff, p - buff)) {
			case CT_KJ2:
				handle_kanji_code(p);
				break;
			case CT_KJ1:
				break;
			case CT_ANK:
				handle_ank_code(p);
				break;
			case CT_ILGL:
			default:
				handle_ctrl_code((int)(*p));
				break;
			}
			p++;
			if (current_y > (Wind->Height - FONT_SIZE)) {
				switch (n = keyin_loop((VOID *)&p, p - buff, MORE_MES)) {
				case QUIT:
					return FALSE;
					break;
				case NORMAL:
					break;
				default:	/* back scroll */
					fseek_back(len + remaining_size + n);
					return TRUE;
					break;
				}
			}
		}
		remaining_size = len - (next_head - head);
		memmove(buff, next_head, remaining_size);
		head = buff + remaining_size;
	}
	if ((n = keyin_loop((VOID *)&buff, 0, FILE_END_MES)) > 0) {
		fseek_back(len + remaining_size + n);
		return TRUE;
	}

	return FALSE;
}


VOID	handle_ank_code(char *code)
{
	if (current_font != resident_font[ANK]) {
		Text(rport, strbuff, strlen(strbuff));
		strbuff[0] = NUL;
		current_font = resident_font[ANK];
		SetFont(rport, current_font);
		current_x = rport->cp_x;
	}

	if ((current_x += current_font->tf_XSize) >= Wind->Width)
		handle_ctrl_code(LF);
	strncat(strbuff, code, sizeof(char));
}


VOID	handle_kanji_code(char *p)
{
	USHORT	kanji;
	int	fontnum;
	char	code;
	struct	TextFont	*tf;

	kanji = (*(p - 1)&0xff)<<CHAR_BIT | *p&0xff;
	kanji = mstojis(kanji);
	fontnum = ((kanji>>CHAR_BIT)&0xff) - SPC;
	code = (char)(kanji&0xff);

	if (fontnum >= SYM1 && fontnum <= KATA) {
		if (regular && (isjalnum(kanji) || isjsymbol(kanji))) {
			int	ascii_code;

			if ((ascii_code = jistoascii(kanji))) {
				tf = resident_font[ANK];
				code = (char)ascii_code;
			}
			else
				tf = resident_font[fontnum];
		}
		else
			tf = resident_font[fontnum];
	}
	else {
		if ((tf = get_font(kanji)) == NULL) {
			tf = resident_font[SYM1];
			code = KNJ_ILGL;
		}
	}
	if (current_font != tf) {
		Text(rport, strbuff, strlen(strbuff));
		strbuff[0] = NUL;
		current_font  = tf;
		SetFont(rport, current_font);
		current_x = rport->cp_x;
	}

	if ((current_x += current_font->tf_XSize) >= Wind->Width)
		handle_ctrl_code(LF);
	strncat(strbuff, &code, sizeof(char));
}


VOID	handle_ctrl_code(int code)
{
	Text(rport, strbuff, strlen(strbuff));
	strbuff[0] = NUL;

	switch (code) {
	case LF:
		current_x = Wind->BorderLeft;
		current_y += FONT_SIZE;
		Move(rport, current_x, current_y);
		count[++current_line] = 0;
		break;
	case TAB:
		current_x = tabalign(rport->cp_x, resident_font[ANK]->tf_XSize);
		Move(rport, current_x, current_y);
		break;
	default:
		break;
	}
}


char	*strchr_r(char *str, int c)
{
	while (*str != c)
		str--;

	return str;
}


#define	forward_scroll(rport)	(ScrollRaster((rport), 0, FONT_SIZE, \
				Wind->BorderLeft, Wind->BorderTop, \
				Wind->Width, Wind->Height - FONT_SIZE - 1))
#define	backward_scroll(rport)	(ScrollRaster((rport), 0, -FONT_SIZE, \
				Wind->BorderLeft, Wind->BorderTop, \
				Wind->Width, Wind->Height - FONT_SIZE - 1))
#define	clear_screen(rport)	(SetRast((rport), 0))

int	keyin_loop(LONG *point, int nbyte, char *mes)
{
	int	retval = NORMAL;

	switch (print_message(mes)) {
	case QUIT_PRG:
		retval = QUIT;
		break;
	case LINE_SCROLL:
		current_y -= FONT_SIZE;
		Move(rport, current_x, current_y);
		forward_scroll(rport);
		top_line++;
		break;
	case BACK_LINE:
		current_y = Wind->BorderTop + rport->TxBaseline;
		Move(rport, current_x, current_y);
		if (top_line > 0) {
			backward_scroll(rport);
			top_line--;
		}
		retval = count_bytes(point, nbyte);
		break;
	case BACK_SCROLL:
		current_y = Wind->BorderTop + rport->TxBaseline;
		Move(rport, current_x, current_y);
		clear_screen(rport);
		top_line =  (top_line > TOTAL_LINES) ?
			top_line - TOTAL_LINES : 0;
		retval = count_bytes(point, nbyte);
		break;
	case PAGE_SCROLL:
	default:
		current_y = Wind->BorderTop + rport->TxBaseline;
		Move(rport, current_x, current_y);
		clear_screen(rport);
		top_line = current_line;
		break;
	}

	return	retval;
}


int	print_message(char *mes)
{
	struct	TextFont	*tf = current_font;
	int	x, y;
	char	strbuf[STRBUFSIZE];

	x = Wind->BorderLeft;
	y = Wind->Height - FONT_SIZE + rport->TxBaseline;
	Move(rport, x, y);

	current_font = resident_font[ANK];
	SetFont(rport, current_font);

	sprintf(strbuf, mes, in_fname);
	SetDrMd(rport, JAM2|INVERSVID);
	Text(rport, strbuf, strlen(strbuf));
	SetDrMd(rport, JAM2);

	current_font = tf;
	SetFont(rport, current_font);

	return handle_rawkey();
}


int	handle_rawkey(VOID)
{
	BOOL	done = FALSE;
	int	retval;
	struct	IntuiMessage	*msg;

	while (!done) {
		WaitPort(Wind->UserPort);
		while (msg = (struct IntuiMessage *)GetMsg(Wind->UserPort)) {
			ReplyMsg((struct Message *)msg);
			switch (msg->Class) {
			case RAWKEY:
				switch (msg->Code) {
				case QUIT_PRG:
				case LINE_SCROLL:
				case BACK_LINE:
				case PAGE_SCROLL:
				case BACK_SCROLL:
					retval = msg->Code;
					done = TRUE;
				default:
					break;
				}
				break;
			default:
				break;
			}
		}
	}

	return retval;
}


int	count_bytes(LONG *point, int nbyte)
{
	int	sum = 0;

	do {
		sum += count[--current_line];
	} while (current_line != top_line);

	count[current_line] = 0;

	if (sum > nbyte)
		return sum - nbyte;
	else {
		*point -= sum;
		return NORMAL;
	}
}


VOID	fseek_back(int nbyte)
{
	if (fseek(infile, -nbyte, SEEK_CUR) != 0)
		clean_exit("fseek error !", RETURN_FAIL);
}


/* initialize the jFontMsg structure */
VOID	init_myMsg(VOID)
{
	struct	jFontMsg	*msg = &fontMsg;

	msg->msg.mn_Node.ln_Type = NT_MESSAGE;
	msg->msg.mn_Length = sizeof(struct jFontMsg);
	msg->msg.mn_ReplyPort = init_myport();
}


/* initialize the MsgPort for ReplyMsg */
struct	MsgPort	*init_myport(VOID)
{
	if (FindPort(JMOREPORT) == NULL) {
		if ((jMorePort = CreatePort(JMOREPORT, NORMAL_PRI)) != NULL)
			return jMorePort;
	}
	clean_exit("jMore is already used.", RETURN_FAIL);

	return (struct MsgPort *)NULL;	/* but not reached here */
}


struct	TextFont	*get_font(USHORT kanji)
{
	struct	MsgPort	*mPort;
	struct	jFontMsg	*msg = &fontMsg;

	msg->kanji = kanji;
	Forbid();
	if ((mPort = FindPort(JFONTPORT)) == NULL)
		clean_exit("jFontSys is not installed !", RETURN_FAIL);
	PutMsg(mPort, (struct Message *)msg);
	Permit();

	WaitPort(jMorePort);
	msg = (struct jFontMsg *)GetMsg(jMorePort);

	return msg->font;
}


struct	TextFont	*init_resident_font(VOID)
{
	int	i;

	for (i = SYM1; i <= KATA; i++)
		resident_font[i] = get_font((USHORT)((i + ' ')<<CHAR_BIT));

	return (resident_font[ANK] = get_font((USHORT)ANK_FONT_CODE));
}


/* ScrnDefn & WindDefn is declere in jmore.h */
VOID	OpenScrnWind(VOID)
{
	if ((Scrn = (struct Screen *)OpenScreen(&ScrnDefn)) == NULL)
		clean_exit("No Screen !", RETURN_FAIL);
	ShowTitle(Scrn, FALSE);

	WindDefn.Screen = Scrn;
	if ((Wind = (struct Window *)OpenWindow(&WindDefn)) == NULL)
		clean_exit("No Window !", RETURN_FAIL);

	rport = Wind->RPort;
}


FILE	*fset(char *fname, char *mode)
{
	FILE	*fp;

	if ((fp = fopen(fname, mode)) == NULL)
		clean_exit(strcat(fname, " not open !"), RETURN_FAIL);

	return fp;
}


VOID	clean_exit(char *mes, int extval)
{
	if (Wind)
		CloseWindow(Wind);
	if (Scrn)
		CloseScreen(Scrn);
	CloseLib();

	if (jMorePort)
		DeletePort(jMorePort);

	if (infile)
		fclose(infile);

	if (olddir)
		CurrentDir(olddir);

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

	exit(extval);
}


VOID	usage(VOID)
{
	puts(ERROR_MESSAGE);
	exit(EXIT_FAILURE);
}


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

	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	c;

	lp = my_getline(fp);
	if ((name_end = strchr(lp, '=')) != NULL) {
		value_head = name_end + 1;
		if (strncmp(startup[TAB_WIDTH], lp, name_end - lp) == 0) {
			tabspc = atoi(value_head);
		}
	}
	else if (strlen(lp) > 0) {
		if (strcmp(startup[REGULAR], lp) == 0) {
			regular = TRUE;
		}
	}
	c = fgetc(fp);
	ungetc(c, fp);	/* for check EOF */

	return c;
}


#define	LINEBUFLEN	80

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 = NUL;

	return linebuf;
}
