#include "iteach1.h"

/*These are the routines common to the IBM versions of MT 1,2 & 3*/
/* A routine for Mac compatibility to print warning messages */
char *command[ ] = {"RESET","OPEN","OVERWRITE","EXIT","RECOGNIZE","RECALL",
	"AFFIXES","PRIMITIVES","RUNG1","RUNG2","RUNG3","TOP","ERRORBOX",
	"SELECT","REVIEW","TEST","SHUFFLE","LOOKUP","STATS","DRILL",
	"TIMESUP","CLOSE","HELP","SAVEAS","BRUSHUP","QUIT","BYE"};

char *shorts[ ] = {"RS","O","OW","X","COG","CAL","AF","P","1","2","3",
	"TOP","E","S","R","T","SH","L","ST","D","A","C","?","SA","B","Q",
	"BYE"};
	/* short version of commands */
char *rstring[] = {"Illegal","Rung: 1","Rung: 2","Rung: 3","Top","Error Box"};
char *mess[MAXMESS];			/*error messages*/

void alert(short n,short m)

/* A routine for compatibility with the Mac version which prints warning messages. On entry n is the type of alert and m the message to print */

{	mess[0] = "Reading or writing file.";
	mess[1] = "Cannot allocate flasharray.";
	mess[5] = "Not a MacTeach 2 or 3 file.";
	mess[4] = "Close the open file first.";
	mess[8] = "Empty the error box first!";
printf("%s\n",mess[m]);
	if (!n) exit(0);
	}

void cmdMes(void)

/* This message is printed whenever a command is expected */

{	puts("\n\nType s to start or select, or");
	puts ("Enter command [upper or lower case], or \nType ? for help.\n");
	}

void commands(void) 

/* If help is requested, this is the list of commands printed */

{	clrscr();
	gotoxy(33,1);
	puts ("COMMAND PAGE 1\n");
	puts ("Commands [Short forms]: Actions performed.\n");
	puts ("Select [S]: Select cards from deck for study.");
	puts ("Review [R]: Review cards on current rung.");
	puts ("Test [T]: Test cards on current rung.");
	puts ("Bye [Bye]: Quit without saving; learning ladder will be\
	restored.");
	puts ("Exit/Quit [X][Q]: End the current action or the session.");
#ifdef TONE
	puts ("Recognition [Cog]: Make English responses to Loglan stimuli.");
	puts ("Recall [Cal]: Make Loglan responses to English stimuli.");
#endif
	puts ("Rung1 [1] Rung2 [2] Rung3 [3]: Change rungs.");
	puts ("Top [Top] Errorbox [E]: Go to Top or Error-Box."); 
#ifdef TTHREE
	if (prog !='2') {
		puts ("Recognition [Cog]: Make English responses to Loglan stimuli.");
		puts ("Recall [Cal]: Make Loglan responses to English stimuli.");
		}
	puts ("Lookup [L]: Lookup a word or affix.");
#endif
	puts ("Stats [St]: Display the session statistics.");
	puts ("Drill-number [D]: Set the number of repeats to escape error-box.");
	puts ("Time-allowance [A]: Set trigger for 'Time's up!' penalty.");
	puts ("Re-Start [Rs]: Restore all cards to deck.");
	puts ("Brushup [B]: Put all cards on Rung 3 and shuffle them.");
	puts ("Shuffle [Sh]: Toggle the shuffle flag.");
	puts ("\nPress space for Command Page 2.");
	puts ("Press return for Ladder Page.");
	if ((cmd = getche()) == ' ') {
		clrscr();
		gotoxy(33,1);
		puts ("COMMAND PAGE 2\n");
		puts ("Commands [Short forms]: Actions performed.\n");
#ifdef TTHREE
		puts ("Affixes [Af]: Preset for study of affixes.");
		puts ("Primitives [P]: Preset for study of primitives.");
#endif
		puts ("Open [O]: Open a study file.");
		puts ("Close [C]: Close the current file.");
		puts ("Overwrite [Ow]: Overwrite study file of same name.");
		puts ("Saveas [SA]: Save the study file with a new name.");
/*	puts ("\nTo combine Review with Rung change, use Ctl with F keys");*/
puts ("\nTo start testing immediately after moving, use F keys to move:");
		puts ("F1-3: Rungs 1-3. F4: Top. F10: Error Box.");
		puts ("\nPress any key for Ladder Page.");
		getche();
		}
	display();
	}

void demote(short n)

/* After an error response, show card n, sound an alarm, and place the 
	card in the error box */

{	if (!reviewflag) { /*demote only on testing*/
#ifdef TTHREE
		gotoxy(15,18);
#else
		gotoxy(10,16);
#endif
		if (!strcmp(instring,"TIME'S UP!")); /*No error mess on overtime*/
		else printf("Incorrect.\n");
		SysBeep(PAR);
		flasharray[n].flag[curflag] = '5'; /*Put in error box*/
#ifdef TONE

/*In case it was a typing error to be corrected, save the old level, else reset it so the whole set of reponses for a given level must be repeated.*/

		oldlevel = flasharray[n].flag[rrflag-'1'+2]; 
		flasharray[n].flag[rrflag -'1'+2] = '0';
#endif	
		}
	}

void display(void)

/* Display the current file, configuration, distribution of cards,and 
	statistics of the latest test, if any */

{	clrscr();
	countflag(); /* Obtain the card distribution */
	gotoxy(35,2);
#ifdef TONE
	curflag = rrflag-'1';
#endif
	printf ("LADDER PAGE \n");
#ifdef TTHREE
	gotoxy(22,4);
	if (prog =='2' || prog == '4')	puts("MacTeach 3: Learning Loglan affixes.");
	else puts("MacTeach 2: Learning Loglan primitives.");
#else
	gotoxy(21,4);
	puts("Macteach 1: Forming Loglan Utterances.");
#endif
	if (affixflag=='1') {
		gotoxy(18,6);
		printf("Mode: %s     Drill number: %d", (rrflag=='1') ?
			"Recognize affixes" : "Recall affix sets",drillflg);
		}
	else {
		gotoxy(21,6);
		printf("Mode: %s     Drill number: %d",(rrflag=='1')	
			? "Recognition " : "Recall      ", drillflg);
		}
	gotoxy(26,7);
	printf("Response time allowance: %d.%d", 
		threshld/factor,((threshld%factor)*10)/factor);
	gotoxy(27,8);
	printf("You're in/on %s\n",rstring[rungflag-'0']);
	gotoxy(31,10);
	printf("%3d On top\n",r[4]);
	gotoxy(31,11);
	printf("%3d On rung 3\n",r[3]);
	gotoxy(31,12);
	printf("%3d On rung 2\n",r[2]);
	gotoxy(31,13);
	printf("%3d On rung 1\n",r[1]);
	gotoxy(31,14);
	printf("%3d Still in deck\n",r[0]);
	gotoxy(31,15);
	printf("%3d In error box\n",r[5]);
	if (tested) {
		gotoxy(25,17);
		printf("Tested: %d   Correct: %d (%d%%)\n", tested, right, 
			(100*right)/tested);
		showStat(); /*Max and min times, standard deviation and timeouts*/
		}
	}

void doTeach(short argc,char **argv)

/* Read in a command and take the appropriate action.*/

{	while (TRUE) { /*Do forever (until quit command) */
		cmdMes();
		cmd = getCommand();
		switch (cmd) {
		case BYE: dirty = 0; quitDo(); break;
		case OPEN: openDo(argc,argv); break;
		case OVERWRITE: saveDo(); break;
		case SAVEAS: saveAsDo(); break;
		case QUIT:
		case EXIT:  writeStat(); quitDo(); break;
		case RECOGNIZE: writeStat(); rrflag = '1'; display(); break;
		case RECALL: writeStat(); rrflag = '2'; display(); break;
	#ifdef TTHREE
		case AFFIXES: writeStat(); affixflag = '1'; display(); break;
		case PRIMITIVES: writeStat(); affixflag = '2'; display(); break;
	#endif
		case RUNG1: rungflag = '1'; display(); break;
		case RUNG2: rungflag = '2'; display(); break;
		case RUNG3: rungflag = '3'; display(); break;
		case TOP:	rungflag = '4'; display(); break;
		case ERRORBOX: rungflag = '5'; display(); break;
		case SELECT: 
			if (!testIn()) break;
			doselect(); 
			display(); 
			break;
		case REVIEW:
			reviewflag = 1; 
			if (!testIn()) break;
			dotest();
			break;
		case TEST: 
			reviewflag = 0; 
			if (!testIn()) break;
			dotest();
			break;
		case SHUFFLE: 
			shufflag ^= TRUE;
			(shufflag) ? puts("Shuffle enabled") : puts ("Shuffle disabled.");
			break;
	#ifdef TTHREE
		case LOOKUP: doSeek(); break;
	#endif
		case STATS: sessions(); break;
		case DRILL: getDrill(); break;
		case TIMESUP: getTimeOut(); break;
		case CLOSE:
			if (inpfile) {
				if (dirty) {
					puts("Save this file? [Y,N, or (C)ancel].");
					switch (toupper(getche())) {
					case 'Y': saveDo(); dirty = FALSE;
					case 'N': break;
					case 'C': return;
						}
					}
				fclose(in);
#ifdef TONE
				fclose(cmt);
#endif
				}
			inpfile = 0; break;
		case RESET: restore(); display(); break;
		case BRUSHUP: brush(); break;
		case HELP: commands(); break;
		case 0xbb: /*Function keys for combined rung + test*/
		case 0xbc:
		case 0xbd:
		case 0xbe:
		case 0xbf:
		case 0xc4:
			if (cmd == 0xc4) cmd = 0xbf;
			rungflag = cmd - 0x8a;
			reviewflag = 0;
			if (!testIn()) break;
			dotest();
			break;
		case 0xde: /*Function keys for combined rung and review*/
		case 0xdf:
		case 0xe0:
		case 0xe1:
		case 0xe2:
		case 0xe7:
			if (cmd == 0xe7) cmd = 0xe2;
			rungflag = cmd - 0xad;
			reviewflag = 1;
			if (!testIn()) break;
			dotest();
			break;
		default: break;
			}
		}
	}

short doTime(void)

/* Obtain time to first keystroke.  System dependent, Returns time in ticks*/

{	int c;
	char *ptr;
	short temp;
	ticks = clock();
	while (!kbhit()) {	// was scr_poll() which does not exist in ANSI  C.  Explore alternate timers.
		temp = clock() - ticks; /* and the response time */
		if (temp<0) temp +=6000;
		if (temp >threshld) { /* And flag timeouts */
			ticks = threshld;
			strcpy(instring,"TIME'S UP!");
#ifdef TTHREE
			gotoxy(15,17);
#else
			gotoxy(5,15);
#endif
			puts(instring);
			return -1;
			}
		}
	ticks = temp;
	return TRUE;
	}



short getCommand(void) 

/* Obtain and parse a command. Each command has a long and short form */

{	short i;
//	while (-1 == -1);		// Was (i = scr_poll())  which doesn't exist.  Explore alternatives.
//	if (i > 127) {
//		getche();
//		return i;
//		}
	gets(instring);
	upstring(instring); /*Put it in upper case for matching */
	for (i=0; i<MAXCMD; ++i) {
		if (!strcmp(instring,command[i]) || !strcmp(instring,shorts[i]))
		return i;
		}
	return MAXCMD;
	}

void getDrill(void)

/* Get the number of correct responses in order to leave the error box */

{	short ch;
	if (r[5]) alert(2,8);
	printf("\nEnter number of repeats to leave error box (%d)-->  ",
		drillflg);
	ch = getchar();
	if (ch==NL) return;
	else ungetc(ch,stdin);
	scanf ("%d",&ch);
	drillflg = ch;
//	getchar();
	}


void getTimeOut(void)

/* This is IBM specific for obtaining the maximum time permitted for 
	response. It is measured in 100ths second. It may have to be adjusted 
	for other systems */

{	char thno[10];
	printf("\nEnter seconds until timeup (%1d.%1d)-->  ",threshld/factor,
		((threshld%factor)*10)/factor);
	gets(thno);
	if (isdigit(thno[0])) {
		switch(strlen(thno)) {
		case 3:
			if (thno[1] != '.' || !isdigit(thno[2])) break;
			thno[1] = thno[2];
			thno[2] = '\0';
			ind1 = ((atoi(thno) * factor))/10;
			break;
		case 1:
			ind1 = atoi(thno) * factor;
			break;
		default:
			ind1 = threshld;
			break;
			}
		}
	if (ind1>1 && ind1<(93 * factor)/10) threshld = ind1;
	}

void iOpen(char *fname)

/* This routine opens  file fname for reading and writing, and warns if
	unsuccessful */

{	if (!(in = fopen(fname,"r+"))) {
		printf("\nCannot open %s",fname);
		}
	}

void myWrite(char *string)

/* Display a string string, removing control punctuation */

{	char *sptr,ch;
#ifdef MAC
	short lin;
#endif
	short col,copy,flg;
	flg = 0;
	sptr = string;
	copy = TRUE;
	while (ch = *sptr++) {
		switch(ch) {
		case '\\': /*Save the next character without checking*/
			putchar (*(sptr++));
			break;

/* If copy is in effect, these characters are converted to a space, or a return if we are too close to the end of a line.  If a comment it is indented */

		case '-':
		case '=':
		case '+':
		case ' ':
#ifdef MAC
			cgetxy( (int *)&col, (int *)&lin, stdin);
#else
		       col = wherex();
#endif
			if (copy) { 
				if (col>68) {
					printf("\n       ");
					clreol();
					if (flg) printf("     ");
					}
				else putchar(SP);
				}
			break;

/* This character is displayed if copy is in effect. A check is made for wrapping or for indenting comments. The return precedes the character */

		case '(':
#ifdef MAC
			cgetxy( (int *)&col, (int *)&lin, stdin);
#else
			col = wherex();
#endif
			if (copy) {
				if (col>68) {
					printf("\n       ");
					if (flg) printf("     ");
					putchar(ch);
					}
				else putchar(ch);
				}
			break;

/*As above, but in these cases the wrap follows the character*/

		case ')':
		case '/':
#ifdef MAC
			cgetxy( (int *)&col, (int *)&lin, stdin);
#else
			col = wherex();
#endif
			if (copy) {
				if (col>68) {
					putchar(ch);
					printf("\n       ");
					if (flg) printf("     ");
					}
				else putchar(ch);
				}
			break;

/*Double space before beginning a comment, and indent this and subsequent lines*/

		case '|':
			printf("\n\n          ");
			flg = 1;
			break;

/*Normally the next two characters are not displayed, except in comments*/

		case ':': if (flg) putchar(ch);
		case '&': break;

/*All other characters are displayed*/

		default:
			if (copy) putchar(ch);
			}
		}
	putchar(SP);
	}

void openDo(short argc,char **argv) 

/* Check if a file name was given on opening, and open it; else request 
	the name; then read it into memory */

{	if (argc == 2) {
		strcpy(filename,argv[1]);
		}
	else {
		puts("Enter filename to open.");
		gets(filename);
		}
	iOpen(filename);
	readfile(); /*Read the file */
	if (argc != 2) display();
	}

void quitDo(void)

/* This is the windup routine. Remind the user if da has not saved the 
	file, close the input file, file and print the session statistics */

{	if (dirty) {
		puts("\nSaving current ladder data...\n");
		saveDo();
		dirty = FALSE;
		}
	if (inpfile) fclose(in);
	exit(0);
	}

void saveAsDo(void) 

/* Save the file under a new name (which is requested) */

{	short i;
	dirty = TRUE;
	if (fclose(in)) puts ("File could not be closed.");
	puts("Enter output file name.");
	gets(instring);
	if (!(in = fopen(instring,"w"))){
		printf ("Cannot create %s",instring);
		}
	else {
		writefile(in);
		}
	}

short screenchr(void)

/* A character input without necessity of a CR */

{	short temp;
	temp = toupper(getche());
	switch(temp) {
	case 13:
	case 10:
	case 'Y': return NEXTF;
	case 'Q': 
	case 'X': return STOPF;
	case 'C': return CANERR;
	default: return PASSF;
		}
	}
	
#ifdef MAC
void clrscr(void)

/* A screen clearing routine that can be customized if needed */

{	cgotoxy(1,1,stdin);
	ccleos(stdin);
	}
	
char getche(void)

{	char chr;
	csetmode(C_CBREAK,stdin);
	chr =getchar();
	csetmode(C_ECHO,stdin);
	return chr;
	}
#endif

void sessions(void)

/* Calculate and display the session statistics */

{	if (!testtotl) {
		puts ("No test results yet.");
		return;
		}
	setstat();
	getend();
	printf ("\n\tSESSION STATISTICS\n");
	printf ("\nOn %d/%d/%d from %d:%02d to %d:%02d",startime->tm_mon+1,
		startime->tm_mday,startime->tm_year,shour,smin,etime->tm_hour,
		etime->tm_min);
	printf ("\nTested %d, Correct %d, %d%%",testtotl,righttot,percent);
	printf ("\nAverage time %d.%d +/- %d.%ds, Overtime %d\n\n",average/factor,
		((average%factor)*10)/factor,sdevn/factor,((sdevn%factor)*10)/factor,tmouts);
	}

void setRung(short n)

/* Set rungflag to n.  Needed because of common code with Mac */

{	rungflag = n;
	}

/* Display the statistics for a single test */

void showStat(void)

{	long temp;
	testav = ticktotl/tested;
	temp = testav - moment;
	tsd = isqrt(labs(temp*temp - square/tested));
	gotoxy(26,18);
	printf ("Average time: %d.%d +/- %d.%ds\n",testav/factor,((testav%factor)*10)/factor,tsd/factor,
		((tsd%factor)*10)/factor);
	gotoxy(26,19);
	printf ("Response range: %d.%d - %d.%d",minimum/factor,((minimum%factor)*10)/factor,
		maximum/factor,((maximum%factor)*10)/factor);
	gotoxy(29,20);
	printf ("Number of overtimes: %d",tmouts);
	}

Boolean testIn(void)

/*Returns a BOOLEAN if a file is not open*/

{  if (!inpfile) {
		puts("Open a file first.");
		return FALSE;
		}
	return TRUE;
	}

void writeStat(void)

/* Write the statistics to the statistics log file, and also to the screen */

{	
#ifdef TTHREE
	if (rrflag == '1' && affixflag == '1')
		if (!(stats = fopen("Stats3.paf","a")))
			puts("Cannot open statslog file");
	if (rrflag == '1' && affixflag == '2')
		if (!(stats = fopen("Stats2.le","a"))) 
			puts("Cannot open statslog file");
	if (rrflag == '2' && affixflag == '1')
		if (!(stats = fopen("Stats3.afp","a"))) 
			puts("Cannot open statslog file");
	if (rrflag == '2' && affixflag == '2')
		if (!(stats = fopen("Stats2.el","a")))
			puts("Cannot open statslog file");
#else
	if (rrflag == '1') {
		if (!(stats = fopen("Stats1.le","a"))) 
			puts("Cannot open statslog file");
			}
	else
		if (!(stats = fopen("Stats1.el","a"))) 
			puts("Cannot open statslog file");
#endif
	if (testtotl) {
		sessions();
		statline();
		testtotl = tested = righttot = maximum = 0;
		sqtotal = ticktotl = timetotl = 0L;
		minimum = 10000;
		fclose(stats);
		}
	}

#ifdef IBM
void SysBeep(PAR)

/* Sound the bell or beep */

	{ putchar(0x07);
	}
#else
Boolean kbhit(void)

{	EventRecord myEvent;
	return EventAvail(8,&myEvent);
	}
#endif

