/*                      QBCC Project

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


Quick Basic Compatible Compiler

Original Concept:  Martyn 'leroy' McDonell
Coding:			   Jason Boettcher


====================================
*/

#include "qbcc.h"
#include "structures.h"
#include "error.h"
#include "info.h"
#include "function.h"
#include "scanner.h"
#include "variable.h"
#include "translate.h"

// BASIC file basically stored in here
func_t *curFUNC = NULL;				// QB Functions
type_t *curTYPE = NULL;				// QB Types
default_t *DefStruct = NULL;		// Default QB Types
select_t *SelStruct = NULL;			// SELECT CASE struct

// Tokens
token_t *curToken;					// Current Token
token_t *LookToken;					// Look ahead Token

int Indent = 0;						// Indentation
int IsCase = FALSE;					// Currently not in a CASE in SELECT CASE

// Input & Output files
FILE *outfile;
FILE *infile;
char basfile[128];
char cfile[128];
char OrigName[128];

///////////////////
// Main Entry Point
int main(int argc, char *argv[])
{
	int error = 0;
	
	ShowQBCCHeader();						// Show program header
		
	error = CheckParameters(argc,argv);		// Check the parameters
	if(error == FALSE)
		return -1;

#ifndef __ALPHA__
	RunPreProcessor(argc,argv);				// Run the Pre-Processor

	strcpy(OrigName,argv[1]);
	strcpy(basfile,"__QBCCF.bas");

#endif

	// Open the files
	if( (outfile = fopen(cfile,"wt")) == NULL)
		SysError(__LINE__,"main", "Cannot open output file");
	if( (infile = fopen(basfile,"rt")) == NULL)
		SysError(__LINE__,"main", "Cannot open input file");
	yyin = infile;
	
	error = Initialize();					// Set up the list
	if(error == FALSE)
		SysError(__LINE__,"main", "Out of Memory");

	AddStandard();							// Add standard C file header
	
	// Start the initial pass
	VarFuncPass();	

#ifdef _DEVSUMMARY_
	ShowDevSummary();
#endif

#ifdef _DEV_
	printf("- Main Parser\n");
#endif

	// Start the Parser
	RestartScanner();
	Block(FALSE,FALSE);
	

#ifdef _DEVCOUTPUT_
	// Print the global variables
	Print_DevCSummary();
#endif
	

	ShutDown();		

	return 0;
}

///////////////////
// Show Header
void ShowQBCCHeader(void)
{
	printf("QBCC - Quick Basic Compatible Compiler - Version " VERSION "\n");
	printf("------------------------------------------------------\n");
#ifndef __ALPHA__
	#ifdef _DEV_
		printf("\t\tDEVELOPER MODE!\n\n");
	#endif
#else
	printf("\t\t\tALPHA\n\n");
#endif

}

///////////////////
// Initialize main list
int Initialize(void)
{
#ifdef _DEV_
	printf("- Initializing\n");
#endif
	
	// Defaults (gotta be allocated first, coz other shit being allocated in here needs it)
	DefStruct = (default_t *)malloc(sizeof(default_t));
	if(DefStruct == NULL)
		SysError(__LINE__,"Initialize","Out of memory");	
	DefStruct->DefaultType = VAR_SINGLE;
	strcpy(DefStruct->INTEGER,"signed short");
	strcpy(DefStruct->LONG,"long");
	strcpy(DefStruct->DOUBLE,"double");
	strcpy(DefStruct->SINGLE,"float");
	strcpy(DefStruct->STRING,"string");
	strcpy(DefStruct->FILE,DefStruct->INTEGER);

	// Functions
	curFUNC = (func_t *)malloc(sizeof(func_t));
	if(curFUNC == NULL)
		SysError(__LINE__,"Initialize","Out of memory");
	strcpy(curFUNC->name,"");
	curFUNC->vars = NULL;
	curFUNC->args = NULL;	
	
	// Types
	curTYPE = (type_t *)malloc(sizeof(type_t));
	if(curTYPE == NULL)
		SysError(__LINE__,"Initialize","Out of memory");
	strcpy(curTYPE->name,"");
	curTYPE->vars = NULL;
	curTYPE->nummembers = 0;

	curFUNC->next = curFUNC->prev = NULL;
	curTYPE->next = curTYPE->prev = NULL;	
			
	AddFunction("__BC_GLOBAL__",VAR_VOID,NULL);
	AddFunction("OPEN",VAR_VOID,NULL);					// To stop QBCC hanging if this function
	curFUNC->flags |= VRF_EXTERN;						// isn't delcared	
		
	
	// Tokens
	curToken = (token_t *)malloc(sizeof(token_t));
	LookToken = (token_t *)malloc(sizeof(token_t));
	if(curToken == NULL)
		SysError(__LINE__,"Initialize","Out of memory");
	if(LookToken == NULL)
		SysError(__LINE__,"Initialize","Out of memory");


	// Select Case
	SelStruct = (select_t *)malloc(sizeof(select_t));
	if(SelStruct == NULL)
		SysError(__LINE__,"Initialize","Out of memory");

	strcpy(SelStruct->expr,"");
	SelStruct->InSelectBlock = FALSE;

	curToken->type = 0;
	curToken->flags = 0;
	strcpy(curToken->text,"");	
	curToken->VarType = DefStruct->DefaultType;
	LookToken->type = 0;
	LookToken->flags = 0;
	strcpy(LookToken->text,"");
	LookToken->VarType = DefStruct->DefaultType;	

	return TRUE;
}

///////////////////
// Shutdown the translator
int ShutDown(void)
{
#ifdef _DEV_
	printf("- ShutDown\n");
#endif
	// TODO: Free whole list

	// Free memory	
	if(curFUNC)
	{
		FindFunction("__BC_GLOBAL__",TRUE);
	
		if(curFUNC->vars)
			free(curFUNC->vars);
		if(curFUNC->args)
			free(curFUNC->args);
		if(curFUNC)
			free(curFUNC);
	}
	
	if(curTYPE)
	{
		if(curTYPE->vars)
			free(curTYPE->vars);	

		if(curTYPE)
			free(curTYPE);
	}

	if(DefStruct)
		free(DefStruct);
	if(SelStruct)
		free(SelStruct);

	// Close the files
	if(outfile)
		fclose(outfile);
	if(infile)
		fclose(infile);

	// Were done 'yay'

	return TRUE;
}

///////////////////
// Check the parameters
int CheckParameters(int argc, char *argv[])
{
	if(argc < 3)	
	{
		// Tell em how to get help
		printf("Incorrect number of arguements!\nUse bc.exe --help for help\n");
		return FALSE;
	}

	if(strcmp(argv[1],"--help") == 0)
	{
		// Show help
		printf("Usage:\n");
		printf("bc.exe basfile.bas cfile.c\n");
		return FALSE;
	}	
	
	strcpy(basfile, argv[1]);
	strcpy(cfile,argv[2]);
	return TRUE;
}

///////////////////
// Run the Pre-Processor
void RunPreProcessor(int argc, char *argv[])
{
	char Args[256];
	int n = 0;

#ifdef _DEV_
	printf("- Running PreProcessor\n");
#endif	
	
	strcpy(Args,"bpp ");
	strcat(Args,argv[1]);
	strcat(Args," ");
	strcat(Args,"__QBCCF.bas");
#ifndef _DEV_
	strcat(Args," >b");
#endif

	
	system(Args);
}


///////////////////
// Show Developer Function Summary
void ShowDevSummary(void)
{
	func_t *poldfunc;
	var_t *poldvar;
	type_t *poldtype;
	char *type[] = {"void","Integer","File","Long","Single","Double","String","User Defined"};
	
	printf("- Structure Summary\n");
	poldtype = curTYPE;
	while(poldtype->prev != NULL)
		poldtype = poldtype->prev;
	while(poldtype != NULL)
	{
		if(strcmp(poldtype->name,"") != 0)
		{
			printf("TYPE: %s\n# Members: %d\n",poldtype->name,poldtype->nummembers);
			poldvar = poldtype->vars;
			while(poldvar->prev != NULL)
				poldvar = poldvar->prev;			
			while(poldvar != NULL)
			{
				if(strcmp(poldvar->name,"") != 0)
				{
					if(poldvar->structtype)
						printf("    %s : %s",poldvar->name,poldvar->structtype->name);
					else
						printf("    %s : %s",poldvar->name,type[poldvar->type]);
				
					// Check Flags
					if(poldvar->flags & VRF_ARRAY)
						printf(" | ARRAY");
					printf("\n");

				}
				poldvar = poldvar->next;
			}
		}
		poldtype = poldtype->next;
	}

	printf("\n- Function / Variable Summary\n");
	FindFunction("__BC_GLOBAL__",TRUE);
	poldfunc = curFUNC;
		
	while(poldfunc != NULL)
	{		
		printf("\nFunction:            %s %s\n",poldfunc->name,type[poldfunc->ret]);
		// Arguements
		printf("    Arguements:\n");
		poldvar = poldfunc->args;
		while(poldvar->prev != NULL)
			poldvar = poldvar->prev;		
		while(poldvar != NULL)
		{
			if(strcmp(poldvar->name,"") != 0)
				if(poldvar->structtype)
					printf("                  %s : %s\n",poldvar->name,poldvar->structtype->name);
				else
					printf("                  %s : %s\n",poldvar->name,type[poldvar->type]);
			poldvar = poldvar->next;
		}
		// Variables
		printf("    Variables:\n");
		poldvar = poldfunc->vars;
		while(poldvar->prev != NULL)
			poldvar = poldvar->prev;
		while(poldvar != NULL)
		{
			if(strcmp(poldvar->name,"") != 0)
			{
				if(poldvar->structtype)
					printf("                  %s : %s",poldvar->name,poldvar->structtype->name);
				else
					printf("                  %s : %s",poldvar->name,type[poldvar->type]);

				// Flags
				if(poldvar->flags & VRF_ARRAY)
					printf(" | ARRAY");				
				if(poldvar->flags & VRF_POINTER)
					printf(" | POINTER");
				if(poldvar->flags & VRF_DIMMED)
					printf(" | DIMMED");
				if(poldvar->flags & VRF_EXTERN)
					printf(" | EXTERN");

				printf("\n");
			}
			poldvar = poldvar->next;
		}
		poldfunc = poldfunc->next;
	}	
}