//	BasicTokenizer.c

#include <stdio.h>
#include <string.h>
#include "IC_Errors.h"
#include "BasicTokenizer.h"
#include "ByteMunger.h"

#define		kNumTokensInTable_BAS	(234 - 128 + 1)
#define		kNumTokensInTable_INT	128

typedef		char	TokenStr[8];
TokenStr	gTokenTable_BAS[kNumTokensInTable_BAS] = {
	"END",		"FOR",		"NEXT",		"DATA",		"INPUT",		"DEL",		"DIM",		
	"READ",		"GR",		"TEXT",		"PR#",		"IN#",			"CALL",		"PLOT",		
	"HLIN",		"VLIN",		"HGR2",		"HGR",		"HCOLOR=",		"HPLOT",	"DRAW",		
	"XDRAW",	"HTAB",		"HOME",		"ROT=",		"SCALE=",		"SHLOAD",	"TRACE",		
	"NOTRACE",	"NORMAL",	"INVERSE",	"FLASH",	"COLOR=",		"POP",		"VTAB",		
	"HIMEM:",	"LOMEM:",	"ONERR",	"RESUME",	"RECALL",		"STORE",	"SPEED=",		
	"LET",		"GOTO",		"RUN",		"IF",		"RESTORE",		"&",		"GOSUB",		
	"RETURN",	"REM",		"STOP",		"ON",		"WAIT",			"LOAD",		"SAVE",		
	"DEF",		"POKE",		"PRINT",	"CONT",		"LIST",			"CLEAR",	"GET",	
	"NEW",		"TAB",		"TO",		"FN",		"SPC",			"THEN",		"AT",		
	"NOT",		"STEP",		"+",		"-",		"*",			"/",		"^",		
	"AND",		"OR",		">",		"=",		"<",			"SGN",		"INT",		
	"ABS",		"USR",		"FRE",		"SCRN",		"PDL",			"POS",		"SQR",		
	"RND",		"LOG",		"EXP",		"COS",		"SIN",			"TAN",		"ATN",		
	"PEEK",		"LEN",		"STR$",		"VAL",		"ASC",			"CHR$",		"LEFT$",		
	"RIGHT$",	"MID$"
};

TokenStr	gTokenTable_INT[kNumTokensInTable_INT] = {
	"Zero",		"EOL",		"_",		":",		"LOAD",		"SAVE",
	"CON",		"RUN",		"RUN",		"DEL",		",",		"NEW",
	"CLR",		"AUTO",		",",		"MAN",		"HIMEM:",	"LOMEM:",
	"+",		"-",		"*",		"/",		"=",		"#",
	">=",		">",		"<=",		"<>",		"<",		"AND",
	"OR",		"MOD",		"^",		"+",		"(",		",",
	"THEN",		"THEN",		",",		",",		"\"",		"\"",
	"(",		"!",		"!",		"(",		"PEEK",		"RND",
	"SGN",		"ABS",		"PDL",		"RNDX",		"(",		"+",
	"-",		"NOT",		"(",		"=",		"#",		"LEN(",
	"ASC(",		"SCRN(",	",",		"(",		"$",		"$",
	"(",		",",		",",		";",		";",		";",
	",",		",",		",",		"TEXT",		"GR",		"CALL",
	"DIM",		"DIM",		"TAB",		"END",		"INPUT",	"INPUT",
	"INPUT",	"FOR",		"=",		"TO",		"STEP",		"NEXT",
	",",		"RETURN",	"GOSUB",	"REM",		"LET",		"GOTO",
	"IF",		"PRINT",	"PRINT",	"PRINT",	"POKE",		",",
	"COLOR=",	"PLOT",		",",		"HLIN",		",",		"AT",
	"VLIN",		",",		"AT",		"VTAB",		"=",		"=",
	")",		")",		"LIST",		",",		"LIST",		"POP",
	"NODSP",	"NODSP",	"NOTRACE",	"DSP",		"DSP",		"TRACE",
	"PR#",		"IN#"
};

/*
	end of line looks like this:
	
	0x00 0xA1 0xA2 0xL1 0xL2

	A = address of next line
	L = line number

	can be split anywhere along that sequence!
*/

void	DeTokenizeBasic(
	char	*i_fileBufP,	ulong	*fileBufIndexSP, 
	ushort	maxTokensS,		ushort	fileBufSizeS,
	Boolean	*new_lineBP, 
	char	*i_basBufP,		ushort 	*basBufCharsSP)
{
	ushort			basBufIndex = 0;
	unsigned char	token;
	char			*tokenStr;
	Boolean			doneB = FALSE;

	do {
		if (*new_lineBP) {
			ushort	lineNum;
			char	lineNumStr[10];
			
			*new_lineBP = FALSE;
			
			//	skip address of next line
			*fileBufIndexSP += 2;

			//	may be end of program			
			if (*fileBufIndexSP < maxTokensS) {
				//	now we read the line number

				lineNum = GetRboShort(*(RboShort *)&i_fileBufP[*fileBufIndexSP]);
				*fileBufIndexSP += 2;
				
				//	may be end of program			
				if (*fileBufIndexSP < maxTokensS) {
					sprintf(lineNumStr, "%hu", lineNum);
					sprintf(&(i_basBufP[basBufIndex]), "%s ", lineNumStr);
					basBufIndex += strlen(lineNumStr) + 1;
				}
			}
		}
		
		if (*fileBufIndexSP < maxTokensS) {
			token = (unsigned char)(i_fileBufP[(*fileBufIndexSP)++]);
			
			if (token == 0x00) {
				//	end of line gets a carriage return
				i_basBufP[basBufIndex++] = 0x0D;
				*new_lineBP = TRUE;
			} else if (token < 128) {
				//	control chars become stars
				if (token < 0x1F) token = '*';
				
				i_basBufP[basBufIndex++] = token;
			} else {
				tokenStr = gTokenTable_BAS[token - 128];
				
				sprintf(&(i_basBufP[basBufIndex]), " %s ", tokenStr);
				basBufIndex += strlen(tokenStr) + 2;
			}
		}
		
		if (maxTokensS < fileBufSizeS) {
			doneB = *fileBufIndexSP >= maxTokensS;
		} else if (*fileBufIndexSP >= (fileBufSizeS >> 1)) {
			*fileBufIndexSP -= fileBufSizeS >> 1;
			doneB = TRUE;
		}
	} while (!doneB);

	*basBufCharsSP = basBufIndex;
}

/* Feed it a byte at a time and it emits a string */
/* DecodeOutputStream simply does the IO  */

CDetokenizeInt		*gTokenizerP = NULL;

Boolean		SetUpTokenizer(void)
{
	gTokenizerP = new CDetokenizeInt;
	if (gTokenizerP == NULL) {
		ReportErrorStr(-1, "Couldn't initialize Tokenizer");
	} else {
		gTokenizerP->IDetokenizeInt();
	}
	
	return gTokenizerP != NULL;
}

void		CDetokenizeInt::IDetokenizeInt(void)
{
	Reset();
}

void		CDetokenizeInt::Dispose(void)
{
	delete this;
}

void		CDetokenizeInt::Reset(void)
{
	i_lineNumber = 0;
	i_statementState = 3;
	i_doNumber = 0;
	i_constant = 0;
	
	i_endOfProgramB = FALSE;
	i_quoteActiveB = FALSE;
	i_remActiveB = FALSE;
	i_lastWasKeyWordB = TRUE;
}

void	DeTokenizeInt(
	char	*inBufZ,		ushort	inBufSizeS,
	char	*outBufZ,		ushort 	*outBufSizeSP)
{
	ushort		curBufS;
	char		bufAC[12], *curCharZ;
	ushort		strLenS;
	ulong		inMaxL	= GetPtrSize((Ptr)inBufZ), 
				outMaxL	= GetPtrSize((Ptr)outBufZ);
	
	ASSERT((ulong)inBufSizeS < inMaxL);
	
	*outBufSizeSP	= 0;
	outBufZ[0]		= 0;
	
	for (curBufS = 0; !gTokenizerP->i_endOfProgramB && curBufS < inBufSizeS; curBufS++) {
		curCharZ	= gTokenizerP->Decode(inBufZ[curBufS], bufAC);
		strLenS 	= strlen(curCharZ);
		
		if (strLenS) {
			*outBufSizeSP += strLenS;
			
			ASSERT((ulong)*outBufSizeSP < outMaxL);
			
			strcat(outBufZ, curCharZ);
		}
	}
	
	//	don't forget the zero on the end
	//outBufZ[++(*outBufSizeSP)] = 0;
}

char	*CDetokenizeInt::Decode(Byte tokenByte, char *buf)
{
	if (i_endOfProgramB) return ("");

	// Deal with length of line and line number
	switch (i_statementState) {
	
		case 0 :
			break;
			
		case 1 : // Line number hi-byte
			i_lineNumber += ((ushort)tokenByte * (ushort)256);
			i_statementState = 0;
			i_lastWasKeyWordB = TRUE; // Consider line # a token
			sprintf(buf, "%5hu", i_lineNumber);
			return buf;
			
		case 2 : // Line number lo-byte
			i_lineNumber = tokenByte;
			i_statementState = 1;
			return "";
			
		case 3 : // Length of stmt
			// Deal with end-of-program (01 00)
			if (tokenByte == 0) {
				i_endOfProgramB = TRUE;
			} else {
				i_statementState = 2;
			}
			
			return "";
			
		default :
			i_statementState = 0;
			return "";
	}

	// Deal with numeric constants (B0-B9)
	switch (i_doNumber) {
	
		case 0 :
			break;
			
		case 1 : // Number hi-byte
			i_constant += ((ushort)tokenByte * (ushort)256);
			i_doNumber = 0;
			
			if (i_lastWasKeyWordB) {
				i_lastWasKeyWordB = FALSE;
				/*" %hd"*/
				sprintf(buf, " %hd", i_constant);
				return buf;
			} else {
				sprintf(buf, "%hd", i_constant);
				return buf;
			}
			
		case 2 : // Number lo-byte
			i_constant = tokenByte;
			i_doNumber = 1;
			return "";
			
		default :
			i_doNumber = 0;
			return "";
	}
	
	if (!i_remActiveB && !i_quoteActiveB && tokenByte > 175 && tokenByte < 186) {
		i_doNumber = 2;
		return "";
	}

	// Deal with end-of-line (01)
	if (tokenByte == 1) {
		i_statementState = 3;
		i_quoteActiveB = FALSE;
		i_remActiveB = FALSE;
		return "\r";
	}

	// Deal with non-printing, printing, & keywords
	if (tokenByte == 40) {
		i_quoteActiveB = TRUE;
		i_lastWasKeyWordB = FALSE;
		/*" %s"*/
		sprintf(buf, " %s", gTokenTable_INT[tokenByte]);
		return buf;
	}
	
	if (tokenByte == 41) {
		i_quoteActiveB = FALSE;
		i_lastWasKeyWordB = TRUE;
		return (gTokenTable_INT[tokenByte]);
	}
		
	if (tokenByte == 64 || tokenByte == 65) {	// $ 
		i_lastWasKeyWordB = TRUE;
		return (gTokenTable_INT[tokenByte]);
	}
		
	if (tokenByte == 93) {
		i_remActiveB = TRUE;
	}
	
	if (tokenByte < 128) {
		i_lastWasKeyWordB = TRUE;
		/*" %s"*/
		sprintf(buf, " %s", gTokenTable_INT[tokenByte]);
		return buf;
	}
		
	if (tokenByte < 160) {
		if (i_lastWasKeyWordB) {
			i_lastWasKeyWordB = FALSE;
			/*" ^%c"*/
			sprintf(buf, " ^%c", (char)(tokenByte - (128)));
			return buf;
		} else {
			sprintf(buf, "^%c", (char)(tokenByte - (128)));
			return buf;
		}
	}
	
	if (tokenByte == 255) {
		i_lastWasKeyWordB = TRUE;
		return (" [DELETE]");
	}
	
	if (tokenByte < 255) {
		if (i_lastWasKeyWordB) {
			i_lastWasKeyWordB = FALSE;
			/*" %c"*/
			sprintf(buf, " %c", (char)(tokenByte - (128)));
			return buf;
		} else {
			sprintf(buf, "%c", (char)(tokenByte - (128)));
			return buf;
		}
	}
	
	return "";
}
