/*                      QBCC Project

This source file is licensed under the GNU Public Licence which should be 
included with the distribution of this source.

*/
// Main Translation Routines
// Fucking messy

#include "qbcc.h"
#include "scanner.h"
#include "info.h"
#include "translate.h"
#include "error.h"
#include "keyword.h"
#include "variable.h"
#include "number.h"
#include "operator.h"
#include "symbol.h"
#include "function.h"


// Define this if QBCC fuck's up to find were exactly the fucking up occured
//#define BUGHUNT

///////////////////
// Main Block
void Block(int group,int OneLine)
{	
	char buffer[4096];
	int BreakBlock = FALSE;

	

	while(curToken->type != TK_EOF)
	{
		GetToken();

		strcpy(buffer,"");
		PrintDepth(buffer);

#ifdef BUGHUNT
		// BUG FINDING LINE
		printf("---\nLn: %d\nFunc: %s\nToken: %s %d\nLook: %s %d\n",LineNumber,curFUNC->name,
			curToken->text,curToken->type,LookToken->text,LookToken->type);
#endif

		switch(curToken->type)
		{
			case TK_KEYWORD:			// Translate Keywords
				BreakBlock = TranslateKEYWORD(curToken->text);
				break;

			case TK_VARIABLE:			// Translate l-value assignments								
				Assign(curToken->text);
				break;

			case TK_FUNCTION:			// Function called
				// Check it's not the function Ret Var				
				if(strcmp(curFUNC->name,curToken->text) == 0)
				{
					Assign(curToken->text);
					break;
				}

				TranslateFUNCTION(buffer,curToken->text);
				fprintf(outfile,"%s;\n",buffer);
				
				// Hack #467
				if(curToken->type == TK_NEWLINE && OneLine)
				{
					group = TRUE;
					BreakBlock = TRUE;
				}
				break;

			case TK_NUMBER:			// Numbers cause l-value errors
				BASError(LineNumber,"Left operand must be l-value");
				break;

			case TK_NEWLINE:
				if(OneLine)
				{
					group = TRUE;
					BreakBlock = TRUE;
				}
				break;
			case TK_LABEL:
				fprintf(outfile,"%s\n",curToken->text);
				break;

			case TK_STRINGCONSTANT:	// Str constant's as lvalues uh? Dickhead progger
				BASError(LineNumber,"Left operand must be l-value");
				break;
		}

		if(group && BreakBlock)
		{
			SUB_DEPTH
			strcpy(buffer,"");
			PrintDepth(buffer);
			fprintf(outfile,"%s}\n",buffer);			
		}
		if(BreakBlock)
			break;
	}
}

///////////////////
// Expression
int Expression(char *retstr,int BracketExp,int IFStat,int Function)
{

	// Do Yaself a favour, don't try and understand this routine
	// It basically evaluates an expression, well.... it should
	char buf[1024];
	int ExitWhile = FALSE;
	int TokenParsed = FALSE;
	int StringExpr = FALSE;
	char exprstat[1024];	
	int VarType = DefStruct->DefaultType;
	var_t Variable;

	int ExprType = VAR_INTEGER;			// Part of our number to ptr algorithm
	int UseNumberPointer = FALSE;		// in other words HACK #12
	int MustUsePtr = FALSE;				// Self explanatary
	int FirstVarisref = FALSE;			// First Var used an '&'
	int IsArrayExpr = FALSE;
	int FirstVarFuncisptr = FALSE;		// Geez these var name's are gettin stupid
										// This is set if the first var/func is a pointer
	
	int ExprCount = 0;					// Part of our Logical / Bitwise determination hack
	int UseBit = FALSE;					//		"		"		"		"		"		"		"		"
	int CompleteExpr = FALSE;			// Try and use a different method of 'complete expressions'

	char LastExpr[1024];				// Last Expression, for the Operators
	int ReqOpSecond = FALSE;			// For operators that need to be converted to functions
	int NoOpConvert = TRUE;				// Didn't do any operator converting	

	// String's
	char StrFmt[64];
	int NumString = 0;
	

	strcpy(StrFmt,"\"");
	strcpy(exprstat,"");
	strcpy(LastExpr,"");
	ExprCount = 0;	

	while(!ExitWhile)
	{
		strcpy(LastExpr,"");
		switch(LookToken->type)
		{

			///////////////////////////    NUMBER    /////////////////////////////////
			case TK_NUMBER:
				TokenParsed = TRUE;
				GetToken();
				strcpy(LastExpr,"");
				TranslateNUMBER(LastExpr,curToken->text);
				ExprCount++;
				UseNumberPointer = TRUE;
				if(curToken->VarType > ExprType)
					ExprType = curToken->VarType;
				MustUsePtr = TRUE;
				break;

			
			///////////////////////////    NEWLINE    /////////////////////////////////
			case TK_NEWLINE:
				ExitWhile = TRUE;						// Quit, don't need these
				break;
			

			///////////////////////////    SOFTLINE ':'    /////////////////////////////////
			case TK_SOFTLINE:
				ExitWhile = TRUE;						// Quit, don't need these
				break;


			///////////////////////////    VARIABLE    /////////////////////////////////
			case TK_VARIABLE:
				TokenParsed = TRUE;
				strcpy(buf,"");
				strcpy(LastExpr,"");
				GetToken();
				Variable = TranslateVARIABLE(buf,curToken->text);

				if(Variable.type == VAR_STRING || StringExpr)
				{
					StringExpr = TRUE;
					strcat(StrFmt,"s");
					NumString++;
				}				
								
				if(StringExpr)
					strcpy(LastExpr,",");

				if(!(Function && (Variable.flags & VRF_ARRAY)))
				{
					if(StringExpr && !(Variable.flags & VRF_POINTER))
						strcat(LastExpr,"&");

					if(!(Variable.flags & VRF_POINTER) && ExprCount == 0 && !StringExpr && Function)
					{
						strcat(LastExpr,"&");
						FirstVarisref = TRUE;
					}
					if((Variable.flags & VRF_POINTER) && Function && ExprCount == 0 && StringExpr)
						FirstVarFuncisptr = TRUE;

					if(Variable.flags & VRF_POINTER && !Function)
						strcat(LastExpr,"*");
				}
				else
					IsArrayExpr = TRUE;
				
				strcat(LastExpr,buf);
				ExprCount++;
				break;


			//////////////////////////    STRING CONSTANT    /////////////////////////////////
			case TK_STRINGCONSTANT:
				TokenParsed = TRUE;
				GetToken();
				StringExpr = TRUE;
				strcat(StrFmt,"c");
				strcpy(LastExpr,"");
				strcat(LastExpr,",");
				strcat(LastExpr,curToken->text);
				NumString = 2;		// Gotta make sure we use 'strVcpy'
				ExprCount++;
				break;


			///////////////////////////    OPERATOR    /////////////////////////////////
			case TK_OPERATOR:
				TokenParsed = TRUE;
				
				// Test which AND,OR to use (bitwise or logical)
				if(IFStat && (strcmp(LookToken->text,"AND") == 0 || 
					strcmp(LookToken->text,"OR") == 0))
				{
					UseBit = !CompleteExpr;
					CompleteExpr = !CompleteExpr;
				}
				if(strcmp(LookToken->text,"=") == 0 || strcmp(LookToken->text,"<>") == 0)				
					CompleteExpr = TRUE;

				if(IFStat && (strcmp(LookToken->text,"AND") == 0 || 
					strcmp(LookToken->text,"OR") == 0) && !UseBit)
				{
					ExitWhile = TRUE;
					break;
				}
				if(strcmp(LookToken->text,"=") == 0)
				{
					ExitWhile = TRUE;
					break;
				}								
				if(StringExpr)
				{
					if(strcmp(LookToken->text,"=") == 0 || strcmp(LookToken->text,"<>") == 0)
					{
						ExitWhile = TRUE;
						break;
					}
					GetToken();
					break;
				}
				GetToken();

				strcpy(LastExpr,"");
				if(!StringExpr)
					MustUsePtr = TRUE;
				
				TranslateOPERATOR(LastExpr,curToken->text,UseBit,IFStat);
				break;


			///////////////////////////    HEX    /////////////////////////////////
			case TK_HEX:
				TokenParsed = TRUE;
				GetToken();
				strcpy(LastExpr,"");
				TranslateHEX(LastExpr,curToken->text);
				ExprCount++;
				if(ExprType < VAR_LONG)
					ExprType = VAR_LONG;
				UseNumberPointer = TRUE;
				MustUsePtr = TRUE;
				break;


			///////////////////////////    KEYWORD    /////////////////////////////////
			case TK_KEYWORD:
				if(strcmp(LookToken->text,"BYVAL") == 0)
				{
					TokenParsed = TRUE;
					GetToken();
					break;
				}
				if(strcmp(LookToken->text,"BYREF") == 0)
				{
					TokenParsed = TRUE;
					GetToken();
					break;
				}
				ExitWhile = TRUE;						// Quit, don't need these
				break;


			///////////////////////////    FUNCTION    /////////////////////////////////
			case TK_FUNCTION:
				TokenParsed = TRUE;
				GetToken();
				strcpy(LastExpr,"");
				if(curToken->VarType == VAR_STRING)
					StringExpr = TRUE;
				if(StringExpr)
				{
					strcat(StrFmt,"s");
					strcat(LastExpr,",");
					NumString++;
				}
				if(!StringExpr)
				{
					if(curToken->VarType > ExprType)
						ExprType = curToken->VarType;
					UseNumberPointer = TRUE;
					if(ExprCount > 1 || BracketExp)
						strcat(LastExpr,"*");
					if(Function && ExprCount == 0 && !StringExpr)
						FirstVarFuncisptr = TRUE;
				}
				TranslateFUNCTION(LastExpr,curToken->text);
				ExprCount++;
				break;


			///////////////////////////    SYMBOL    /////////////////////////////////
			case TK_SYMBOL:
				TokenParsed = TRUE;
				strcpy(LastExpr,"");
				if(strcmp(LookToken->text,"(") == 0)
				{
					GetToken();
					strcat(LastExpr,"(");
					ExprCount++;
					Expression(LastExpr,TRUE,FALSE,FALSE);				// Recursive
					break;
				}
				if(strcmp(LookToken->text,";") == 0)
				{
					ExitWhile = TRUE;
					break;
				}
				if(strcmp(LookToken->text,",") == 0)
				{
					ExitWhile = TRUE;
					break;
				}

				if(strcmp(LookToken->text,")") == 0)
				{
					if(BracketExp)
					{
						GetToken();
						TranslateSYMBOL(LastExpr,curToken->text);
					}
					ExitWhile = TRUE;
					break;
				}
				break;
		}		

		if(ReqOpSecond)
			strcat(LastExpr,")");
		
		ReqOpSecond = FALSE;

		if(ExitWhile)
		{
			strcat(exprstat,LastExpr);
			break;
		}
		
		if(!TokenParsed)		
			GetToken();

		NoOpConvert = TRUE;
		
		// Check Next Token
		if(IsSpecialOp(LookToken->text))					// Special Operators
		{
			GetToken();
			ConvertSpecOp(curToken->text,buf);
			strcat(exprstat,buf);
			strcat(exprstat,"(");
			strcat(exprstat,LastExpr);
			strcat(exprstat,",");
			ReqOpSecond = TRUE;
			NoOpConvert = FALSE;
		}

		if(NoOpConvert)
			strcat(exprstat,LastExpr);

		TokenParsed = FALSE;
	

	}	// END OF WHILE LOOP

	// write string function
	if(NumString > 1)
	{
		if(StringExpr)
		{
			strcat(retstr,"strVcpy(");
			strcat(retstr,StrFmt);
			strcat(retstr,"\"");
		}
	}

	// write number/value function
	if(!Function || IsArrayExpr)
		UseNumberPointer = FALSE;

	if(Function && ExprCount > 1 && FirstVarFuncisptr && !StringExpr)
	{
		// Confusing Explanation #1
		// If the first var/func is a pointer and we let it go, yet we found more of an expr
		// we need to de-reference that var/func to a value to let op's be used
		UseNumberPointer = TRUE;
		strcpy(buf,"*");
		strcat(buf,exprstat);
		strcpy(exprstat,buf);
	}
	
	if(FirstVarisref && (UseNumberPointer || (MustUsePtr && Function)))
	{
		// Since were using a number pointer algorithm we need to get rid of that ptr ref '&'
		// at the start of the expression
		// FUCK, this whole routine is getting stupidly messy!	
		
		if(exprstat[0] == '&')
			MyDupStr(exprstat,exprstat,1);		// Get rid of starting ampersand
	}


	if((UseNumberPointer && ExprCount > 1) || (MustUsePtr && Function) && !IsArrayExpr)
	{
		if(StringExpr)
			BASError(LineNumber,"Type Mismatch");

		strcat(retstr,"Get");		
		switch(ExprType)
		{
			case VAR_INTEGER: strcat(retstr,"INT"); break;
			case VAR_LONG: strcat(retstr,"LNG"); break;
			case VAR_SINGLE: strcat(retstr,"SNG"); break;
			case VAR_DOUBLE: strcat(retstr,"DBL"); break;
			case VAR_FILE: strcat(retstr,"INT"); break;
		}
		strcat(retstr,"ptr(");
		UseNumberPointer = TRUE;
	}
	else
		UseNumberPointer = FALSE;



	if(NumString == 1 && StringExpr)
		MyDupStr(exprstat,exprstat,1);		// Get rid of starting comma
	
	strcat(retstr,exprstat);

	if( (StringExpr && NumString > 1) || UseNumberPointer)
		strcat(retstr,")");

	return StringExpr;
}


///////////////////
// Variable assign (l-value)
void Assign(char *var)
{

	// Don't ask the question here 'Why don't i just run expression()'!	

	char retstr[4096];
	char lvalue[1024];
	char StrHead[64];
	char Strformat[64];
	int IsString = FALSE;
	char depthpad[64];
	int LVPointer = FALSE;
	int ExitWhile = FALSE;
	int TokenParsed = FALSE;
	int VarType = DefStruct->DefaultType;
	char buf[4096];
	var_t VarStruct;

	char LastExpr[1024];				// Last Expression, for the Operators
	int ReqOpSecond = FALSE;			// For operators that need to be converted to functions
	int NoOpConvert = TRUE;				// Didn't do any operator converting
	
	strcpy(LastExpr,"");
	strcpy(retstr,"");
	strcpy(lvalue,"");
	strcpy(Strformat,"\"");
	strcpy(depthpad,"");
	PrintDepth(depthpad);	

	if(curToken->VarType == VAR_STRING || curToken->type == TK_STRINGCONSTANT)
		IsString = TRUE;
	
	FindVariable(var,&VarStruct);

	VarStruct = TranslateVARIABLE(lvalue,var);

	if(VarStruct.flags & VRF_POINTER)
		LVPointer = TRUE;
	VarType = VarStruct.type;

	if(VarType == VAR_STRING)
		IsString = TRUE;

	strcpy(StrHead,"*strVcpy(");

	
	// For now we will leave this out
	//if(LookToken->type != TK_OPERATOR)
	//	BASError(LineNumber,"Missing operator");
	
	// Parsing begins
	while(!ExitWhile)
	{
		strcpy(LastExpr,"");
		switch(LookToken->type)
		{
			case TK_KEYWORD:
			case TK_NEWLINE:
			case TK_SOFTLINE:
				ExitWhile = TRUE;
				break;

			case TK_OPERATOR:
				TokenParsed = TRUE;				
				GetToken();
				strcpy(LastExpr,"");
				if(!IsString)
					TranslateOPERATOR(LastExpr,curToken->text,TRUE,FALSE);
				break;

			case TK_VARIABLE:
				TokenParsed = TRUE;
				GetToken();
				strcpy(LastExpr,"");
				if(strcmp(curToken->text,curFUNC->name) == 0 && strcmp(LookToken->text,"(") == 0)
				{
					if(IsString)				
						strcat(Strformat,"s");
					strcat(LastExpr,",");
					TranslateFUNCTION(LastExpr,curToken->text);
					break;
				}
				strcpy(buf,"");
				VarStruct = TranslateVARIABLE(buf,curToken->text);

				if(IsString)
				{
					strcat(LastExpr,",");
					strcat(Strformat,"s");
					if(!(VarStruct.flags & VRF_POINTER))
						strcat(LastExpr,"&");
				}
				else				
					if(VarStruct.flags & VRF_POINTER)
						strcat(LastExpr,"*");

				strcat(LastExpr,buf);
				
				break;

			case TK_STRINGCONSTANT:
				TokenParsed = TRUE;
				GetToken();
				strcpy(LastExpr,"");
				if(!IsString)
					BASError(LineNumber,"Type Mismatch");

				strcat(LastExpr,",");
				strcat(LastExpr,curToken->text);
				strcat(Strformat,"c");
				IsString = TRUE;
				break;

			case TK_NUMBER:
				TokenParsed = TRUE;
				GetToken();
				strcpy(LastExpr,"");
				if(IsString)
					BASError(LineNumber,"Type Mismatch");
				TranslateNUMBER(LastExpr,curToken->text);
				break;

			case TK_HEX:
				TokenParsed = TRUE;
				GetToken();
				strcpy(LastExpr,"");
				if(IsString)
					BASError(LineNumber,"Type Mismatch");
				TranslateHEX(LastExpr,curToken->text);
				break;
			
			case TK_SYMBOL:
				TokenParsed = TRUE;
				GetToken();
				strcpy(LastExpr,"");
				if(IsString)
					// Ignore it
					break;
				if(strcmp(curToken->text,"(") == 0)
				{
					strcat(LastExpr,"(");
					Expression(LastExpr,TRUE,FALSE,FALSE);
				}
				else
					TranslateSYMBOL(LastExpr,curToken->text);
				break;

			case TK_FUNCTION:
				TokenParsed = TRUE;
				GetToken();
				strcpy(LastExpr,"");
				if(strcmp(curToken->text,curFUNC->name) == 0 && strcmp(LookToken->text,"=") == 0)
				{
					if(IsString)
					{
						strcat(LastExpr,",");
						strcat(Strformat,"s");
					}					
					TranslateVARIABLE(LastExpr,curToken->text);
					break;
				}
				if(curToken->VarType == VAR_STRING)
					IsString = TRUE;
				if(IsString)
				{
					strcat(LastExpr,",");
					strcat(Strformat,"s");
				}
				else
					strcat(LastExpr,"*");

				TranslateFUNCTION(LastExpr,curToken->text);
				break;
		}
		
		if(ReqOpSecond)
			strcat(LastExpr,")");
		
		ReqOpSecond = FALSE;

		if(ExitWhile)
		{
			strcat(retstr,LastExpr);
			break;
		}
		
		if(!TokenParsed)		
			GetToken();

		NoOpConvert = TRUE;
		
		// Check Next Token
		if(IsSpecialOp(LookToken->text))					// Special Operators
		{
			GetToken();
			ConvertSpecOp(curToken->text,buf);
			strcat(retstr,buf);
			strcat(retstr,"(");
			strcat(retstr,LastExpr);
			strcat(retstr,",");
			ReqOpSecond = TRUE;
			NoOpConvert = FALSE;
		}

		if(NoOpConvert)
			strcat(retstr,LastExpr);

	}

	strcat(Strformat,"\"");
	if(LVPointer)
	{
		strcpy(buf,"*");
		strcat(buf,lvalue);
		strcpy(lvalue,buf);
	}

	if(IsString)
		fprintf(outfile,"%s%s = %s%s%s);\n",depthpad,lvalue,StrHead,Strformat,retstr);
	else
		fprintf(outfile,"%s%s%s;\n",depthpad,lvalue,retstr);	
}


///////////////////
// Condition Statement
int Condition(char *retstr)
{
	// Returns if it's a string statement or not
	int IsString = FALSE;
	char buffer[1024];
	int UseRecurs = FALSE;
	char StrOp[8];

	strcpy(StrOp,"");

	strcpy(buffer,"");

	while(curToken->type != TK_NEWLINE)
	{
		if(Expression(buffer,FALSE,TRUE,FALSE) == TRUE)
			IsString = TRUE;
		
		GetToken();		

		if(strcmp(curToken->text,"=") == 0)
			if(IsString)
			{
				strcat(buffer,",");
				strcpy(StrOp," == ");
			}
			else
				strcat(buffer," == ");

		if(strcmp(curToken->text,"<>") == 0)
			if(IsString)
			{
				strcat(buffer,",");
				strcpy(StrOp," != ");
			}
			else
				strcat(buffer," != ");

		// Check Logical op's
		if(strcmp(curToken->text,"AND") == 0)
		{
			if(IsString)
			{
				strcat(buffer,")");
				strcat(buffer,StrOp);
				strcat(buffer,"0");
			}
			strcat(buffer," && ");
			UseRecurs = TRUE;
			break;
		}
		if(strcmp(curToken->text,"OR") == 0)
		{
			if(IsString)
			{
				strcat(buffer,")");
				strcat(buffer,StrOp);
				strcat(buffer,"0");
			}
			strcat(buffer," || ");
			UseRecurs = TRUE;
			break;
		}

		if(strcmp(curToken->text,"THEN") == 0)
			break;
		if(curToken->type == TK_NEWLINE)
			break;
		if(curToken->type == TK_SOFTLINE)
			break;
	}

	if(IsString)
		strcat(retstr,"strQcmp(");
	strcat(retstr,buffer);
	
	if(IsString && !UseRecurs)
	{
		strcat(retstr,")");
		strcat(retstr,StrOp);
		strcat(retstr,"0");
	}

	if(UseRecurs)
		IsString = Condition(retstr);

	return IsString;
}