/*
 *  TEX Device Driver  ver 2.00-
 *  copyright 1988, 1989 by TSG, 1990-93 by SHIMA
 *
 *  putdvi.c :
 *      3rd edition
 *
 *	modified for non PC-9801 machines by sempa 1992
 *	very slightly modified against warnings by Oh-Yeah? 25 May 1992
 */

#include <stdio.h>
#define _DEF_STDIO_H_
#include <ctype.h>
#include "dd.h"
#include "err.h"
#include "dviread.h"

#define TO_FLG     '-'
#define OPTION_FLG '-'
#define	TOP_PAGE	0x40

#define min(a,b) ( (a) < (b) ? (a) : (b) )

BOOL device_open_flag = FALSE;
static int num_scroll = 1;
static int unit_pages = 1;
int current_page;
#ifdef	DVIPRT
static int f_even = FALSE;
char *f_page_scroll;
#endif

extern BOOL dvifile_page;
extern DIMENSION dviout_dimension;

#ifdef	RAWOUT
void   pr_flush(void);
extern BOOL f_rawerr;
extern BOOL f_rawwait;
#endif

/* bitmap.c */
void bitmap_init(OUTPUT_INFO *out);

/* clrbuf.asm ( or buffer.c ) */
void clear_buf(BUF_INFO *);

/* device.c */
void device_init(DIMENSION *);
void device_end(void);
void device_clear(OUTPUT_INFO *);
NEXT_ACTION device_out(OUTPUT_INFO *, DIMENSION *);

/* pret.c */
void interpret(OUTPUT_INFO *);

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

/* stack.c */
void stack_rewind(void);

/* p_out() */
void pr_new_page(void);

/* option.c */
FILE *open_config(char *config_file);

#ifdef	DVIPRT
extern BOOL f_pause;
#endif

#ifdef	FDOWN
extern int f_download;
void d_resume(void);
#endif

#ifdef	STR_SEARCH
extern uint f_s_search;
#endif
#ifdef	HYPERTEX
extern int f_hyper;
#endif

typedef enum {
	FORWARD, REVERSE
} SearchDirection;

void put_dvi(DVIFILE_INFO *, DIMENSION *, int, char **);
void init_output(OUTPUT_INFO *);
BOOL print_page(DVIFILE_INFO *, DIMENSION *);
static void set_output_size(DIMENSION *, OUTPUT_INFO *);
static BOOL search_page(int, char **, DIMENSION *);
static int strtopage(char **, DIMENSION *, SearchDirection);

void put_dvi(DVIFILE_INFO *dvi, DIMENSION *dim, int argc, char **argv)
	/* dviファイルをコマンドラインのページ指定に従って出力．
	 */
{
	char *tmp;
	int part;
	BOOL do_print;

	ENTER("put_dvi");
#ifdef	DVIPRT
	f_even = FALSE;
	num_scroll = 1;
	for (tmp = f_page_scroll; tmp && *tmp; tmp++) {
		if (*tmp == 'e') {
			f_even = TRUE;
			goto skip_2;
		}
		if (*tmp == 'o') {
skip_2:		num_scroll = (num_scroll > 0) ? 2 : -2;
		}
		else if (*tmp == 'r')
			num_scroll = -num_scroll;
	}
#endif
	part = 0;
#ifndef	RAWOUT
	if (dim->start_page > 0 && dim->start_page <= dim->total_page) {
		if (argc > 2 && *argv[argc - 2] != OPTION_FLG)
			goto repeat;
		dim->prt_type |= TOP_PAGE;
		dim->end_page = dim->total_page;
		print_page(dvi, dim);
	}else
#endif
	{
repeat:
#ifdef	FDOWN
		if (f_download) {
			if (f_download == 1) {
				clear_buf(bitmap_buf_pointer);
				error(DIRECT, "Font counting: ");
			}
			else
				error(DIRECT, "\n");
		}
#endif
		for (do_print = search_page(argc, argv, dim); do_print;
			 do_print = search_page(argc, NULL, dim)) {
#ifdef	DVIPRT
			if ((num_scroll & 1) == 0) {
				if (f_even) {
					if ((dim->start_page & 1) != 0)
						dim->start_page++;
					if ((dim->end_page & 1) != 0)
						dim->end_page--;
				}
				else {
					if ((dim->start_page & 1) == 0)
						dim->start_page++;
					if ((dim->end_page & 1) == 0)
						dim->end_page--;
				}
			}
#endif
			if (dim->start_page > dim->end_page)
				continue;
			/* search all page-directions */
			if (
#ifdef	FDOWN
				   f_download != 1 &&
#endif
				   part++)
				pr_new_page();
			if (dim->start_page < 0)
				continue;
			if (!print_page(dvi, dim))
				break;
		}
#ifdef	FDOWN
		if (f_download++ == 1) {
			d_resume();
			goto repeat;
		}
#endif
	}
#ifdef	FDOWN
	f_download = 0;
#endif
	device_end();
	device_open_flag = FALSE;

	END();
}

static int strtopage(char **str_ptr, DIMENSION *dim, SearchDirection dir)
	/* string to integer */
{
	char *ptr;
	int page = 0;
	int i, num;

	ENTER("strtopage");

	ptr = *str_ptr;
	while (isdigit(*ptr))
		page = page * 10 + (*ptr++ - '0');

	*str_ptr = ptr;

	if (dvifile_page) {
		for (i = 1; i <= dim->total_page; i++) {
			num = (dir == REVERSE) ? (dim->total_page - i + 1) : i;
			if (dim->page_index[num].number == page)
				RETURN(num);
		}
		error(
#ifdef	WIN32
			ILLEGAL_ARGS,
#else
			PROGRAM_STOP, 
#endif
			"No such a page : dvifile-page : %d", page);
		RETURN(-2);
	}
	RETURN(page);
}

#ifdef	DVIPRT
static int getnum(int *vz, char **pt)
{
	char ch, *s;
	int num, tmp, flag;

	for( num = flag = 0, s = *pt; ; ){
		if(isdigit(ch = *s )){
			tmp = ch - '0';
			while(isdigit(ch = *++s))
				tmp = tmp * 10 + ch - '0';
nxt:		if(flag){
				num -= tmp;
				flag = 0;
			}
			else
				num += tmp;
		}
		else if(ch >= 'v' && ch <= 'z'){
			s++;
			tmp = vz[ch - 'v'];
			goto nxt;
		}else if(ch == '-'){
			flag = 1;
cnt:		s++;
			continue;
		}else if(ch == '+'){
			goto cnt;
		}else if(ch == ',' || ch == ';' || ch == '[' || ch == ']' || !ch){
			*pt = s;
			return num;
		}else
			return 0x7000;
	}
}

static char *setnum(int *vz, char *s)
{
	int	ch, i, j;

	if(*s == ';')
		return s+1;
	while((ch = *s++) >= 'v' && ch <= 'z'){
		i = *s++;
		if(*s++ != '=' || (j = getnum(&vz[0], &s)) == 0x7000)
err:		return NULL;
		ch -= 'v';
		switch (i){
			case '+':
				vz[ch] += j;
				break;

			case '-':
				vz[ch] -= j;
				break;

			case '*':
				vz[ch] *= j;
				break;

			case '/':
				vz[ch] /= j;
				break;

			case '&':
				vz[ch] &= j;
				break;

			default:
				goto err;
		}
		switch(*s++){
			case ',':
				continue;

			case ';':
				return s;

			default:
				goto err;
		}
	}
	return NULL;
}


#endif

static BOOL search_page(int argc, char **argv, DIMENSION *dim)
	/* search pages directed in command-line from dvi-file */
{
#define	MAX_MACRO	512
	static char **page_dir = NULL;
	static int count = 0;
	int i;
	char *str;
#ifdef	DVIPRT
	static char *pt_top;
	static char *pt_loop;
	static char *pt_macro;
	static char *pt_wait;
	static int x, y;
	static int vz[5];
	static int f_depth;
	static char macro_buf[MAX_MACRO];
	static FILE *fp_page;
	static uchar *in_mac;
	char page_buf[256];
	int	ch, j;
#endif

	ENTER("search_page");

	if (argv != NULL) {
#ifdef	DVIPRT
		f_depth = 
#endif
		count = 0;
#ifdef	WIN32
		dvifile_page = FALSE;
#endif

		for (i = 1; i < argc; i++)
			/* Skip options */
			if (*argv[i] != OPTION_FLG) {
				page_dir = &argv[i];
				count = i;
				break;
			}
		/* Now, 'page_dir' directs ptr-ptr of dvi-filename */
		if (count == argc - 1) {
			/* When no page-directions... */
			dim->start_page = 1;
			dim->end_page = dim->total_page;
			RETURN(TRUE);
		}
#ifdef	DVIPRT
		if(in_mac)
			in_mac = NULL;
		if(fp_page)
			goto f_end;
	}
rep:
	if(f_depth > 0){
in_mac0:for(;;){					/* Macro for pages  */
			switch(*pt_macro++){
				case '[':				/* begin block */
					f_depth = 2;
					break;

				case ']':				/* end block */
					f_depth = 1;
					pt_wait = NULL;
					break;

				case ',':				/* next number */
					break;

				case  0:				/* End of one loop */
					if((pt_macro = setnum(&vz[0], pt_loop)) == NULL)
						goto errm;
					vz[4] -= 1;

				case ';':				/* check END of MACRO */
					if(vz[4] > 0)
						break;
					f_depth = -2;
					goto rep;			/* END of MACRO */

				case 'Z':
					str = pt_macro - 1;
					if(*pt_macro == '+' || *pt_macro == '-')
						pt_macro++;
					goto ps;

				case '.':				/* output a NULL page */
nl:					if(f_depth != 2)
						goto nul;
					if(!pt_wait)
						pt_wait = pt_macro;
					break;

				default:				/* get page number */
					pt_macro--;
					if((i = getnum(&vz[0], &pt_macro)) == 0x7000)
						goto errm;
					if(i < x || i > y)
						goto nl;
					if(pt_wait){
						f_depth = 1;
						pt_macro = pt_wait;
						pt_wait = NULL;
						goto nul;
					}
					dim->start_page = dim->end_page = i;
					RETURN(TRUE);
			}
		}
	}

	if (fp_page || in_mac){
		while((ch = (fp_page)?fgetc(fp_page):(*in_mac++)) != '-' && ch != '.' && ch != 'Z'
		  && ch != 'M' && ch != 'P' && ch != 'Q' && ch != '@' && (ch < '0' || ch > '9')){
			if( ch == '#' ){
				while((ch = (fp_page)?fgetc(fp_page):(*in_mac++)) != '\n' && ch != EOF && ch);
			}
			if(ch == EOF || !ch){
				if( fp_page ){
f_end:				fclose(fp_page);
					fp_page = NULL;
				}else
					in_mac = NULL;
				goto rep;
			}
		}
		if(f_depth < 0){
			str = &page_buf[0];
			j = 255;
		}else{
			str = &macro_buf[0];
			j = MAX_MACRO-1;
		}
		for(i=0; i<j; ){
			str[i++] = ch;
			if( (ch = (fp_page)?fgetc(fp_page):(*in_mac++)) <= ' ' ){
				str[i] = 0;
				goto pg;
			}
		}
errp:	
#ifdef	WIN32
		error(ILLEGAL_ARGS, "Bad page direction %s", str);
#else
		error(FILE_FAULT, "Bad page direction %s", str);
#endif
		RETURN(FALSE);
#endif
	}
	if (++count >= argc){
		/* When all page-directions are over */
// #ifdef	DVIPRT
		if(f_depth == -1){
			dim->start_page = 1;
			dim->end_page = dim->total_page;
			goto d1;
		}
// #endif
		RETURN(FALSE);
	}

	str = *++page_dir;				/* next argv[] */
pg:
#ifdef	DVIPRT
	if(*str == '@'){
		if(fp_page)
			goto errp;
		if((fp_page = fopenf(str+1, "r")) == NULL
		  && (fp_page = open_config(str)) == NULL){
			error(NO_FILE, "%s", str+1);
			dim->start_page = -2;
			return FALSE;
		}
		goto rep;
	}
	if(*str == 1){
		in_mac = str+1;
		++str;
		goto rep;
	}
	if(*str == 'Q'){
false:
		if(fp_page)
			fclose(fp_page);
		fp_page = NULL;
		RETURN(FALSE);
	}
	if(*str == '.'){
nul:	dim->start_page = -1;
		dim->end_page = 0;
		RETURN(TRUE);
	}
#ifdef	WIN32
	if(*str == 'P'){
		dvifile_page = TRUE;
		goto rep;
	}
#endif
	if(*str == 'Z'){
ps:		switch(str[1]){
			case '+':
				f_pause = TRUE;
				break;
			case '-':
				f_pause = FALSE;
				break;
			default:
				f_pause = (f_pause == TRUE)?FALSE:TRUE;
				break;
		}
		goto rep;
	}
	if(*str == 'M'){
		f_depth = (*(pt_top = str+1))?-1:0;
		goto rep;
	}
#endif
	dim->start_page = (*str == TO_FLG) ? 1 : strtopage(&str, dim, FORWARD);
	if (*str == '+') {
		dim->prt_type |= TOP_PAGE;
		str++;
		goto skip;
	}
	if (*str++ == TO_FLG)
skip:	dim->end_page = (*str == '\0') ?
			dim->total_page : strtopage(&str, dim, REVERSE);
	else
		dim->end_page = dim->start_page;
	if(dim->start_page == -2 || dim->end_page == -2)
		goto false;
#ifdef	DVIPRT
	if(f_depth < 0){
d1:		vz[0] = vz[1] = 0;
		x = vz[2] = dim->start_page;
		y = vz[3] = dim->end_page;
		vz[4] = y - x + 1;
		if((pt_macro = setnum(&vz[0], pt_top)) == NULL){
errm:		error(ILLEGAL_ARGS, "Page Macro %s", pt_loop);
			goto false;
		}
		f_depth = 1;
		pt_loop = pt_macro;
		while(*pt_macro && *pt_macro != ';')
			pt_macro++;
		goto in_mac0;
	}
#endif
	RETURN(TRUE);
}

int GetNextPages(int argc, char **argv, DIMENSION *dim, int *mode)
{
	if(argv)
		f_pause = 0;
	if(!search_page(argc, argv, dim))
		return 0;
	*mode = f_pause;
	return dim->start_page;
}

BOOL print_page(DVIFILE_INFO *dvi, DIMENSION *dim)
	/* print pages between start_page and end_page in DIMENSION */
{
	OUTPUT_INFO output;
	BOOL part_printing, do_next_part;
	int page;
	BOOL last_part = FALSE;

	ENTER("print_page");
	output.dvifile_ptr = dvi->file_ptr;
	output.print_direction = dim->print_direction;
	output.bitmap_ptr = (HUGE_BUF *)bitmap_buf_pointer->start;
	page = (num_scroll > 0) ? dim->start_page : dim->end_page;

	if ((dim->prt_type & TOP_PAGE) != 0)
		dim->start_page = 1;

	for (;;) {
		if (page < 1 || page > dim->total_page)
			error(WARNING, "No such a page: %d", page);

		output.page_start_offset = (dim->page_index)[page].offset;
		output.page = page;
		output.split = (last_part)?dim->split:1;
		last_part = FALSE;

#if	defined(DVIPRT)
		error(DIRECT, "[%d]", output.page);
#endif
		do {
#if	!defined(WIN32G)||defined(RAWOUT)
			if(device_open_flag == FALSE){
				if(!(dim->prt_type & NO_PRN_OUT))
					device_open_flag = TRUE;
				device_init(dim);
			}
#else
			device_init(dim);
#endif
# ifdef RAWOUT
			if(f_rawerr)
				return FALSE;
# endif
			set_output_size(dim, &output);
			init_output(&output);
			/* initializing */
#ifdef	FDOWN
			if (f_download == 1) {
				interpret(&output);
				part_printing = do_next_part = FALSE;
				goto next_part;
			}
#endif
#ifdef	STR_SEARCH
			if(!f_s_search)
#endif
#ifdef	HYPERTEX
				if(!(f_hyper & 0x100))
#endif
			clear_buf(bitmap_buf_pointer);
#ifdef	RAWOUT
			pr_flush();
			f_rawwait = TRUE;
#endif
			interpret(&output);
#ifdef	RAWOUT
			pr_flush();
			f_rawwait = FALSE;
#endif
			if(dim->prt_type & NO_PRN_OUT)
				return(TRUE);
			/* interpret dvi-file */
			device_clear(&output);

			/* printing the page and split */
			for (;;) {
				part_printing = TRUE;
				do_next_part = FALSE;
				switch (device_out(&output, dim)) {
				  case NextPage:
					  if (unit_pages < 0) {
						  unit_pages = -unit_pages;
						  goto prev_page;
					  }
					  if (page < dim->end_page) {
						  do_next_part = TRUE;
						  page = min(page + unit_pages, dim->end_page);
					  }
					  part_printing = FALSE;
					  break;
				  case PreviousPage:
prev_page:			  if (page > dim->start_page) {
						  do_next_part = TRUE;
						  page = max(page - unit_pages, dim->start_page);
					  }
					  part_printing = FALSE;
					  break;
				  case NextPart:
					  if (output.split < dim->split) {
						  output.split++;
						  do_next_part = TRUE;
					  }
					  else {
#ifndef	DVIPRT
						  if (page < dim->end_page)
#endif
						  {
next_part:					  page += num_scroll;
							  if (page <= dim->end_page
								  && page >= dim->start_page) {
								  do_next_part = TRUE;
								  part_printing = FALSE;
							  }
						  }
					  }
					  break;
				  case PreviousPart:
					  part_printing = TRUE;
					  if (output.split > 1) {
						  do_next_part = TRUE;
						  output.split--;
					  }
					  else{
						  if (page > dim->start_page){
							  page -= num_scroll;
							  if (page <= dim->end_page
								  && page >= dim->start_page) {
								  last_part = do_next_part = TRUE;
								  part_printing = FALSE;
							  }
						  }
					  }
					  break;
				  case NextPageDirection:
					  RETURN(TRUE);
				  case EscapeQuit:
					  RETURN(FALSE);
				}
				unit_pages = 1;
				if (!do_next_part) {
#ifdef FDOWN
					if (f_download != 1)
#endif
						error(DIRECT,"\007");
#ifdef DVIPRT
					RETURN(TRUE);
#endif
				}
				else
					break;
			}
		} while (part_printing);
#ifdef FDOWN
		if (f_download != 1)
#endif
			pr_new_page();
	}
}

void init_output(OUTPUT_INFO *output)
{
	fseek(output->dvifile_ptr, output->page_start_offset, SEEK_SET);
	/* ページの先頭に持っていく．*/
	stack_rewind();
	bitmap_init(output);
	current_page = output->page;
#if	0
	error(Warning, "buffer %p, width %d, heigth %d, byte_width %d",
		output->bitmap_ptr, output->width, output->height, output->byte_width);
#endif
}

static void set_output_size(DIMENSION *dim, OUTPUT_INFO *out)
{
	ENTER("set_output");

	if (out->print_direction == HORIZONTAL) {
		out->h_0 = -dim->x_offset;
		out->width = dim->buf_width;
		out->v_0 = (out->split - 1) * dim->buf_height;
		out->height = min(dim->buf_height, dim->text_height - out->v_0);
		out->v_0 -= dim->y_offset;
	}
	else {
		out->h_0 = (out->split - 1) * dim->buf_width;
		out->width = min(dim->buf_width, dim->text_height - out->h_0);
		out->h_0 -= dim->y_offset;
		out->v_0 = -dim->x_offset;
		out->height = dim->buf_height;
	}

	out->byte_width = (out->width + 7) / 8;
	out->byte_height = (out->height + 7) / 8;

	END();
}

/* end of file : putdvi.c */
