var curStart = 2;
var readingFunc = false;
var curFuncName = '';
var curFuncBody = '';
var curFuncInd = null;
var curCallIndex = new Array();
var curNumOfParam = null;
var numOpenBrackets = 0;
var variables = new Array();
var stringCache = new Array();
var functions = new Array();
var cachePrefix = "Scache_x00F000";
var intRegExp = /[^0-9]/
var digitRegExp = /[0-9]/
var possFuncStr = /^[a-zA-Z0-9]*\([a-zA-Z0-9,]*\)$/
var strRegExp = /^"[0-9,a-z,A-Z,\s,_,\W]*"$/
var floatRegExp = /[0-9]*\.[0-9]*/
var invalidFormatErrorMsg = "Invalid Value Format";
var reservedVariableNameMsg = "That is not a valid variable name: reserved keyword";
var invalidOperatorMsg = "Invalid Operator for that Data-type";
variables.unshift(new variable("null",null));
variables.unshift(new variable('true',true));
variables.unshift(new variable('false',false));


function check(){
	var fullVal = document.theForm.source.value;
	var curCommand = fullVal.substring(curStart, fullVal.length);
	//quick hack to avoid annoying IE issue
	if(curCommand.charAt(0) == '>' && navigator.appName == 'Microsoft Internet Explorer'){
		curCommand = curCommand.substring(1,curCommand.length);
	}
	var response = '';
	if(curCommand.indexOf('\n') >= 0){
	if(!readingFunc && curCommand.indexOf('function') < 0){
		if(curCommand.trim() == 'clear'){
			fullVal = '>>';
			reset();
		}
		else{
			if(navigator.appName == 'Microsoft Internet Explorer')
				response = interpret(curCommand.substring(0,curCommand.length-2));
			else
				response = interpret(curCommand.substring(0,curCommand.length-1));
			
			if(response != '' || typeof response == 'boolean'){
				response = response + '\n'
			}
			fullVal = fullVal + response + ">>";
			stringCache = new Array();
			document.theForm.source.value = fullVal;
			
			
		}
	}
	else if(curCommand.indexOf('}') < 0 && (readingFunc || (curCommand.indexOf('function') >= 0 && curCommand.indexOf('{') > 0))){
		if(curCommand.indexOf('function') >= 0){
			curCommand = curCommand.trim();
			if(!readingFunc)
				readingFunc = true;
			else
				throw("Cannot declare a function within another function");
			try{
				readFunctionHeader(curCommand);
			}catch(error){
				readingFunc = false;
				fullVal = fullVal + error + "\n>>";
				document.theForm.source.value = fullVal;
			}
			numOpenBrackets = 1;
		}
		else{
			if(curCommand.indexOf('{') >= 0){
				numOpenBrackets = numOpenBrackets + 1;
			}
			functions[curFuncInd].body = functions[curFuncInd].body + curCommand;
		}
			
	}
	else if(readingFunc && curCommand.indexOf('}') >= 0){
		if(numOpenBrackets == 1){
			readingFunc = false;
			curFuncInd = null;
			curFuncName = '';
			curFuncBody = '';
			fullVal = fullVal + ">>";
			document.theForm.source.value = fullVal
			numOpenBrackets = 0
		}
		else{
			functions[curFuncInd].body = functions[curFuncInd].body + curCommand;
			numOpenBrackets = numOpenBrackets - 1;
		}
	}
	else{
		fullVal = fullVal + ">>";
		document.theForm.source.value = fullVal
	}
	curStart = fullVal.length;
	document.theForm.source.scrollTop = document.theForm.source.scrollHeight;
	}
}

function interpret(newCommand){
	if(newCommand.indexOf('=') > 0){
		
		var items = newCommand.split('=');
		if(items.length == 2){
			var name = items[0];
			var value = items[1];
			name = name.trim();
			value = value.trim();
			var retValue = '';
			try{
				retValue = processAssignment(name, value);
			}catch(error){
				retValue = error + '\n';
			}
			return retValue;
		}
		else{
			throw('Improper Assignment Format');
		}
	}
	else{
		newCommand = newCommand.trim();
		var ind = null;
		if(curCallIndex.length > 0){
			ind = isALocalVar(newCommand);
			if(ind == -1){
				ind = isAVariable(newCommand);
				if(ind != -1){
					return variables[ind].value;
				}
			}
			else{
				return curCallIndex[curCallIndex.length - 1].vars[ind].value;
			}
		}
		else{
			ind = isAVariable(newCommand);
			if(ind != -1){
				return variables[ind].value;
			}
		}
		if(newCommand.indexOf('+')>0 || newCommand.indexOf('-')>0 || newCommand.indexOf('*')>0 || newCommand.indexOf('/')>0){
			try{
				var newCom = convertToNoParen(newCommand);
				var result = makeCalculation(newCom);
			}catch(error){
				result = error;
			}
			return result;
		}
		else if(newCommand.indexOf('(') > 0 && newCommand.indexOf(')') == newCommand.length-1){
			var funcName = newCommand.substring(0,newCommand.indexOf('('));
			var funcInd = isAFunction(funcName);
			var funcCallVal = '';
			if(funcInd != -1){
				var funcCall = newCommand;
				funcCall = funcCall.replace(funcName,'');
				funcCall = funcCall.replace('(','');
				funcCall = funcCall.replace(')','');
				if(funcCall.trim() != ''){
					var paramValues = funcCall.split(',');
				}
				else{
					var paramValues = new Array();
				}
				curCallIndex.push(new callIndex(funcInd));
				try{
					funcCallVal = evaluateFunctionCall(funcInd,paramValues);
				}catch(error){
					
				}
				curCallIndex.pop();
				return funcCallVal;
			}
		}
	}
	return '';
}


function processAssignment(name, value){
	var nameInd = null;
	var realValue = null;
	if(name.indexOf(' ') < 0 && checkVarStart(name.charAt(0))){
		if(curCallIndex.length > 0){
			nameInd = isALocalVar(name);
			if(nameInd == -1){
				nameInd = isAVariable(name);
				realValue = findValue(value);
				if(nameInd>-1){
					if(name!='null')
						variables[nameInd].value = realValue;
					else
						throw(reservedVariableNameMsg);
				}
				else{
					if(name != 'clear'){
						curCallIndex[curCallIndex.length - 1].vars.push(new variable(name, realValue));
					}
					else{
						throw(reservedVariableNameMsg);
					}
				}
			}
			else{
				realValue = findValue(value);
				if(nameInd>-1){
					if(name!='null')
						curCallIndex[curCallIndex.length - 1].vars[nameInd].value = realValue;
					else
						throw(reservedVariableNameMsg);
				}
				else{
					if(name != 'clear'){
						curCallIndex[curCallIndex.length - 1].vars.push(new variable(name, realValue));
					}
					else{
						throw(reservedVariableNameMsg);
					}
				}
				
			}
		}
		else{
			nameInd = isAVariable(name);
			realValue = findValue(value);
			if(nameInd>-1){
				if(name!='null')
					variables[nameInd].value = realValue;
				else
					throw(reservedVariableNameMsg);
			}
			else{
				if(name != 'clear'){
					variables.unshift(new variable(name, realValue));
				}
				else{
					throw(reservedVariableNameMsg);
				}
			}
		}
	}
		else
			throw("Error in Variable name format!");
	return '';
}
function checkVarStart(varName){
	var varStartRegExp = /[a-zA-Z]/
	var varRegExp = /[a-zA-Z_]+/
	if(varStartRegExp.test(varName.charAt(0))){
		return (varRegExp.test(varName));
	}
	else
		return false;
}

function evaluateFunctionCall(index,paramArray){
	
	if(paramArray.length == functions[index].parameterList.length){
		for(var ind = 0;ind<paramArray.length;ind++){
			curCallIndex[curCallIndex.length - 1].vars.push(new variable(functions[index].parameterList[ind], findValue(paramArray[ind])));
		}
	}
	else{
		throw("Incorrect Number of Parameters");
	}
	var funcLines = functions[index].body.split('\n');
	var returnVal = '';
	var x = 0;
	while(x < funcLines.length){
		if(funcLines[x].indexOf('return ') >= 0){
			returnVal = funcLines[x].replace('return ','');
			returnVal = returnVal.trim();
			var interpretedVal = interpret(returnVal);
			if(interpretedVal == ''){
				return findValue(returnVal);
			}
			else{
				return interpretedVal;
			}
		}
		else if(funcLines[x].trim().indexOf('if ') >= 0 || funcLines[x].trim().indexOf('if(') >= 0){
			if(funcLines[x].indexOf('{') > 0){
				var ifHeader = funcLines[x].replace('if','');
				ifHeader = ifHeader.replace('(','');
				ifHeader = ifHeader.replace(')','');
				ifHeader = ifHeader.replace('{','');
				var endOfIf = findClosingBracket(funcLines, x+1);
				if(findValue(ifHeader)){					
					x = x+1;
				}
				else{
					x = endOfIf
				}
			}
			else{
				throw("Missing Bracket on if statement");
			}
		}
		else{
			interpret(funcLines[x]);
			x = x+1;
		}
	}
	return '';
}
function processIfBlock(codeLines,startInd,endInd){
	
	
	
}
function convertToNoParen(exp){
	var endVal = '';
	var Stack = new Array();
	var n = 0;
	var ind = 0;
	var full = '';
	while(n < exp.length){
		var curChar = exp.charAt(n);
		if(!isOperator(curChar) && curChar != '(' && curChar != ')' && curChar != ' '){
			if(curChar == '\"'){
				ind = findEndOfString(exp,n);
				full = exp.substring(n,ind);
				var varForCache = cachePrefix + n;
				stringCache.unshift(new variable(varForCache, full));
				if(endVal == '')
					endVal = varForCache;
				else
					endVal = endVal + ' ' + varForCache;
				
				n = ind;
			}
			else{
				ind = findEndOfNumber(exp,n);
				full = exp.substring(n,ind);
				if(endVal == '')
					endVal = full;
				else
					endVal = endVal + ' ' + full;
				
				n = ind;
			}
		}
		else if(isOperator(curChar)){
			while(Stack.length > 0 && isOperator(Stack[Stack.length-1]) && isHigherPriority(Stack[Stack.length-1],curChar)){
				var op = Stack.pop();
				if(endVal == '')
					endVal = op;
				else
					endVal = endVal + ' ' + op;
			}
			Stack.push(curChar);
			n = n+1;
		}
		else if(curChar == '('){
			Stack.push(curChar);
			n = n+1;
		}
		else if(curChar == ')'){
			var popped = Stack.pop();
			while(popped != '('){
				if(endVal == '')
					endVal = popped;
				else
					endVal = endVal + ' ' + popped;
				
				popped = Stack.pop();
			}
			n = n+1;
		}
		else
			n = n+1;
		
	}
	while(Stack.length > 0){
		var p = Stack.pop();
		endVal = endVal + ' ' + p;
	}
	return endVal;
	
}

function makeCalculation(S){
	var items = S.split(' ');
	var stack = new Array();
	var i = 0;
	for(i=0;i<items.length;i++){
		if(isOperator(items[i])){
			var first = stack.pop();
			if(typeof first =='string')
				first = findValue(first);
			var second = stack.pop();
			if(typeof second=='string')
				second = findValue(second);
			var result = compute(items[i],second,first);
			stack.push(result);
		}
		else if(items[i] != '' && items[i] != ' ')
			stack.push(items[i]);
	}
	return stack.pop();
			
	
}

function compute(op, f, s){
	if(op=='+'){
		var fType = typeof f;
		var sType = typeof s;
		if(sType == 'string' && fType == 'string'){
			return f.substring(0,f.length-1) + s.substring(1,s.length);
		}
		else
			return f+s;
	}
	else if(op=='-'){
		var fType = typeof f;
		var sType = typeof s;
		if(sType == 'string' || fType == 'string'){
			throw(invalidOperatorMsg);
		}
		else
			return f-s;
	}
	else if(op=='*'){
		var fType = typeof f;
		var sType = typeof s;
		if(sType == 'string' || fType == 'string'){
			throw(invalidOperatorMsg);
		}
		else
			return f*s;
	}
	else{
		var fType = typeof f;
		var sType = typeof s;
		if(sType == 'string' || fType == 'string'){
			throw(invalidOperatorMsg);
		}
		else
			return f/s;
	}
}
function evaluateBool(string){
	var items = null;
	var firstVal = null;
	var secondVal = null;
	if(string.indexOf('==') > 0){
		items = string.split('==');
		if(items.length == 2){
			firstVal = findValue(items[0]);
			secondVal = findValue(items[1]);
			return (firstVal == secondVal);
		}
		else{
			throw("Invalid Boolean Format");
		}
	}
	else if(string.indexOf('>=') > 0){
		items = string.split('>=');
		if(items.length == 2){
			firstVal = findValue(items[0]);
			secondVal = findValue(items[1]);
			return (firstVal >= secondVal);
		}
		else{
			throw("Invalid Boolean Format");
		}
	}
	else if(string.indexOf('<=') > 0){
		items = string.split('<=');
		if(items.length == 2){
			firstVal = findValue(items[0]);
			secondVal = findValue(items[1]);
			return (firstVal <= secondVal);
		}
		else{
			throw("Invalid Boolean Format");
		}
	}
	else if(string.indexOf('>') > 0){
		items = string.split('>');
		if(items.length == 2){
			firstVal = findValue(items[0]);
			secondVal = findValue(items[1]);
			return (firstVal > secondVal);
		}
		else{
			throw("Invalid Boolean Format");
		}
	}
	else if(string.indexOf('<') > 0){
		items = string.split('<');
		if(items.length == 2){
			firstVal = findValue(items[0]);
			secondVal = findValue(items[1]);
			return (firstVal < secondVal);
		}
		else{
			throw("Invalid Boolean Format");
		}
	}
	else{
		throw("Invalid Boolean Format");
	}
	
}

function isHigherPriority(first, sec){
	return ((first == '*' || first == '/') && (sec == '+' || sec == '-'));
}
		

function isOperator(S){
	return (S == '+' || S == '-' || S == '*' || S == '/');
}
function findEndOfNumber(string, start){
	var end = start;
	while(!isOperator(string.charAt(end)) && (string.charAt(end) != '(') && 
		(string.charAt(end) != ')') && (string.charAt(end) != ' ') && end < string.length){
		end = end+1;
		if(string.charAt(end) == '('){
			if(isAFunction(string.substring(start,end) != -1)){
				while(string.charAt(end) != ')'){
					end = end+1
					if(end  > string.length){
						throw("Missing Ending Parenthese on function call");
					}
				}
				return end+1;
			}
		}
	}
	return end;
}
function findEndOfString(string, start){
	var end = start;
	do{
		end = end+1;
		if(end > string.length)
			throw("Missing Ending Quote for String Value");
		
	}while(string.charAt(end) != '\"' && end <= string.length)
	return end+1;
}
function findClosingBracket(array, startInd){
	var numBrackets = 1;
	var i = startInd;
	var endInd = -1;
	while(i < array.length){
		if(array[i].indexOf('{') > 0 && array[i].split('{').length == 2){
			numBrackets = numBrackets+1;
		}
		else if(array[i].indexOf('}') >=0 && array[i].split('}').length  == 2){
			if(numBrackets == 1){
				endInd = i;
			}
			else{
				numBrackets = numBrackets - 1;
			}
		}
		i = i+1;
	}
	if(i>=array.length && endInd == -1){
		throw("No Closing Bracket");
	}
	return endInd;
}

function findValue(V){
	var ind = -1;
	if(typeof V == 'string'){//encloses whole function
	
	V = V.trim();
	if(V.indexOf('cache') >= 0){
		ind = scanStringCache(V);
	}
	if(ind == -1){
		if(curCallIndex.length > 0){
			ind = isALocalVar(V);
			if(ind != -1){
				return curCallIndex[curCallIndex.length - 1].vars[ind].value;
			}
		}
	}
	else{
		return stringCache[ind].value;
	}
	if(ind == -1){
		ind = isAVariable(V);
		if(ind > -1){
			return variables[ind].value;
		}
		else if(V.indexOf('(') > 0 && V.indexOf(')') == V.length-1){
			var funcName = V.substring(0,V.indexOf('('));
			var funcInd = isAFunction(funcName);
			var funcCallVal = '';
			if(funcInd != -1){
				var funcCall = V;
				funcCall = funcCall.replace(funcName,'');
				funcCall = funcCall.replace('(','');
				funcCall = funcCall.replace(')','');
				var paramValues = funcCall.split(',');
				for(var s=0;s<paramValues.length;s++){//evaluate params before Call is made
					paramValues[s]=findValue(paramValues[s]);
				}
				curCallIndex.push(new callIndex(funcInd));
				try{
					funcCallVal = evaluateFunctionCall(funcInd,paramValues);
				}catch(error){
					
				}
				curCallIndex.pop();
				return funcCallVal;
			}
		}
		else if(V.indexOf('+')>0 || V.indexOf('-')>0 || V.indexOf('*')>0 || V.indexOf('/')>0){
			var newCom = convertToNoParen(V);
			var result = makeCalculation(newCom);
			return result;
		}
		else if(V.indexOf('==')>0 || V.indexOf('>=')>0 || V.indexOf('<=')>0 || V.indexOf('>')>0 || V.indexOf('<')>0){
			return evaluateBool(V);
		}
		else{
			if(strRegExp.test(V)){
				return V;
			}
			else if(!intRegExp.test(V)){
				return parseInt(V);
			}
			else if(V.indexOf('.') > 0){
				var array = V.split('.');
				if(array.length == 2){
					if(!intRegExp.test(array[0]) && !intRegExp.test(array[1]))
						return parseFloat(array[0]+'.'+array[1]);
					else
						throw(invalidFormatErrorMsg);
				}
				else
					throw(invalidFormatErrorMsg);
			}
			else{
				throw(invalidFormatErrorMsg);
			}
		}	
	}
	else
		return curCallIndex[curCallIndex.length -1].vars[ind].value;
}
return V;
}
function scanStringCache(name){
	for(i = 0; i<stringCache.length;i++){
		if(stringCache[i].name == name){
			return i;
		}
	}
	return -1;
}
function isAVariable(name){
	var check = -1;
	if(curCallIndex.length > 0){
		check = isALocalVar(name);
	}
	if(check == -1){
		for (i = 0; i<variables.length;i++){
				if(variables[i].name == name){
					return i;
				}
		}
	}
	else{
		return check;
	}
	return -1;
}
function isALocalVar(name){
	var searchIndex = curCallIndex[curCallIndex.length -1];
	for(i=0; i<curCallIndex[curCallIndex.length -1].vars.length; i++){
		if(curCallIndex[curCallIndex.length -1].vars[i].name == name){
			return i;
		}
	}
	return -1;
}
function isAFunction(name){
	for (i = 0; i<functions.length;i++){
			if(functions[i].name == name){
				return i;
			}
	}
	return -1;
}
function readFunctionHeader(head){
	
	var curHead = head.replace('function','');
	var indOfParamStart = curHead.indexOf('(');
	if(indOfParamStart < 0)
		throw("Missing parentheses for Parameters");
	else{
		var name = curHead.substring(0,indOfParamStart);
		name = name.trim();
		if(name == '')
			throw("No Function Name Specified");
		else
			curFuncName = name;
		curHead = curHead.replace(name,'');
		curHead = curHead.replace('(','');
		curHead = curHead.replace(')','');
		curHead = curHead.replace('{','');
		curHead = curHead.trim();
		var paramArray = new Array();
		if(curHead != ''){
			paramArray = curHead.split(',');
		}
		for(n = 0; n<paramArray.length;n++){
			paramArray[n] = paramArray[n].trim();
		}
		functions.unshift(new func(curFuncName, paramArray));
		curFuncInd = 0;
	}
}

String.prototype.trim = function () {
    return this.replace(/^\s+|\s+$/g, "");
};

function variable(n, v){
	this.name = n
	this.value = v
}
function func(name, parameters){
	this.name = name;
	this.body = '';
	this.variableList = new Array();
	this.parameterList = parameters;
}

function callIndex(ind){
	this.ind = ind;
	this.vars = new Array();
}

function reset(){
	document.theForm.source.value = ">>";
	curStart = 2;
	stringCache = new Array();
	functions = new Array();
	variables = new Array();
	variables.unshift(new variable("null",null));
	variables.unshift(new variable('true',true));
	variables.unshift(new variable('false',false));
}