/*
 docCopyright("Jonathan Wright; Steve Dekorte (2002)", 2006)
 docLicense("BSD revised")
 */

#include "IoMessage_opShuffle.h"
#include "IoMap.h"
#include "IoNumber.h"

#define DATA(self) ((IoMessageData *)IoObject_dataPointer(self))

typedef struct IoOpTable {
	char *opString;
	int level;
} IoOpTable;

int IoToken_LevelForOp_(char *s)
{
	IoOpTable ops[] = { 
	{"@",   0},
	{"@@",  0},
	{"'",   0},
	{".",   0},
	{"?",   0},
	{"(",   0},
	{")",   0},
		
	{"**",  1},
		
	{"++",  2},
	{"--",  2},
		
	{"*",   3},
	{"/",   3},
	{"%",   3},
		
	{"+",   4},
	{"-",   4},
		
	{"<<",  5},
	{">>",  5},
		
	{">",   6},
	{"<",   6},
	{"<=",  6},
	{">=",  6},
		
	{"==",  7},
	{"!=",  7},
		
	{"&",   8},
		
	{"^",   9},
		
	{"|",   10},
		
	{"and", 11},
	{"&&",  11},
		
	{"or",  12},
	{"||",  12},
		
	{"..",  13},
		
	{"=",   14},
	{"+=",  14},
	{"-=",  14},
	{"*=",  14},
	{"/=",  14},
	{"%=",  14},
	{"&=",  14},
	{"^=",  14},
	{"|=",  14},
	{"<<=", 14},
	{">>=", 14},
	{":=",  14},
	{"<-",  14},
	{"<->", 14},
	{"->",  14},
		
	{"return", 15},
		
	{",", 16},
	{NULL, 0},
	};
	
	IoOpTable *entry = ops;
	
	while (entry->opString) 
	{
		if (strcmp(entry->opString, s) == 0) 
		{
			return entry->level;
		}
		entry ++;
	}
	
	return -1;
}

// TODO move the IO_OP_MAX_LEVEL define to be adjacent to the op table.
// TODO Set IO_OP_MAX_LEVEL to max-1 or something massive otherwise people playing with custom operators don't have many levels available
#define IO_OP_MAX_LEVEL 17

enum LevelType {ATTACH, ARG, NEW, UNUSED};

typedef struct {
	IoMessage *message;
	enum LevelType type;
	int precedence;
} Level;

void Level_finish(Level *self);
void Level_attachAndReplace(Level *self, IoMessage *msg);
void Level_setAwaitingFirstArg(Level *self, IoMessage *msg, int precedence);

typedef struct {
	Level pool[IO_OP_MAX_LEVEL];
	int currentLevel;
	
	List *stack;
	IoMap *operatorTable;
} Levels;

void Levels_reset(Levels *self)
{
	int i;
	self->currentLevel = 1;
	
	for (i = 0; i < IO_OP_MAX_LEVEL; i ++)
	{
		Level *level = &self->pool[i];
		level->type = UNUSED;
	}
	
	{
		Level *level = &self->pool[0];
		level->message = NULL;
		level->type = NEW;
		level->precedence = IO_OP_MAX_LEVEL;
	}
	
	List_removeAll(self->stack);
	List_append_(self->stack, &self->pool[0]);
}

// --- Levels ----------------------------------------------------------

Levels *Levels_new(IoMessage *msg)
{
	Levels *self = calloc(1, sizeof(Levels));
	IoObject *opTable = IoObject_rawGetSlot_(msg, IoState_symbolWithCString_(msg->state, "OperatorTable"));
	
	self->operatorTable = NULL;
	
	if (opTable)
	{
		IoObject *rightOperators = IoObject_rawGetSlot_(opTable, IoState_symbolWithCString_(msg->state, "rightOperators"));
		
		if (rightOperators)
		{
			if (ISMAP(rightOperators))
			{
				self->operatorTable = rightOperators;
			}
			else
			{
				IoState_error_(msg->state, NULL, "compile error: Message OperatorTable rightOperators is not a Map. %p %s", rightOperators, CSTRING(IoMessage_name(msg)));
			}
		}
	}
	
	self->stack = List_new();
	Levels_reset(self);
	return self;
}

void Levels_free(Levels *self)
{
        List_free(self->stack);
	free(self);
}

inline Level *Levels_currentLevel(Levels *self)
{
	return List_top(self->stack);
}

void Levels_popDownTo(Levels *self, int targetLevel)
{
	Level *level;
	
	while (level = List_top(self->stack), level->precedence <= targetLevel && level->type != ARG)
	{
		Level_finish(List_pop(self->stack));
		self->currentLevel--;
	}
}

void Levels_attachToTopAndPush(Levels *self, IoMessage *msg, int precedence)
{
	Level *level = NULL;
	{
		Level *top = List_top(self->stack);
		Level_attachAndReplace(top, msg);
	}
	
	{
		// TODO: Check for overflow of the pool.
		if (self->currentLevel >= IO_OP_MAX_LEVEL)
		{
			IoState_error_(msg->state, NULL, "compile error: Overflowed operator stack. Only %d levels of operators currently supported.", IO_OP_MAX_LEVEL-1);
		}
		
		level = &self->pool[self->currentLevel++];
		Level_setAwaitingFirstArg(level, msg, precedence);
		List_append_(self->stack, level);
	}
}

void Level_finish(Level *self)
{
	if (self->message)
	{
		IoMessage_rawSetNext(self->message, NULL);
	}
	
	self->type = UNUSED;
}

void Level_attach(Level *self, IoMessage *msg)
{
	switch (self->type)
	{
		case ATTACH:
			IoMessage_rawSetNext(self->message, msg);
			break;
			
		case ARG:
			IoMessage_addArg_(self->message, msg);
			break;
			
		case NEW:
			self->message = msg;
			break;
			
		case UNUSED:
			break;
	}
}

void Level_attachAndReplace(Level *self, IoMessage *msg)
{
	Level_attach(self, msg);
	self->type = ATTACH;
	self->message = msg;
}

void Level_setAwaitingFirstArg(Level *self, IoMessage *msg, int precedence)
{
	self->type = ARG;
	self->message = msg;
	self->precedence = precedence;
}

void Level_setAlreadyHasArgs(Level *self, IoMessage *msg)
{
	self->type = ATTACH;
	self->message = msg;
}

int Levels_levelForOp(Levels *self, char *messageName, IoSymbol *messageSymbol, IoMessage *msg)
{
	if (self->operatorTable)
	{
		IoObject *value = IoMap_rawAt(self->operatorTable, messageSymbol);
		
		if (!value)
		{
			return -1;
		}
		
		if (ISNUMBER(value))
		{
			return IoNumber_asInt((IoNumber*)value);
		}
		else
		{
			IoState_error_(msg->state, msg, "compile error: Value for '%s' in Message OperatorTable is not a number. Values in the OperatorTable are numbers which indicate the precedence of the operator.", messageName);
			return -1; // To keep the compiler happy.
		}
	}
	else
	{
		return IoToken_LevelForOp_(messageName);
	}
}

void Levels_attach(Levels *self, IoMessage *msg, List *expressions)
{
	// TODO clean up this method.
	
	IoSymbol *messageSymbol = IoMessage_name(msg);
	char *messageName = CSTRING(messageSymbol);
	int precedence = Levels_levelForOp(self, messageName, messageSymbol, msg);
	
	// `o a := b c ; d`  becomes  `o setSlot("a", b c) ; d`
	// 
	// a      attaching
	// :=     msg
	// b c    msg->next
	
	if (strcmp(messageName, ":=") == 0 || strcmp(messageName, "=") == 0)
	{
		IoState *state = (IoState *)(msg->state);
		Level *currentLevel = Levels_currentLevel(self);
		IoMessage *attaching = currentLevel->message;
		IoSymbol *setSlotName;
		
		if (attaching == NULL) // `:= b ;`
		{
			// Could be handled as, message(:= 42) -> setSlot(nil, 42)
			
			IoState_error_(state, msg, "compile error: %s requires a symbol to its left.", messageName);
		}
		
		if (IoMessage_argCount(attaching) > 0) // `a(1,2,3) := b ;`
		{
			IoState_error_(state, msg, "compile error: The symbol to the left of %s cannot have arguments.", messageName);
		}
		
		
		{
			// `a := b ;`
			IoSymbol *slotName = DATA(attaching)->name;
			IoSymbol *quotedSlotName = IoSeq_newSymbolWithFormat_(state, "\"%s\"", CSTRING(slotName));
			IoMessage *slotNameMessage = IoMessage_newWithName_returnsValue_(state, quotedSlotName, slotName);
			
			// `a := b ;`  ->  `a("a") := b ;`
			IoMessage_addArg_(attaching, slotNameMessage);
			
			if (strcmp(messageName, ":=") == 0)
			{
				if (isupper(CSTRING(slotName)[0]))
				{
					setSlotName = state->setSlotWithTypeSymbol;
				}
				else
				{
					setSlotName = state->setSlotSymbol;
				}
			}
			else
			{
				setSlotName = state->updateSlotSymbol;
			}
		}
		
		// `a("a") := b ;`  ->  `setSlot("a") := b ;`
		DATA(attaching)->name = IoObject_addingRef_(attaching, setSlotName);
		
		currentLevel->type = ATTACH;
		
		if (IoMessage_argCount(msg) > 0) // `setSlot("a") :=(b c) d e ;`
		{
			// `b c`
			IoMessage *arg = IoMessage_rawArgAt_(msg, 0);
			
                        if (DATA(msg)->next == NULL || IoMessage_isEOL(DATA(msg)->next))
                        {
                                IoMessage_addArg_(attaching, arg);
                        }
                        else
                        {
                                // `()`
                                IoMessage *foo = IoMessage_newWithName_(state, IoState_symbolWithCString_(state, ""));
                                
                                // `()`  ->  `(b c)`
                                IoMessage_addArg_(foo, arg);
                                
                                // `(b c)`  ->  `(b c) d e ;`
                                IoMessage_rawSetNext(foo, DATA(msg)->next);
                                
                                // `setSlot("a") :=(b c) d e ;`  ->  `setSlot("a", (b c) d e ;) :=(b c) d e ;`
                                IoMessage_addArg_(attaching, foo);
                        }
		}
		else // `setSlot("a") := b ;`
		{
			// `setSlot("a") :=` or `setSlot("a") := ;`
			IoMessage *mn = DATA(msg)->next;
			IoSymbol *name = mn ? DATA(mn)->name : NULL;
			IoSymbol *semi = ((IoState *)(msg->tag->state))->semicolonSymbol;
			
			//if (mn == NULL || IoMessage_isEOL(mn))
			if (mn == NULL || name == semi)
			{
				IoState_error_(state, msg, "compile error: %s must be followed by a value.", messageName);
			}
			
			// `setSlot("a") := b c ;`  ->  `setSlot("a", b c ;) := b c ;`
			IoMessage_addArg_(attaching, DATA(msg)->next);
		}
		
		// process the value (`b c d`) later  (`setSlot("a", b c d) := b c d ;`)
		if (DATA(msg)->next != NULL && !IoMessage_isEOL(DATA(msg)->next))
		{
			List_push_(expressions, DATA(msg)->next);
		}
		
		{
			IoMessage *last = msg;
			while (DATA(last)->next != NULL && !IoMessage_isEOL(DATA(last)->next))
			{
				last = DATA(last)->next;
			}

			IoMessage_rawSetNext(attaching, DATA(last)->next);
			
			// Continue processing in IoMessage_opShuffle loop
			IoMessage_rawSetNext(msg, DATA(last)->next);
			
                        if (last != msg)
                        {
                                IoMessage_rawSetNext(last, NULL);
                        }
		}
		
		// make sure b in `1 := b` gets executed
		IoMessage_cachedResult_(attaching, NULL);
	}
	else if (IoMessage_isEOL(msg))
	{
		Levels_popDownTo(self, IO_OP_MAX_LEVEL-1);
		Level_attachAndReplace(Levels_currentLevel(self), msg);
	}
	else if (precedence != -1)
	{
		if (IoMessage_argCount(msg) > 0)
		{
			Level_setAlreadyHasArgs(Levels_currentLevel(self), msg);
		}
		else
		{
			Levels_popDownTo(self, precedence);
			Levels_attachToTopAndPush(self, msg, precedence);
		}
	}
	else
	{
		Level_attachAndReplace(Levels_currentLevel(self), msg);
	}
}

void Levels_nextMessage(Levels *self)
{
	Level *level;
	
	while ((level = List_pop(self->stack)))
	{
		Level_finish(level);
	}
	
	Levels_reset(self);
}

void IoMessage_opShuffle_(IoMessage *self)
{
	if (IoObject_rawGetSlot_(self, IOSTATE->opShuffleSymbol) && IoMessage_name(self) != IOSTATE->noShufflingSymbol)
	{
		IoMessage_locals_performOn_(IOSTATE->opShuffleMessage, IOSTATE->lobby, self);
	}
}

IoMessage *IoMessage_opShuffle(IoMessage *self, IoObject *locals, IoMessage *m)
{
	Levels *levels = Levels_new(self);
	List *expressions = List_new();
	
	List_push_(expressions, self);
	
	while (List_size(expressions) >= 1)
	{
		IoMessage *n = List_pop(expressions);
		
		do
		{
			Levels_attach(levels, n, expressions);
			List_appendSeq_(expressions, DATA(n)->args);
		} while (n = DATA(n)->next);
		
		Levels_nextMessage(levels);
	}

    List_free(expressions);
	Levels_free(levels);

	return self;
}



syntax highlighted by Code2HTML, v. 0.9.1