#include "Leggi.h"

#define ITALICON  3
#define ITALICOFF 23
#define UNDERLINEDON  4
#define UNDERLINEDOFF 24
#define BOLDON  1
#define BOLDOFF 22
#define PLAIN 0
#define FOREGROUNDID 3
#define BACKGROUNDID 4
#define FONTS "BRKAEHYZ"
#define TSEQLEN 20

/*
*
* The ANSI/ISO parsing automaton.
*
* WARNING: The following variables MUST be inizialized with a call to InitAutomaton()
* before calling the automaton.
*
* The variables you can set are: a LineDesc * (destination structure), a char *
* (destination lines).
*
* To use the automaton, you must first initialize it with a call to InitAutomaton(),
* then call it with a char * (source) and int (len) parameters: the output will be
* added to the destination structure and lines at every call. At the end, use
* EndAutomaton().
*
* Why doesn't C give me nested functions?
*/

static short State, CurrentPos, TabSize=8, WordWrap=0;
static unsigned char CurrentFont, CurrentColor, EscSeq[TSEQLEN], *globaldest, Class[256], Fonts[]=FONTS;
static LDesc *LineDesc;

enum States { NORMAL, ESCSEQ, SKIPCHAR, SHORTSUBSEQ, LONGSUBSEQ, ENDLINE, TABADD, CHECKSEQ, STATESCOUNT };
enum Classes { OTHER, SKIP, ALPHA, TAB, SYMBOL, SQBRA, SHSEQ, CSI, ESC, CR, CLASSESCOUNT };

static char Transition[CLASSESCOUNT][STATESCOUNT] =
	{
		{ NORMAL, NORMAL, NORMAL, NORMAL, NORMAL, NORMAL, NORMAL, NORMAL },
		{ SKIPCHAR, SKIPCHAR, SKIPCHAR, SKIPCHAR, SKIPCHAR, SKIPCHAR, SKIPCHAR, SKIPCHAR },
		{ NORMAL, NORMAL, NORMAL, CHECKSEQ, CHECKSEQ, NORMAL, NORMAL, NORMAL },
		{ TABADD, TABADD, TABADD, TABADD, TABADD, TABADD, TABADD, TABADD },
		{ NORMAL, NORMAL, NORMAL, NORMAL, LONGSUBSEQ, NORMAL, NORMAL, NORMAL },
		{ NORMAL, LONGSUBSEQ, NORMAL, NORMAL, LONGSUBSEQ, NORMAL, NORMAL, NORMAL },
		{ NORMAL, SHORTSUBSEQ, NORMAL, NORMAL, LONGSUBSEQ, NORMAL, NORMAL, NORMAL },
		{ LONGSUBSEQ, LONGSUBSEQ, LONGSUBSEQ, LONGSUBSEQ, LONGSUBSEQ, LONGSUBSEQ, LONGSUBSEQ, LONGSUBSEQ },
		{ ESCSEQ, ESCSEQ, ESCSEQ, ESCSEQ, ESCSEQ, ESCSEQ, ESCSEQ, ESCSEQ },
		{ ENDLINE, ENDLINE, ENDLINE, ENDLINE, ENDLINE, ENDLINE, ENDLINE, ENDLINE }
	};


__asm transition(register short int __d0, register char * __a0, register char * __a1);


/*
*
* SetTabSize()
*
* takes a value n>0 and sets the tab size. Reeeally object-oriented.
*
*/

__regargs void SetTabSize(int n) {

	TabSize = n;
}


/*
*
* SetWordWrap()
*
* takes a value n>=0 and sets the WordWrap to n chars.
*
*/

__regargs void SetWordWrap(int n) {

	WordWrap = n;
}


/*
*
* InitAutomaton()
*
* initializes the global fields. Takes a LineDesc* and a char*.
*
*/

__regargs void InitAutomaton(LDesc *LD, char *d) {

	register int i;

	globaldest = d;
	*(globaldest++) = CurrentFont = CurrentPos = 0;
	*(globaldest++) = CurrentColor = 1;
	State = NORMAL;
	LineDesc = LD;
	LineDesc->Line = d;

	if (!Class[10]) {
		memset(Class, OTHER, 256);
		for(i=0; i<' '; i++) Class[i] = Class[i+128] = SKIP;
		for(i=' '; i<'A'; i++) Class[i] = SYMBOL;
		for(i='A'; i<='Z'; i++) Class[i] = Class[i+32] = ALPHA;
		Class['\t'] = TAB;
		Class['['] = SQBRA;
		Class['('] = Class['#'] = SHSEQ;
		Class[0x0a] = CR;
		Class[0x1b] = ESC;
		Class[0x9b] = CSI;
	}
}

/*
*
* EndAutomaton()
*
* correctly ends the automaton work.
*
*/

void EndAutomaton(void) {

	LineDesc->Line = NULL;
}

/*
*
* ParseASCIIChars()
*
* takes a pointer to a a source buffer and the length of the buffer. Outputs
* to the LineDesc and dest pointers initialized by InitAutomaton().
* This is the real automaton.
*
*/


__regargs void ParseASCIIChars(char *source, int len) {

	register unsigned char *p = source, *dest = globaldest, (*transition)[STATESCOUNT] = Transition, *class = Class;
	register short state = State, currentpos = CurrentPos;
	unsigned char CurrentChar;
	int i;

	while(p-source<len)
		if (state = transition[0][(class[*(dest++) = *(p++)]<<3)+state]) {
			CurrentChar = *(--dest);
			switch(state) {
				case ESCSEQ:	memset(EscSeq, 0, TSEQLEN);
									break;

				case LONGSUBSEQ:	if ((CurrentChar != '[') && (CurrentChar != 0x9b)) EscSeq[strlen(EscSeq)<TSEQLEN-2 ? strlen(EscSeq) : TSEQLEN-2 ] = CurrentChar;
										else if (CurrentChar == 0x9b) memset(EscSeq, 0, TSEQLEN);
										break;

				case ENDLINE:	*(dest++) = 0;
									*(dest++) = ENDOFSTREAM;
									(++LineDesc)->Line = dest;
									*(dest++) = CurrentFont;
									*(dest++) = CurrentColor;
									CurrentPos = 0;
									break;

				case TABADD:	*(dest++) = 0;
									*(dest++) = TABCHAR;
									*(dest++) = CurrentFont;
									*(dest++) = CurrentColor;
									CurrentPos += TabSize;
									break;

				case CHECKSEQ: if (CurrentChar == 'm') {
										ParseEscSeq();
										*(dest++) = 0;
										*(dest++) = CurrentFont;
										*(dest++) = CurrentColor;
									}
									else if (!EscSeq[0]) {
										for(i=0; i<strlen(FONTS); i++) if (CurrentChar == Fonts[i])
											CurrentFont = (CurrentFont & 0x7) | (8*i);
										*(dest++) = 0;
										*(dest++) = CurrentFont;
										*(dest++) = CurrentColor;
									}
									break;

				default:			break;

			}
		}
		else {
			CurrentPos++;
			if (WordWrap && CurrentPos>WordWrap && *p==' ') *p=0x0A;
		}

		globaldest = dest;
		State = state;
		CurrentPos = currentpos;
}


/*
*
* ParseEscSeq()
*
* parses the Esc-sequence and sets CurrentFont, CurrentColor.
*
*/

void ParseEscSeq(void) {

	register char *p = EscSeq, *q;
	register int i;

	do {
		q = p;
		while((*p>='0') && (*p<='9')) p++;
		*(p++)=0;
		i = Atol(q);
		switch(i) {
			case BOLDON:
				CurrentFont |= FSF_BOLD;
				break;
			case ITALICON:
				CurrentFont |= FSF_ITALIC;
				break;
			case UNDERLINEDON:
				CurrentFont |= FSF_UNDERLINED;
				break;
			case BOLDOFF:
				CurrentFont &= ~FSF_BOLD;
				break;
			case ITALICOFF:
				CurrentFont &= ~FSF_ITALIC;
				break;
			case UNDERLINEDOFF:
				CurrentFont &= ~FSF_UNDERLINED;
				break;
			case PLAIN:
				CurrentFont &= ~(FSF_BOLD | FSF_ITALIC | FSF_UNDERLINED);
				CurrentColor = 1;
			default:
				if ((i%10>=0) & (i%10<8))
					switch(i/10) {
						case FOREGROUNDID:
							CurrentColor &= ~7;
							CurrentColor |= i%10;
							break;
						case BACKGROUNDID:
							CurrentColor &= 7;
							CurrentColor |= (i%10)*8;
							break;
						default:
							break;
					}
		}
	} while(*p);
}
