#include "script.hpp"


//
// Faster float to long  (Always rounds to nearest)  ie: -2.7 goes to 3
//
extern DWORD FindWordResult;
inline long float2long( float fval )
{
	fval += 3 << 22;
	return ((*(long*)&fval)&0x007fffff) - 0x00400000;
}

//
// Return next value - but no pointer to variable
//
float FloatGetNextExpression()
{
	long* tv=pLastVariable;
	VAR_SCOPE tlv=LastVariableScope;
	float result=FloatGetExpression();
	pLastVariable=tv;
	LastVariableScope=tlv;
	return result;
}




//
// Get next value
//
float FloatGetExpression()
{
	float Stack[32];
	int StackOp[32];	
	float result=0;
	int NextOp, sp=0;

	while(1)
	{
		result = FloatGetValue();			// Get next value and operator
		NextOp = FloatGetOperator();
		if( *pLine==' ' )				// Skip any spaces
			pLine++;

		while( (sp!=0) && (NextOp&0xff) <= (StackOp[sp-1]&0xff) )			// Changed to <= to fix 100-60-20 bug
		{
			result=FloatDoOperator( Stack[sp-1], result, StackOp[sp-1] );
			sp--;
		}								// Pop and execute lower priority operators

		if( NextOp==0 )					// Reached end of expression?
			break;
	
		if( sp==32 )
			ScriptError("Expression too complex");

		Stack[sp]=result;			// Else, save value and operator
		StackOp[sp++]=NextOp;
	}
	return result;
}

//
//
//Precedence	Operator
//
//		80		  () parenthesis
//		75		  ~ (Compliment)
//		70		  ! (not)
//		65		  - (unary minus)
//		60		  + (unary plus)
//		55		  * / %
//		50		  + -
//		45		  << >>
//		40		  < <= > >=
//		35		  = == != <>		(Note both forms of = and <> are allowed)
//		30		  &
//		25		  ^
//		20		  |
//		15		  && AND			(Note both forms of AND are allowed)
//		10		  || OR				(Note both forms of OR are allowed)
//
// Called by GetExpression
//
float FloatDoOperator( float Value1, float Value2, int Operator )
{
	switch( Operator>>8 )
	{
		case 1:
			return Value1-Value2;
		case 2:
			return Value1+Value2;
		case 3:
			if( Value2==0 )
				ScriptError("Division by zero");
			return Value1/Value2;
		case 4:
			return Value1*Value2;
			///
		case 5:
			if( Value2==0 )
				ScriptError("Division by zero");
			return (float) (float2long(Value1)%float2long(Value2));
			///
		case 6:
			return (float) (float2long(Value1)<<float2long(Value2));
		case 7:
			if( Value1!=Value2 )
				return 1;
			else
				return 0;
		case 8:
			if( Value1<=Value2 )
				return 1;
			else
				return 0;
		case 9:
			if( Value1<Value2 )
				return 1;
			else
				return 0;
			///
		case 0xa:
			return (float) (float2long(Value1)>>float2long(Value2));
		case 0xb:
			if( Value1>=Value2 )
				return 1;
			else
				return 0;
		case 0xc:
			if( Value1>Value2 )
				return 1;
			else
				return 0;
		case 0xd:
			if( Value1==Value2 )
				return 1;
			else
				return 0;
			///
		case 0xe:
			return (float) (float2long(Value1)^float2long(Value2));
		case 0xf:
			if( Value1 && Value2 )
				return 1;
			else
				return 0;
			///
		case 0x10:
			return (float) (float2long(Value1)&float2long(Value2));
		case 0x11:
			if( Value1 || Value2 )
				return 1;
			else
				return 0;
		case 0x12:
			return (float) (float2long(Value1)|float2long(Value2));

	}
	ScriptError("Internal Error, bad operator");
	return 0;
}



//
// Called by GetExpression
//
// Low 8 bits   = Precedence
// Hight 8 bits = Operator
//
int FloatGetOperator()
{
	switch( *pLine++ )
	{
		default:
		case 0:
			--pLine;
			return 0;						// End of line or unknown
		case ' ':
			return FloatGetOperator();
		case '-':
			return 0x0150;
		case '+':
			return 0x0250;
		case '/':
			return 0x0355;		
		case '*':
			return 0x0455;		
		case '%':
			//ScriptError("The \">>\" operator is invalid for floating-point expressions");
			return 0x0555;		
		case '<':
			if( *pLine=='<' )		// <<
			{
				//ScriptError("The \"<<\" operator is invalid for floating-point expressions");
				pLine++;
				return 0x0645;
			}
			if( *pLine=='>' )		// <>
			{
				pLine++;
				return 0x0735;
			}
			if( *pLine=='=' )		// <=
			{
				pLine++;
				return 0x0840;
			}
			return 0x0940;			// <
		case '>':
			if( *pLine=='>' )		// >>
			{
				//ScriptError("The \">>\" operator is invalid for floating-point expressions");
				pLine++;
				return 0x0a45;
			}
			if( *pLine=='=' )		// >=
			{
				pLine++;
				return 0x0b40;
			}
			return 0x0c40;			// >
		case '=':
			if( *pLine=='=' )		// ==
			{
				pLine++;
				return 0x0d35;
			}
			return 0x0d35;			// =
		case '!':
			if( *pLine=='=' )		// !=
			{
				pLine++;
				return 0x0735;
			}
			break;
		case '^':
			//ScriptError("The \"^\" operator is invalid for floating-point expressions");
			return 0x0e25;
		case '&':
			if( *pLine=='&' )		// &&
			{
				pLine++;
				return 0x0f15;
			}
			//ScriptError("The \"&\" operator is invalid for floating-point expressions");
			return 0x1030;
		case '|':
			if( *pLine=='|' )		// !!
			{
				pLine++;
				return 0x1110;
			}
			//ScriptError("The \"|\" operator is invalid for floating-point expressions");
			return 0x1220;
		case 'a':
			if( *pLine=='n' && *(pLine+1)=='d' && *(pLine+2)==' ')		// AND
			{
				pLine+=3;
				return 0x0f15;
			}
			else pLine--;
			break;
		case 'o':
			if( *pLine=='r' )		// OR
			{
				pLine++;
				return 0x1110;
			}
			else pLine--;
			break;
	}
	return 0;
}



//
// Returns the next number pointed to and advances past it
//
float FloatGetValue()
{
	int Negate=0;
	float result=0;

	if( *pLine==' ' )
		pLine++;

	while(1)
	{
//
// Decimal number?
//
		if( (*pLine<='9' && *pLine>='0') || *pLine == '.' )
		{
//
// is it a hex number (0x1234?)
//
			if( *pLine=='0' && *(pLine+1)=='x' )				// Start of a HEX number?
			{
				pLine+=2;
				while( (*pLine>='0' && *pLine<='9') || (*pLine>='a' && *pLine<='f') )
				{
					if( result > 0xffffffff )
						ScriptError("Number overflow");
					result = result*16 + (*pLine<='9' ? *pLine-'0' : *pLine-'a'+10);
					pLine++;
				}
				break;
			}

			float divisor = 1.0f;
			bool doingRemainder = false	;

			while( (*pLine<='9' && *pLine>='0') || *pLine == '.')
			{
				if (*pLine == '.')
				{
					if( doingRemainder )
						ScriptError( "Invalid floating point number" );
					doingRemainder = true;
					*pLine++;
				}
				if( result > 0x19999999 )
					ScriptError("Number overflow");
				if (!doingRemainder)
					result = result*10 + *pLine-'0';
				else
				{	
					divisor *= 10.0f;
					result = result + ((float)(*pLine-'0')) /  divisor;
				}
				pLine++;
			}
			break;
		}
		else if( *pLine=='(' )				// Parenthesis
		{
			pLine++;
			result=FloatGetExpression();
			if( *pLine!=')' )
				ScriptError("Missing parenthesis");
			pLine++;
			break;
		}
		else if( *pLine=='!' )				// NOT
		{
			pLine++;
			result=FloatGetValue();
			if( result!=0 )
				result=0;
			else
				result=1;
			break;
		}
		else if( *pLine=='-' && *(pLine+1)=='-' && FirstKeywordChar[*(BYTE*)(pLine+2)] )
		{
			pLine+=2;						// Predecrement
			char* pKeyWord=pLine;

			int Exist=GetKeyword();			// Try and match against variables

			if( !(Exist==VAR_INT || Exist == VAR_FLOAT)	)
			{
				char Buffer[SCRIPT_MAX_LINE_LENGTH];
				char* pDest=Buffer;

				while( KeywordChar[*(BYTE*)pKeyWord] )
					*pDest++=*pKeyWord++;

				*pDest=0;

				ScriptError("Expected an integer variable, found '%s'",Buffer );	// Found an INT?
			}
			if (Exist == VAR_INT)
			{
				*pLastVariable-=1;
				result=(float) *pLastVariable;
			}
			else
			{
				*((float*)pLastVariable) -= 1.0f;
				result = *((float*)pLastVariable);
			}
			break;
		}
		else if( *pLine=='+' && *(pLine+1)=='+' && FirstKeywordChar[*(BYTE*)(pLine+2)] )
		{
			pLine+=2;						// Preincrement
			char* pKeyWord=pLine;

			int Exist=GetKeyword();			// Try and match against variables

			if( !(Exist == VAR_FLOAT || Exist == VAR_INT))
			{
				char Buffer[SCRIPT_MAX_LINE_LENGTH];
				char* pDest=Buffer;

				while( KeywordChar[*(BYTE*)pKeyWord] )
					*pDest++=*pKeyWord++;

				*pDest=0;

				ScriptError("Expected an integer variable, found '%s'",Buffer );	// Found an INT?
			}
			if (Exist == VAR_INT)
			{
				*pLastVariable+=1;
				result=(float) *pLastVariable;
			}
			else
			{
				*((float*)pLastVariable) += 1.0f;
				result = *((float*)pLastVariable);
			}
			break;
		}
		else if( *pLine=='-' )				// Unary minus
		{
			Negate^=1;
			pLine++;
		}
		else if( *pLine=='+' )				// Unary Plus
		{
			Negate=0;
			pLine++;
		}
//
// Is it a decimal number?
//
		else if (Equals(pLine,"equal$"))
		{
			char tmp1[512];
			char tmp2[512];
			pLine += 6;
			if( *pLine==' ' )				
				pLine++;
			if (*pLine != '(')
				ScriptError("Syntax Error: equal$(<var_string s1>, <var_string s2>)");
			else 
				pLine++;
			char * str1 = GetString();
			#ifdef _DEBUG
			if (!str1)
				ScriptError("Syntax Error: equal$(<var_string s1>, <var_string s2>)");
			#endif
			strcpy(tmp1, str1);

			while( *pLine==' ' )				
				pLine++;
			if (*pLine != ',')
				ScriptError("Syntax Error: equal$(<var_string s1>, <var_string s2>)");
			else
				pLine++;
			if (*pLine == ' ')
				pLine++;
			char * str2 = GetString();
			#ifdef _DEBUG
			if (!str2)
				ScriptError("Syntax Error: equal$(<var_string s1>, <var_string s2>)");
			#endif
			strcpy(tmp2, str2);

			if (*pLine != ')')
				ScriptError("Syntax Error: equal$(<var_string s1>, <var_string s2>)");
			pLine++;
			if (strcmpi(tmp1, tmp2) == 0)
				result = 1;
			else
				result = 0;

			break;
		}
		else if( Equals(pLine,"length$") )
		{
			pLine += 7;
			if (*pLine == ' ')
				pLine++;
			if (*pLine != '(')
				ScriptError("Missing parenthesis");
			else
				pLine++;
			int kw = GetKeyword();
			if (kw != VAR_STRING)
				ScriptError("Length$(<str>) was expecting a string");
			if (*pLine == ' ')
				pLine++;
			if (*pLine != ')')
				ScriptError("Missing parenthesis");
			else
				pLine++;
			if (*pLastVariable == 0) 
				result = 0;
			else 
				result = (float) _mbstrlen((char *) *pLastVariable);
			break;
		}
		else if( Equals(pLine,"fabs") && (*(pLine+4)==' ' || *(pLine+4)=='(') )
		{
			pLine += 4;
			if (*pLine == ' ')
				pLine++;
			if (*pLine != '(')
				ScriptError("Syntax Error: fabs(expr)");
			else
				pLine++;
			float val = FloatGetExpression();
			if (*pLine == ' ')
				pLine++;
			if (*pLine != ')')
				ScriptError("Syntax Error: fabs(expr)");
			else
				pLine++;
			result=(float)fabs(val);
			break;
		}
		else if( (BYTE*)FirstKeywordChar[*(BYTE*)pLine] || (BYTE*)FirstKeywordChar[(*(BYTE*)pLine)-128] )
		{
			char* pKeyWord=pLine;
			int What=-1;
//
// Look for constants (TRUE, FALSE, GameOS enums...)
//
			if( 0==(*((BYTE*)pLine)&128) )
			{
				What=FindWord();

				result=(float)FindWordResult;

				if( What==0 )
					break;									// String found
				if( What==1 )
				{
					DWORD Function=FindWordResult;			// Routine found
					result = (float)ExecuteFunction(Function);
					break;
				}
			}
			else
			{
				*(BYTE*)pLine&=127;
			}
			char* pTemp=pLine;
			int Exist=GetKeyword();
			*(BYTE*)pTemp|=128;

			if( Exist == KEY_RANDOM)
			{
				if (*pLine == ' ')
					pLine++;
				if (*pLine != '(')
					ScriptError("random was expecting 'random(<low>, <high>)'");
				else
					pLine++;
				int lo = GetExpression();
				if (*pLine == ' ')
					pLine++;
				if (*pLine != ',')
					ScriptError("random was expecting 'random(<low>, <high>)'");
				else 
					pLine++;
				int hi = GetExpression();
				if (*pLine == ' ')
					pLine++;
				if (*pLine != ')')
					ScriptError("random was expecting 'random(<low>, <high>)'");
				else
					pLine++;
	
	
				float percent = ((float) gos_rand())/((float)RAND_MAX);
				result = (float) float2long(((hi-lo)*percent + lo));
				break;
			}
	 		if (Exist == VAR_STRING)
			{
				if (*pLastVariable && strlen((char *)*pLastVariable))
					result = (float) *pLastVariable;
				else
					result = 0.0f;
				break;
			}
			if( Exist!=VAR_INT && Exist !=VAR_OBJECT && Exist != VAR_FLOAT)
			{
				char Buffer[SCRIPT_MAX_LINE_LENGTH];
				char* pDest=Buffer;

				while( KeywordChar[*(BYTE*)pKeyWord] )
					*pDest++=*pKeyWord++;

				*pDest=0;

				ScriptError("Expected an float or integer variable, found \"%s\"",Buffer );	// Found an INT?
			}

			if (Exist == VAR_FLOAT)
				result=*((float*)pLastVariable);			// Get value
			else
				result=(float) *pLastVariable;				// Get value

			if( *pLine=='-' && *(pLine+1)=='-' )
			{
				pLine+=2;									// Postdecrement?
				if (Exist != VAR_FLOAT)
				{
					*pLastVariable-=1;
				}
				else
					*((float*)pLastVariable) -= 1.0f;
			}
			if( *pLine=='+' && *(pLine+1)=='+' )
			{
				pLine+=2;									// Postincrement?
				if (Exist != VAR_FLOAT)
				{
					*pLastVariable+=1;
				}
				else
					*((float*)pLastVariable) -= 1.0f;
			}
			break;
		}
		else
		{
			if (*pLine == '\'')
			{
				pLine++;
				if (*pLine != '\\' && *(pLine+1) == '\'')
				{
					result = *pLine;
					pLine+= 2;
					break;
				}
				else
				{
					pLine++;
					if (*pLine == 'n')
					{
						pLine+= 2;
						result = 0x0d; // return
						break;
					}
					if (*pLine == 't')
					{
						pLine += 2;
						result = 0x09; // tab
						break;
					}
				}

				ScriptError("Couldn't decode 'char'");
			}
			else
				ScriptError("Number expected");
		}
	}

	if( Negate )
		result=0-result;
	return result;
}


