/*
 *  abc2ps: a program to typeset tunes written in abc format using PostScript
 *  Copyright (C) 1996,1997  Michael Methfessel
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  The author can be contacted as follows:
 *  
 *  Michael Methfessel
 *  msm@ihp-ffo.de
 *  Institute for Semiconductor Physics, PO Box 409,
 *  D-15204 Frankfurt (Oder), Germany
 *
 * Modified by Jean-François Moine, 1998
 * mailto:Jean-Francois.Moine@bst.bsf.alcatel.fr
 *
 * Modified by John Atchley, 1999-2000
 * mailto:john@guitarnut.com
 */

/* Main program abc2ps.c */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <string.h>
#include <ctype.h>
#ifdef _MSVC
#include <process.h>
#endif
#include "abc2ps.h"

/* ----- global variables ------- */

int db=DEBUG_LV;	   /* debug control */

struct ISTRUCT info,default_info;
struct SYMBOL *sym;		/* (points to the symbols of the current voice) */

struct FORMAT sfmt;		/* format after initialization */
struct FORMAT dfmt;		/* format at start of tune */
struct FORMAT cfmt;		/* current format for output */

char fontnames[50][STRLFMT];	/* list of needed fonts */
int  nfontnames;

char mbf[501];			/* mini-buffer for one line */
float bposy;			/* current position in buffered data */
int use_buffer;			/* 1 if lines are being accumulated */
char *text[NTEXT];		/* pool for history, words, etc. lines */
int text_type[NTEXT];	/* type of each text line */
int ntext;				/* number of text lines */
int FixEols;				/* Fix line breaks in source files when 1 */

char page_init[201];	/* initialization string after page break */
int do_mode;			/* control whether to do index or output */
int linenum;			/* current line number in input file */
int tunenum;			/* number of current tune */
int tnum1,tnum2;
int numtitle;			/* how many titles were read */
int nsym;			/* number of symbols in line */
int pagenum;			/* current page in output file */
int xrefnum;			/* xref number of current tune */

int bagpipe;			/* switch for HP mode */
int within_tune, within_block;	/* where we are in the file */
int do_this_tune;		/* are we typesetting the current one ? */
float posx,posy;		/* overall scale, position on page */
int transpose;

int insert_btype,insert_num;	/* needed to split bars across lines */

int vb, verbose;		/* verbosity, global and within tune */
int in_page=0;

				/* switches modified by flags: */
int gmode;			/* switch for glue treatment */
int include_xrefs;		/* to include xref numbers in title */
int one_per_page;		/* new page for each tune ? */
int pagenumbers;		/* write page numbers ? */
int write_history;		/* write history and notes ? */
int interactive;		/* interactive mode ? */
int help_me;			/* need help ? */
int select_all; 		/* select all tunes ? */
int epsf;			/* for EPSF postscript output */
int choose_outname;		/* 1 names outfile w. title/fnam */
int break_continues;		/* ignore continuations ? */
int search_field0;		/* default search field */

char in_file[MAXINF][STRL1];	/* list of input file names */
int  ninf;			/* number of input file names */

char outf[STRL1];		/* output file name */
char infostr[STRL1];		/* title string in PS file */

int  file_open; 		/* for output file */
int  file_initialized;		/* for output file */
FILE *fout;			/* for output file */
int nepsf;			/* counter for epsf output files */

char sel_str[MAXINF][STRL1];	/* list of selector strings */
int  s_field[MAXINF];		/* type of selection for each file */
int  psel[MAXINF];		/* pointers from files to selectors */

static int do_interactive(char *bbb);

static BOOL ConvertEol(char *AbcFile) {
	FILE	*in, *out;
	int		i;
	char	c, lastc = '\0';
	BOOL	LastWasEOL = FALSE, Completed;
	char	*BkFile;
	
	if (vb > 0) printf("Converting line breaks in [%s]...\n", AbcFile);
	BkFile = (char *)malloc(strlen(AbcFile) + 8);
	if (!BkFile) return FALSE;
	strcpy(BkFile, AbcFile);
	strcat(BkFile, "~");
	remove(BkFile);
	if (rename(AbcFile, BkFile)) return FALSE;
	/* The file has been renamed with a "~" extension. */
	if (vb > 0) printf("   ...file has been backed up to [%s]...\n", BkFile);
	in = fopen(BkFile, "rb");
	if (!in) return FALSE;
	out = fopen(AbcFile, "wt");
	if (!out) {
		fclose(in);
		return FALSE;
	}
	/* Files are open, copy, correcting linebreaks for this platform */
	do {
		i = getc(in);
		if (i == EOF) break;
		c = i;
		if ((c == 13) || (c == 10) || (c == '\n')) {
			if (LastWasEOL && (c != lastc)) {
				/* drop this character in the bit bucket. */
				LastWasEOL = FALSE;
			} else {
				LastWasEOL = TRUE;
				/* convert character to platform linefeed sequence */
				i = putc('\n', out);
				if (i == EOF) break;
			}
		} else {
			LastWasEOL=FALSE;
			i = putc(c, out);
			if (i == EOF) break;
		}
		lastc = c;
	} while (i != EOF);
	Completed = feof(in);
	fclose(out);
	fclose(in);
	free(BkFile);
	if ((vb > 0) && Completed) printf("   ...conversion completed.\n");
	return Completed;
}

/* ----- start of main ------ */
int main(int argc,
	 char *argv[])
{
	char bbb[501], ext[41];
	char xref_str[STRL1], pat[30][STRL1];
	int isel, j, npat, search_field, i;
	FILE *fin;			/* input file */
	char		*p, *pf, chrtime[32];
	time_t		ltime;
	struct tm	*stime;
	const char	cMonths[12][12] = { "January", "February", "March", "April", "May", "June",
							    	"July", "August", "September", "October", "November", "December" };

	/* --------- Clear the "^text" FIFO objects. ---------- */
	/* [JSA]												*/
	tFIFO	*Fifo, *HdFifo, *ChdFifo;
	int		v;
	for (v = 0; v < MAXVOICE; v++) {
		Fifo = &voice_tb[v].TextDecs;
		HdFifo = &voice_tb[v].TextHdDecs;
		ChdFifo = &voice_tb[v].Chords;
		for (i = 0; i < MAXSTRDECO; i++) {
			Fifo->strings[i] = HdFifo->strings[i] = ChdFifo->strings[i] = NULL;
		}
		Fifo->inidx = Fifo->outidx = HdFifo->inidx = HdFifo->outidx = 0;
		ChdFifo->inidx = Fifo->outidx = 0;
	}

	/* ----- set default options and parse arguments ----- */
	init_ops(1);
	if (parse_args(argc, argv) != 0)
		exit(1);
	if (interactive || (do_mode==DO_OUTPUT))
		printf("This is jaabc2ps, version " VERSION " (%s)\n", VDATE);

	/* ----- set the page format ----- */
	nfontnames=0;
	if (!set_page_format())
		exit(3);
	if (help_me==2) {
		print_format(cfmt);
		exit(0);
	}

	/* ----- help printout  ----- */
	if (argc<=1)
		help_me=1;
	if (help_me==1) {
		write_help();
		exit(0);
	}

	if (ninf==0 && !interactive)
		rx("No input file specified", "");

	isel = psel[ninf-1];
	search_field0 = s_field[isel];	 /* default for interactive mode */
	if (epsf)
		cutext(outf);

	/* ----- initialize ----- */
	voice_tb[0].sym = malloc(MAXSYMS * sizeof(struct SYMBOL));
	voice_tb[0].name = "";
	sym = voice_tb[0].sym;
	zero_sym();
	pagenum=0;
	transpose=0;
	tunenum=tnum1=tnum2=0;
	verbose=0;
	file_open=file_initialized=0;
	nepsf=0;
	bposy=0;
	posx=cfmt.leftmargin;
	posy=cfmt.pageheight-cfmt.topmargin;

	page_init[0] = '\0';
	bbb[0] = '\0';
	for (j=0;j<ninf;j++) {
		strcat(bbb,in_file[j]);
		strcat(bbb," ");
	}

	/* ----- start infinite loop for interactive mode ----- */
	for (;;) {
		if (interactive)
			if (do_interactive(bbb) < 0)
				break;

		/* ----- loop over files in list ---- */
		if (ninf==0) printf("++++ No input files\n");
		for (j = 0; j < ninf; j++) {
			getext(in_file[j],ext);
			/*  skip .ps and .eps files */
			if (!strcmp(ext,"ps") || !strcmp(ext,"eps")) continue;
			/* [JSA] Clean up line breaks in the abc file if commanded to... */
			if (FixEols) ConvertEol(in_file[j]);
			/* ...[JSA] */
			if ((fin = fopen(in_file[j], "r")) == NULL) {
				if (ext[0] == '\0') strext(in_file[j], "abc");
				/* [JSA] Clean up line breaks in the abc file if commanded to... */
				if (FixEols) {
					if (!ConvertEol(in_file[j])) {
						printf("++++ Failed to convert line breaks: %s (skipping this file)\n", in_file[j]);
						continue;
					}
				}
				/* ...[JSA] */
				if ((fin = fopen(in_file[j], "r")) == NULL) {
					printf("++++ Cannot open input file: %s\n", in_file[j]);
					continue;
				}
			}
			isel=psel[j];
			search_field=s_field[isel];
			npat=rehash_selectors(sel_str[isel], xref_str, pat);
			dfmt = sfmt;
			/* [JSA] Watermark stuff... */
			if (dfmt.Watermark) {
				/* Copy the copyright string to the watermark string. */
				strcpy(dfmt.tWatermarkText, dfmt.BookCR);
				if (dfmt.WatermarkFile) {
					/* Strip only the filename and extension from the */
					/* path and append it to the watermark string.	  */
					pf = in_file[j];
					for (;;) {
						p = strchr(pf, ':');
						if (!p) p = strchr(pf, '/');
						if (!p) p = strchr(pf, '\\');
						if (p) {
							pf = p + 1;
						} else {
							break;
						}
					}
					if (dfmt.tWatermarkText[0]) strcat(dfmt.tWatermarkText, "/");
					strcat(dfmt.tWatermarkText, pf);
				}
				if (dfmt.WatermarkDate) {
					/* Get todays date (local time) and append it	*/
					/* to the watermark string.						*/
					time(&ltime);
					stime = localtime(&ltime);
					sprintf(chrtime, "%s %d, %d", cMonths[stime->tm_mon], 
						    stime->tm_mday, stime->tm_year + 1900);
					if (dfmt.tWatermarkText[0]) strcat(dfmt.tWatermarkText, "/");
					strcat(dfmt.tWatermarkText, chrtime);
				}
			} else {
				dfmt.tWatermarkText[0] = '\0';
			}
			/* ...[JSA] */

			strcpy(infostr, in_file[j]);

			if (do_mode==DO_INDEX) {
				printf("%s:\n", in_file[j]);
				do_index(fin,xref_str,npat,pat,select_all,search_field);
			} else {
				if (!epsf) {
					strext(outf, "ps");
					if (choose_outname) {
						strcpy(outf, in_file[j]);
						strext(outf, "ps");
					}
					open_output_file(outf, in_file[j]);
				}
				printf("%s: ", in_file[j]);
				if ((vb>=3) || interactive)
					printf("\n");
				process_file(fin,fout,
					     xref_str, npat, pat,
					     select_all, search_field);
				printf("\n");
			}
		}
		if (!interactive)
			break;
	}

	if (!interactive && (do_mode==DO_INDEX))
		printf("Selected %d title%s of %d\n",
		       tnum1, tnum1==1?"":"s", tnum2);

	close_output_file();
	/* [JSA] 05/00 text decorations */
	/* free any strings that weren't popped from the FIFO buffer. */
	for (i = 0; i < MAXVOICE; i++) {
		for (j = 0; j < MAXSTRDECO; j++) {
			if (voice_tb[i].TextDecs.strings[j]) {
				free(voice_tb[i].TextDecs.strings[j]);
				voice_tb[i].TextDecs.strings[j] = NULL;
			}
			if (voice_tb[i].TextHdDecs.strings[j]) {
				free(voice_tb[i].TextHdDecs.strings[j]);
				voice_tb[i].TextHdDecs.strings[j] = NULL;
			}
			if (voice_tb[i].Chords.strings[j]) {
				free(voice_tb[i].Chords.strings[j]);
				voice_tb[i].Chords.strings[j] = NULL;
			}
		}
		voice_tb[i].TextDecs.inidx = voice_tb[i].TextDecs.outidx = 0;
		voice_tb[i].TextHdDecs.inidx = voice_tb[i].TextHdDecs.outidx = 0;
		voice_tb[i].Chords.inidx = voice_tb[i].Chords.outidx = 0;
	}
	/* back to your regularly scheduled programming */
	return 0;
}

static char	*safe_gets(char s[], int sz) {
	char	*rv;

	rv = fgets(s, sz, stdin);
	if (rv) {
		/* gets strips the trailing newline, fgets doesn't	*/
		/* we have to strip it correctly on all platforms	*/
		/* We're actually stripping all trailing whitespace */
		/* but that's okay in this application.				*/
		while (isspace(s[strlen(s) - 1])) s[strlen(s) - 1] = '\0';
	}
	return rv;
}

static int do_interactive(char *bbb)
{
	char aaa[501], ccc[501];
	char *urgv[100];
	int j, urgc;

	for (;;) {
		printf("\nSelect tunes: ");
		/* Michael was using gets() here and it was NOT a problem as it	*/
		/* was used.  But since the darn GCC linker shouted a dire-		*/
		/* sounding warning each time the program was built, I replaced	*/
		/* the function so I wouldn't be inundated with "but GCC says"	*/
		/* e-mails ;-)													*/
		/* TODO: Ironically, interactive mode isn't working anyway!		*/
		safe_gets(aaa, 500);
		if (isblankstr(aaa)) return -1;
		sscanf(aaa,"%s",ccc);
		if (ccc[0]=='?') {
			printf("%s\n", bbb);
			continue;
		}
		if (ccc[0]=='*') {
			strcat(bbb,strchr(aaa,'*')+1);
			strcpy(aaa,bbb);
			printf("%s\n", aaa);
		}
		if (ccc[0]=='!') {
			bbb[0] = '\0';
			for (j=0;j<ninf;j++) {
				strcat(bbb,in_file[j]);
				strcat(bbb," ");
			}
			strcat(bbb,strchr(aaa,'!')+1);
			strcpy(aaa,bbb);
			printf("%s\n", aaa);
		}
		strcpy(bbb,aaa);
		urgc=make_arglist(aaa, urgv);
		if (!strcmp(urgv[1],"q")
		    || !strcmp(urgv[1],"quit"))
			return -1;
		init_ops(0);
		if (parse_args(urgc, urgv) != 0)
			continue;
		ops_into_fmt(&cfmt);
		if (do_mode==DO_OUTPUT) {
			if (set_page_format() == 0)
				continue;
		}
		if (epsf)
			cutext(outf);
		if (help_me==1) {
			write_help();
			continue;
		}
		if (help_me==2) {
			print_format(cfmt);
			continue;
		}
	}
	return 0;
}
