//////////////////////////////////////////////////////////////////////
//
//                             Pixie
//
// Copyright © 1999 - 2003, Okan Arikan
//
// Contact: okan@cs.berkeley.edu
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
// 
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
// 
// You should have received a copy of the GNU General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
//
//  File				:	sdrc.cpp
//  Classes				:	-
//  Description			:	The main shader compiler source code
//
////////////////////////////////////////////////////////////////////////



//////////////////////////////////////////////////////////////////////////////////////////////////
//
//
//
//
//
//
//
//	The main shader compiler source code
//
//
//
//
//
//
//
//////////////////////////////////////////////////////////////////////////////////////////////////


// Standard includes
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#ifndef		__INTEL_COMPILER
#ifdef		WIN32
#ifdef		_DEBUG
#include <crtdbg.h>
#endif
#endif
#endif


extern	"C" int	preprocess(char *,FILE *,int,char **);

#include "common/global.h"							// The glorious global header
#include "common/os.h"								// OS dependent stuff)
#include "sdr.h"									// The CScriptContext
#include "opcodes.h"								// The opcodes/arguments

// The environment variables to be searched for the preprocessor and include files
#define			PATH				"PATH"			// The environment variable to search for the preprocessor
#define			INCLUDE				"INCLUDE"		// The default include path for the preprocessor



// If the preprocessing is not turned off, then the preprocessor will be invoked to generate
// preprocessed stuff into the argumentTemporaryFileName and then the compiler will be run on
// this file

// The symbol to be defined by the preprocessor
static	char	*defineProgramName				=			"PIXIE";
// The name of the executable
static	char	*compilerName					=			"sdrc";
// The extension of the compiled script
static	char	*shaderExtension				=			"sdr";


/////////////////////////////////////////////////////////////////////
// Program command line switches
static	char	*argumentIncludeDirectory		=			"-I";
static	char	*argumentDefine					=			"-D";
static	char	*argumentSurpressWarnings		=			"-nw";
static	char	*argumentSurpressErrors			=			"-ne";
static	char	*argumentResolutionInfo			=			"-ri";
static	char	*argumentHelp					=			"--help";
static	char	*argumentPrintVersionInfo		=			"-v";

///////////////////////////////////////////////////////////////////////
// Function				:	printVersion
// Description			:	Print the version
// Return Value			:	-
// Comments				:
// Date last edited		:	4/28/2002
void	printVersion() {
	printf("RenderMan Shading Language Compiler (%s) v%d.%d.%d\n",compilerName,VERSION_RELEASE,VERSION_BETA,VERSION_ALPHA);
}

///////////////////////////////////////////////////////////////////////
// Function				:	printUsage
// Description			:	Print the compiler usage
// Return Value			:	-
// Comments				:
// Date last edited		:	4/28/2002
void	printUsage() {
	printVersion();
	printf("Usage:\n");
	printf("%s [options] [<in_file>]\n",compilerName);
	printf("options:\n");

	printf("%s<path>\t: Specify additional include path for the preprocessor\n",argumentIncludeDirectory);

	printf("%s<symbol>\t: Define \"symbol\" for the preprocessor\n",argumentDefine);

	printf("%s\t\t: Surpress warnings\n",argumentSurpressWarnings);

	printf("%s\t\t: Surpress errors\n",argumentSurpressErrors);

	printf("%s\t\t: Display resolution info\n",argumentResolutionInfo);

	printf("%s\t\t: Display version info\n",argumentPrintVersionInfo);

	printf("%s\t\t: Display this message\n",argumentHelp);

	printf("The environment variable PATH will be searched for the preprocessor by default\n");

	printf("The environment variable INCLUDE will be passed to the preprocessor by default\n");

	printf("***<in_file> can have wildcards\n");
}

///////////////////////////////////////////////////////////////////////
// Function				:	initError
// Description			:	Display an error and exit
// Return Value			:	-
// Comments				:
// Date last edited		:	4/28/2002
void	initError(char *mes,...) {
	va_list	args;

	va_start(args,mes);
	vprintf(mes,args);
	va_end(args);
	exit(-1);
}

///////////////////////////////////////////////////////////////////////
// Function				:	append
// Description			:	Append a file name
// Return Value			:	-
// Comments				:
// Date last edited		:	4/28/2002
int		append(const char *file,void *ud) {
	CArray<char *>	*sourceFiles	=	(CArray<char *> *)	ud;

	sourceFiles->push(strdup(file));

	return TRUE;
}


///////////////////////////////////////////////////////////////////////
// Function				:	main
// Description			:	Shading compiler main
// Return Value			:	-
// Comments				:
// Date last edited		:	4/28/2002
int main(int argc, char* argv[]) {
	CScriptContext	*currentCompiler;
	int				i;
	int				settings	=	COMPILER_SURPRESS_DEFINITIONS;
	TSearchpath		*dsoPath;
	CArray<char *>	*sourceFiles;
	char			*sourceFile;
	char			*ppargv[100];
	int				ppargc;


	// Init the memory manager
	memoryManagerInit();
	
	sourceFiles					=	new CArray<char *>;

	dsoPath						=	NULL;

	ppargc				=	1;
	ppargv[ppargc++]	=	"-c";
	ppargv[ppargc++]	=	"6";
	ppargv[ppargc++]	=	"-d";
	ppargv[ppargc++]	=	defineProgramName;

	// Process the arguments
	for (i=1;i<argc;i++) {
		if (strcmp(argv[i],argumentSurpressWarnings) == 0) {
			settings	|=	COMPILER_SURPRESS_WARNINGS;
		} else if (strcmp(argv[i],argumentSurpressErrors) == 0) {
			settings	|=	COMPILER_SURPRESS_ERRORS;
		} else if (strcmp(argv[i],argumentResolutionInfo) == 0) {
			settings	&=	~COMPILER_SURPRESS_DEFINITIONS;
		} else if (strcmp(argv[i],argumentPrintVersionInfo) == 0) {
			printVersion();
			exit(1);
		} else if (strcmp(argv[i],argumentHelp) == 0) {
			printUsage();
			exit(1);
		} else if (strncmp(argv[i],argumentDefine,strlen(argumentDefine)) == 0) {
			ppargv[ppargc++]	=	"-d";
			ppargv[ppargc++]	=	&argv[i][2];
		} else if (strncmp(argv[i],argumentIncludeDirectory,strlen(argumentIncludeDirectory)) == 0) {
			TSearchpath *nPath	=	new TSearchpath;

			ppargv[ppargc++]	=	"-i";
			ppargv[ppargc++]	=	&argv[i][2];

			nPath->directory	=	strdup(&argv[i][2]);
			nPath->next			=	dsoPath;
			dsoPath				=	nPath;
		} else {
			// Save the files
			sourceFiles->push(strdup(argv[i]));
			argv[i]	=	NULL;
		}
	}

	ppargv[ppargc++]	=	"-v";

	// Go over the arguments and replace the wildcards
	{
		char			*file;
		CArray<char *>	*newSources	=	new CArray<char *>;

		for (file=sourceFiles->first();file != NULL;file = sourceFiles->next()) {
			if ((strchr(file,'*') != NULL) || (strchr(file,'?') != NULL)) {
				osEnumerate(file,append,newSources);
				free(file);
			} else {
				newSources->push(file);
			}
		}

		delete sourceFiles;
		sourceFiles	=	newSources;
	}

	if (sourceFiles->numItems == 1) {
		// Get a temp file
		FILE	*in	=	fopen("sdrc.temp","w+");

		// Do we have a go ?
		if (in != NULL) {

			// Get the source file
			sourceFile	=	sourceFiles->pop();

			// Preprocess the file
			if (preprocess(sourceFile,in,ppargc,ppargv) == TRUE) {

				// Rewind in the file
				fseek(in,0,SEEK_SET);

				// Create the compiler
				currentCompiler				=	new CScriptContext(settings);
				currentCompiler->dsoPath	=	dsoPath;

				// Set the file name
				if (currentCompiler->sourceFile != NULL)	free(currentCompiler->sourceFile);

				// Compile the file
				currentCompiler->sourceFile	=	strdup(sourceFile);
				currentCompiler->compile(in);

				// Ditch the compiler
				delete currentCompiler;
			}

			// Ditch the temp file
			fclose(in);
			osDeleteFile("sdrc.temp");
		} else {
			fprintf(stderr,"Unable to create temporary file\n");
		}

		// Ditch the filename
		free(sourceFile);
	} else {
		char	tmp[OS_MAX_PATH_LENGTH];
		char	*fp;

		// Recreate the argument list
		sprintf(tmp,"");
		for (i=0;i<argc;i++) {
			if (argv[i] != NULL) {
				strcat(tmp,argv[i]);
				strcat(tmp," ");
			}
		}

		fp	=	tmp	+ strlen(tmp);

		for (sourceFile=sourceFiles->first();sourceFile!=NULL;sourceFile=sourceFiles->next()) {
			sprintf(fp,sourceFile);

			fprintf(stdout,"Compiling %s\n",sourceFile);

			system(tmp);

			free(sourceFile);
		}
	}


	delete sourceFiles;

	// Shutdown the memort manager
	memoryManagerTini();

	return 0;
}

