/* symnode.cpp */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "symnode.h"

SymNode::SymNode (SymNode *n, char *record)
{
int pos;
	fileName = NULL;
	funName = NULL;
	symName = NULL;
	level = -1;
	block = -1;
	line = -1;
	address = -1;
	endAddress = -1;
	next = n;
	address = -1;
	endAddress = -1;
	switch (record [0]) {
		case 'F' :	// Function Records
		case 'S' :	// Symbol Records
			switch (record [2]) {
				case 'G' :	// Global						S:G$<Name><$><Level><$><Block><(> ...
					type = GLOBAL;
					pos = 4;
					symName = getName (record, '$', pos);
					level = getNumber (record, '$', pos);
					block = getNumber (record, '(', pos);
					break;
				case 'F' :	// File (Static)				S:F<Filename><$><Name><$><Level><$><Block><(> ...
					type = STATIC;
					pos = 3;
					fileName = getName (record, '$', pos);
					symName = getName (record, '$', pos);
					level = getNumber (record, '$', pos);
					block = getNumber (record, ':', pos);
					break;
				case 'L' :	// Local (Function)				S:L { <function> | ``-null-`` }<$><Name><$><Level><$><Block><(> ...
					type = LOCAL;
					pos = 3;
					funName = getName (record, '$', pos);
					symName = getName (record, '$', pos);
					level = getNumber (record, '$', pos);
					block = getNumber (record, '(', pos);
					break;
			}
			// ... <{><Size><}><DCLType> <,> {<DCLType> <,>} <:> <Sign> <)><,><AddressSpace><,><OnStack><,><Stack><,><[><Reg><,>{<Reg><,>}<]> 
			pos ++;		// Skip <{>
			varSize = getNumber (record, '}', pos);
			varType = getVarType (record, pos);
			switch (varType) {
				case ARRAY :
				case FUNCTION :
				case POINER :
				case CODEPOINTER :
				case XDPOINTER :
				case DPOINTER :
				case PPOINER :
				case IPOINTER :
					if (varType == ARRAY) {
						numEl = getNumber (record, ',', pos);
					} else {
						pos ++;	// skips ","
					}
					secVarType = getVarType (record, pos);
					switch (secVarType) {
						case POINER :
						case CODEPOINTER :
						case XDPOINTER :
						case DPOINTER :
						case PPOINER :
						case IPOINTER :
							thirdVarType = getVarType (record, pos);
							if (thirdVarType == STRUCT) {
								recType = getName (record, ':', pos);
							} else {
								pos ++;	// skips ","
							}
							break;
						case STRUCT :	// Pointer to struct: get name
							recType = getName (record, ':', pos);
							break;
					}
					break;
				case STRUCT :
					recType = getName (record, ':', pos);
					break;
				default :
					pos ++;	// Skips ':'
					break;
			}
			Signed = record [pos++] == 'S';
			pos ++;	// skips ")"
			pos ++;	// skips ","
			switch (record [pos++]) {
				case 'A':
					addrSpace = XSTACK;
					break;
				case 'B':
					addrSpace = STACK;
					break;
				case 'C':
					addrSpace = CODE;
					break;
				case 'D':
					addrSpace = SCODE;
					break;
				case 'E':
					addrSpace = LORAM;
					break;
				case 'F':
					addrSpace = XRAM;
					break;
				case 'G':
					addrSpace = RAM;
					break;
				case 'H':
					addrSpace = BITSPACE;
					break;
				case 'I':
					addrSpace = SFRSPACE;
					break;
				case 'J':
					addrSpace = SBITSPACE;
					break;
				case 'R':
					addrSpace = REGISTER;
					break;
				default:
					addrSpace = UNDEFINED;
					break;
			};
			pos ++;	// skips ","
			onStack = getNumber (record, ',', pos) != 0;
			stkOffs = getNumber (record, ',', pos);
			break;
		case 'L' :	// Linker records
			switch (record [2]) {	// Subtypes of linker record
				case 'G' :	// Global						L:G$<name><$><level><$><block><:><address>
					type = GLOBAL;
					pos = 4;
					symName = getName (record, '$', pos);
					level = getNumber (record, '$', pos);
					block = getNumber (record, ':', pos);
					address = getNumber (record, '\n', pos, 16);
					break; 
				case 'F' :	// File (Static)				L:F<filename><$><name><$><level><$><block><:><address> 
					type = STATIC;
					pos = 3;
					fileName = getName (record, '$', pos);
					symName = getName (record, '$', pos);
					level = getNumber (record, '$', pos);
					block = getNumber (record, ':', pos);
					address = getNumber (record, '\n', pos, 16);
					break; 
				case 'L' :	// Local (Function)				L:L<function><$><name><$><level><$><block><:><address> 
					type = LOCAL;
					pos = 3;
					funName = getName (record, '$', pos);
					symName = getName (record, '$', pos);
					level = getNumber (record, '$', pos);
					block = getNumber (record, ':', pos);
					address = getNumber (record, '\n', pos, 16);
					break; 
				case 'A' :	// ASM line record				L:A$<Filename><$><Line><:><EndAddress> 
					type = ASMLINE;
					pos = 4;
					fileName = getName (record, '$', pos);
					line = getNumber (record, ':', pos);
					endAddress = address = getNumber (record, '\n', pos, 16);
					break; 
				case 'C' :	// C line record				L:C$<Filename><$><Line><$><Level><$><Block><:><EndAddress>
					type = CLINE;
					pos = 4;
					fileName = getName (record, '$', pos);
					line = getNumber (record, '$', pos);
					level = getNumber (record, '$', pos);
					block = getNumber (record, ':', pos);
					endAddress =address = getNumber (record, '\n', pos, 16);
					break;
				case 'X' :	// End Records 
					switch (record [3]) {
						case 'G' :	// Global				<L><:><X><G><$><name><$><level><$><block><:><Address> 
							type = GLOBAL;
							pos = 5;
							symName = getName (record, '$', pos);
							level = getNumber (record, '$', pos);
							block = getNumber (record, ':', pos);
							endAddress = getNumber (record, '\n', pos, 16);
							break; 
						case 'F' :	// File (static)		<L><:><X>F<filename><$><name><$><level><$><block><:><Address> 
							type = STATIC;
							pos = 4;
							fileName = getName (record, '$', pos);
							symName = getName (record, '$', pos);
							level = getNumber (record, '$', pos);
							block = getNumber (record, ':', pos);
							endAddress = getNumber (record, '\n', pos, 16);
							break; 
						case 'L' :	// Local (funcition)	<L><:><X>L<functionName><$><name><$><level><$><block><:><Address>
							type = LOCAL;
							pos = 4;
							funName = getName (record, '$', pos);
							symName = getName (record, '$', pos);
							level = getNumber (record, '$', pos);
							block = getNumber (record, ':', pos);
							endAddress = getNumber (record, '\n', pos, 16);
							break; 
						default:
							break;
					}
					break;
			}
			break;
	}
}

void SymNode::merge (SymNode *tmpSymbol)
{
	if (tmpSymbol -> address >= 0) {
		address = tmpSymbol -> address;
	}
	if (tmpSymbol -> endAddress >= 0) {
		endAddress = tmpSymbol -> endAddress;
	}
}

varTypes SymNode::getVarType (char *source, int &position)
{
varTypes t = NONE;
	if (source [position++] == 'D') {
		switch (source [position++]) {
			case 'A' :
				t = ARRAY;
				break;
			case 'F' :
				t = FUNCTION;
				break;
			case 'G' :
				t = POINER;
				break;
			case 'C' :
				t = CODEPOINTER;
				break;
			case 'X' :
				t = XDPOINTER;
				break;
			case 'D' :
				t = DPOINTER;
				break;
			case 'P' :
				t = PPOINER;
				break;
			case 'I' :
				t = IPOINTER;
				break;
		}
	} else {	// "S*"
		switch (source [position++]) {
			case 'L' :
				t = LONG;
				break;
			case 'I' :
				t = INT;
				break;
			case 'C' :
				t = CHAR;
				break;
			case 'S' :
				t = SHORT;
				break;
			case 'V' :
				t = VOID;
				break;
			case 'F' :
				t = FLOAT;
				break;
			case 'T' :
				t = STRUCT;
				break;
			case 'X' :
				t = BIT;
				break;
			case 'B' :
				t = BITFIELD;
				break;
		}
	}
	return t;
}

char *SymNode::getName (char *source, char term, int &position)
{
int len;
char *sym;
	for (len = 0;source [position + len]>=' ' && source [position + len] != term; len ++)
		;
	sym = new char [len + 1];
	strncpy (sym,source + position, len);
	sym [len] = 0;	// To be shure, add the terminator
	position += len;
	if (source [position] == term) {
		position ++;
	}
	return sym;
}

int SymNode::getNumber (char *source, char term, int &position, int base)
{
int len;
int val;
	val = strtol (source + position, NULL, base);
	for (len = 0;source [position + len] && source [position + len] != term; len ++)
		;
	position += len;
	if (source [position] == term) {
		position ++;
	}
	return val;
}

int SymNode::hasIdx (idxs idx)
{
	switch (idx) {
		case LINEIDX:
			return fileName != NULL && line >= 0;
		case ADDRIDX:
			return address >= 0;
		case SYMIDX:
			return symName != NULL;
	}
	return 0;
}

/* Confronta con il nodo "to".
 * >0 -> this > to
 * =0 -> this = to
 * <0 -> this < to
 *
 * Nella ricerca "to" e' il nodo che cerco, "this" il nodo dell'albero
 */
int SymNode::compare (idxs idx, SymNode *to)
{
int pre;
	switch (idx) {
		case LINEIDX:
			pre = strcmp (fileName, to->fileName);
			if (!pre) {
				return line - to->line;
			}
			return pre;
		case ADDRIDX:
			if (address == to->address) {
				if (type == GET_FIRST || to->type == GET_FIRST) {
					return 0;
				}
				return type - to->type;
			}
			return address - to->address;
		case SYMIDX:
			pre = strcmp (symName, to->symName);
			if (!pre && funName && to->funName) {
				pre = strcmp (funName, to->funName);
			}
			if (!pre) {	// There is a local symbol to use
				if (block == to->block) {
					return to->level - level;
				}
				return to->block - block;
			}
			return pre;
		case SYMSEARCHMODE:
			pre = strcmp (symName, to->symName);
			if (!pre && funName && to->funName) {
				pre = strcmp (funName, to->funName);
			}
			if (!pre) {	// There is a local symbol to use
				if (block == to->block) {
					return (to->level < level) ? -1 : 0;
				}
				return (to->block < block) ? -1 : 0;
			}
			return pre;
	}
	return 0;
}

int SymNode::isFile (const char *file)
{
	return strcmp (fileName, file);
}

SymNode::SymNode ()
{
	next = NULL;
	fileName = NULL;
	funName = NULL;
	symName = NULL;
  	varType = NONE;
  	varSize = 0;
  	recType = NULL;
	level = 0;
	block = 0;
	line = -1;
	address = -1;
	endAddress = -1;
}


SymNode::~SymNode ()
{
	if (fileName) {
		delete fileName;
	}
	if (funName) {
		delete funName; 
	}
	if (symName) {
		delete symName; 
	}
	/* if (recType) {
		delete recType; 
	} */
}

#ifdef TREEDEBUG
void SymNode::debPrint ()
{
	printf ("File %s Fun %s Sim %s L=%d B=%d Lin %d Ind %04x Fine %04x\n",
		(fileName) ? fileName : "<0>",
		(funName) ? funName : "<0>",
		(symName) ? symName : "<0>",
		level,
		block,
		line,
		address & 0X1ffff,
		endAddress & 0X1ffff);
}
#endif


