#include <stdio.h>
#include <string.h>
#include <windows.h>

#define NOPRINTF
#define NORECT

#include "..\scene.h"

#define PATHLEN			128
#define NAMELEN			128
#define MAXELEMENTS		12
#define MAXELEMENTSIZE	32
#define NUMFLAGS		1
#define FLAGSIZE		16

// Sets flags used in resource script
char	flags[NUMFLAGS][FLAGSIZE] = {"TRUE"}; 

char	resfilename[NAMELEN];
char	resfilepath[PATHLEN];
char	resfilesubpath[PATHLEN];

BOOL	ReadScript(FILE *handle);
BOOL	ProcessBuffer(char *buffer, DWORD length);
BOOL	ReadLine(char *buffer, char *line, DWORD *pos, DWORD length);
BOOL	ProcessLine(char *line);
BOOL	BreakupLine(char *line, char *element, DWORD *pos);
BOOL	ConvertFilename(char *filename);
BOOL	WriteScene(char *srcfile, long level, BOOL savetores, BOOL subdir);
BOOL	WriteRects(char *srcfile, long level, BOOL savetores, BOOL subdir);
DWORD	GetFlags(char *elements);

void main(int argc, char *argv[]) 
{
	int value = sizeof(SceneStruct);
	memset(resfilepath, 0 ,sizeof(resfilepath));
	memset(resfilesubpath, 0 ,sizeof(resfilesubpath));

	char filename[NAMELEN];

	if (argc < 2)
	{	   
		printf("Usage: builder filename.ext\n" );
		printf("filename.ext is the name of a file containing a resource script.\n");
		
		exit(0);
	}

	strcpy(filename, argv[1]);

	FILE *handle = fopen(filename, "rt");

	if (NULL == handle)
	{
		printf("Unable to open %s\n", filename);
		
		exit(0);
	}

	ReadScript(handle);
	
	fclose(handle);
}

BOOL ReadScript(FILE *handle)
{
	fseek(handle, 0, SEEK_END);
	DWORD length = ftell(handle);
	rewind(handle);

	if (length <= 0)
	{
		printf("File size is 0 bytes. Unable to read resource script.\n");

		return FALSE;
	}

	char *buffer = new char[length];

	if (NULL == buffer)
	{
		printf("Unable to allocate memory for resource script.\n");

		return FALSE;
	}

	fread(buffer, 1, length, handle);

	return ProcessBuffer(buffer, length);
}

BOOL ProcessBuffer(char *buffer, DWORD length)
{
	char	line[512];
	DWORD	pos = 0;

	while (pos < length)
	{
		ReadLine(buffer, line, &pos, length);
		ProcessLine(line);
	}

	return TRUE;
}

BOOL ReadLine(char *buffer, char *line, DWORD *pos, DWORD length)
{
	char *buftmp	= buffer + *pos;
	char *linetmp	= line;

	while (1)
	{
		if (10 == *buftmp)
		{
			(*pos)++;
			*linetmp = 0;

			return TRUE;
		}
	
		else if (13 == *buftmp)
		{
			(*pos)++;
			*linetmp = 0;

			return TRUE;
		}
	
		*linetmp = *buftmp;

		buftmp++;
		linetmp++;
		(*pos)++;

		if (*pos >= length)
		{
			*linetmp = 0;

			return FALSE;
		}
	}

	return TRUE;
}

BOOL ProcessLine(char *line)
{
	char	elements[MAXELEMENTS][MAXELEMENTSIZE];							
	DWORD	currentelement	= 0;
	DWORD	numelements		= 0;
	DWORD   pos				= 0;
	DWORD	flags			= 0;
	long	width			= 0;
	long	height			= 0;
	long	level			= 0;
	char	filename1[NAMELEN];

	while (TRUE == BreakupLine(line, elements[numelements], &pos))
	{
		if (!strcmp(elements[numelements], "//") && 0 == numelements)
		{
			return FALSE;
		}

		numelements++;
	}
	
	// Set resource file
	if (!strcmp(elements[currentelement], "file"))
	{
		currentelement++;
	  
	  // Remove '"' from filenames	
		if ('"' == elements[currentelement][0])
		{
			ConvertFilename(elements[currentelement]);
		}
	
		else
		{
			return FALSE;
		}

		strcpy(resfilename, elements[currentelement]);
		
		currentelement++;
	}

	// Set save path
	else if (!strcmp(elements[currentelement], "path"))
	{
		currentelement++;
		strcpy(resfilepath, elements[currentelement]);

		currentelement++;
	}

	// Set save subpath
	else if (!strcmp(elements[currentelement], "subpath"))
	{
		currentelement++;
		strcpy(resfilesubpath, elements[currentelement]);

		currentelement++;
	}

	// convert scene text file to scene binary file
	else if (!strcmp(elements[currentelement], "scene"))
	{
		currentelement++;

	  // save file to a seperate file	 	  
		if (!strcmp(elements[currentelement], "save"))
		{
			currentelement++;
		  // Remove '"' from filenames	
			if ('"' == elements[currentelement][0])
			{
				ConvertFilename(elements[currentelement]);
				strcpy(filename1, elements[currentelement]);

				currentelement++;
			}

			else
			{
				return FALSE;
			}

			level = atol(elements[currentelement++]);
		
			if (level <= 0)
			{
				return FALSE;
			}

			flags |= GetFlags(elements[currentelement++]);

			WriteScene(filename1, level, FALSE, flags & 1);
		}

	  // save file in resource
		else if (!strcmp(elements[currentelement], "add"))
		{
			currentelement++;
		  
		  // Remove '"' from filenames	
			if ('"' == elements[currentelement][0])
			{
				ConvertFilename(elements[currentelement]);
				strcpy(filename1, elements[currentelement]);

				currentelement++;
			}

			else
			{
				return FALSE;
			}

			level	= atol(elements[currentelement++]);
		
			if (level <= 0)
			{
				return FALSE;
			}

			flags |= GetFlags(elements[currentelement++]);

			WriteScene(filename1, level, TRUE, flags & 1);
		}
	}
	
	// convert scene text file to scene binary file
	else if (!strcmp(elements[currentelement], "rect"))
	{
		currentelement++;

	  // save file to a seperate file	 	  
		if (!strcmp(elements[currentelement], "save"))
		{
			currentelement++;
		  // Remove '"' from filenames	
			if ('"' == elements[currentelement][0])
			{
				ConvertFilename(elements[currentelement]);
				strcpy(filename1, elements[currentelement]);

				currentelement++;
			}

			else
			{
				return FALSE;
			}

			level	= atol(elements[currentelement++]);
		
			if (level < 0)
			{
				return FALSE;
			}

			flags |= GetFlags(elements[currentelement++]);

			WriteRects(filename1, level, FALSE, flags & 1);
		}

	  // save file in resource
		else if (!strcmp(elements[currentelement], "add"))
		{
			currentelement++;
		  
		  // Remove '"' from filenames	
			if ('"' == elements[currentelement][0])
			{
				ConvertFilename(elements[currentelement]);
				strcpy(filename1, elements[currentelement]);

				currentelement++;
			}

			else
			{
				return FALSE;
			}

			level	= atol(elements[currentelement++]);
		
			if (level <= 0)
			{
				return FALSE;
			}

			flags |= GetFlags(elements[currentelement++]);

			WriteRects(filename1, level, TRUE, flags & 1);
		}
	}

	return TRUE;
}

BOOL BreakupLine(char *line, char *element, DWORD *pos)
{
	char	*elementtmp	= element;
	char	*linetmp	= line + *pos;

	while (1)
	{
		if (0 == *linetmp)
		{
			return FALSE;
		}
	
		while (1)
		{
			if (0 == *linetmp)
			{
				*elementtmp = 0;
				
				return TRUE;
			}

			if (32 == *linetmp) 
			{
				while(1)
				{
					(*pos)++;
					linetmp++;
					
					if (32 != *linetmp)
					{
						break;
					}
				}
				
				*elementtmp = 0;
																										return TRUE;
			}

			*elementtmp = *linetmp;
			elementtmp++;
			linetmp++;
			(*pos)++;
		}
	}

	return TRUE;
}

BOOL ConvertFilename(char *filename)
{
	char *filenametmp = filename;

	for (DWORD loop = 0; loop < strlen(filename); loop++)
	{
		if ('"' == *(filename + loop))
		{
			continue;
		}

		*filenametmp = *(filename + loop);
		
		filenametmp++;
	}
	
	*filenametmp = 0;
	
	return TRUE;
}

DWORD GetFlags(char *elements)
{
	for (int loop = 0; loop < NUMFLAGS; loop++)
	{
		if (!strcmp(elements, flags[loop]))
		{
			return 1 << loop;
		}
	}

	return 0;
}

BOOL WriteScene(char *srcfile, long levelnum, BOOL savetores, BOOL subdir)
{
	char	elements[1024][MAXELEMENTSIZE];							
	char	destfile[NAMELEN + PATHLEN];
	char	level[4];
	char	line[512];
	char	*buffer			= NULL;
	char	*binary			= NULL;
	DWORD	numelements		= 0;
	DWORD	pos				= 0;
	DWORD	pos2			= 0;
	DWORD	loop;
	DWORD	length;
	long	numclays		= 0;
	long	floor			= 0;
	long	zlimit			= 200;
	long	zoffscreen		= 100;
	long	currentclay		= -1;
	DWORD	version			= VERSION;
	long	OnReport		= FALSE;

	SceneStruct *Scene = NULL;
		
	FILE	*handle	= fopen(srcfile, "rb");

	if (NULL == handle)
	{
		goto End;
	}

	fseek(handle, 0, SEEK_END);
	length = ftell(handle);
	rewind(handle);

	if (length <= 0)
	{
		fclose(handle);
		printf("%s is not a valid file\n.", srcfile);

		goto End;
	}

  // Allocate memory for file and resulting image.
	buffer = new char[length];

	if (NULL == buffer)
	{
		goto End;
	}

	binary = new char[length];
	
	if (NULL == binary)
	{
		goto End;
	}

	memset(binary, 0, length);
	fread(buffer, length, 1, handle);
	fclose(handle);

	while (pos < length)
	{
		ReadLine(buffer, line, &pos, length);
		pos2 = 0;

		while (TRUE == BreakupLine(line, elements[numelements], &pos2))
		{
			if (!strcmp(elements[numelements], "//"))
			{
				continue;
			}

			numelements++;
		}
	}
	
	for (loop = 0; loop < numelements; loop++)
	{
		if (!strcmp(elements[loop], "Clays"))
		{
			loop += 2;
			numclays = (short)atol(elements[loop]);
		
			if (numclays <= 0 || numclays > MAXCLAYS)
			{
				printf("Number of clays is either < 0  or > MAXCLAYS\n");

				goto End;
			}
		
			Scene = new SceneStruct[numclays];		

			memset(Scene, 0, sizeof(Scene) * numclays);
		}

		if (!strcmp(elements[loop], "Floor"))
		{
			loop += 2;
			floor = (short)atol(elements[loop]);
		
			if (floor <= 0)
			{
				goto End;
			}
		}

		if (!strcmp(elements[loop], "ZLimit"))
		{
			loop += 2;
			zlimit = (short)atol(elements[loop]);
		
			if (zlimit <= 0)
			{
				goto End;
			}
		}

		if (!strcmp(elements[loop], "ZOffscreen"))
		{
			loop += 2;
			zoffscreen = (short)atol(elements[loop]);
		
			if (zoffscreen <= 0)
			{
				goto End;
			}
		}

		if (!strcmp(elements[loop], "OnReport"))
		{
			loop += 2;

			if (!strcmp(elements[loop], "False"))
			{
				OnReport = FALSE;
			}

			else if (!strcmp(elements[loop], "True"))
			{
				OnReport = TRUE;
			}
		}

		else if (!strcmp(elements[loop], "Clay"))
		{
			loop += 2;
			currentclay = atol(elements[loop]) - 1;
		
			if (currentclay >= numclays)
			{
				printf("Number of clay descriptions in file is greater than number of clays specified by 'Clays ='.\n");

				goto End;
			}
		}

		else if (!strcmp(elements[loop], "Color"))
		{
			
			if (currentclay < 0 || currentclay > numclays)
			{				
				printf("The clay description number needs to be set with 'Clay = x'.\n");

				goto End;
			}

			loop += 2;
			
			if (!strcmp(elements[loop], "White"))
			{
				Scene[currentclay].Color = htonl(CC_WHITE);
			}

			else if (!strcmp(elements[loop], "Grey"))
			{
				Scene[currentclay].Color = htonl(CC_GREY);
			}

			else if (!strcmp(elements[loop], "Orange"))
			{
				Scene[currentclay].Color = htonl(CC_ORANGE);
			}
		}

		else if (!strcmp(elements[loop], "Speed"))
		{
			
			if (currentclay < 0 || currentclay > numclays)
			{				
				printf("The clay description number needs to be set with 'Clay = x'.\n");

				goto End;
			}

			loop += 2;
			Scene[currentclay].Speed = htonl(atol(elements[loop]));
		}

		else if (!strcmp(elements[loop], "Delay"))
		{
			
			if (currentclay < 0 || currentclay > numclays)
			{				
				printf("The clay description number needs to be set with 'Clay = x'.\n");

				goto End;
			}

			loop += 2;
			Scene[currentclay].Delay = htonl(atol(elements[loop]));
		}

		else if (!strcmp(elements[loop], "Init.x"))
		{
			
			if (currentclay < 0 || currentclay > numclays)
			{				
				printf("The clay description number needs to be set with 'Clay = x'.\n");

				goto End;
			}

			loop += 2;
			Scene[currentclay].Init.x = htonl(atol(elements[loop]));
		}

		else if (!strcmp(elements[loop], "Init.y"))
		{
			
			if (currentclay < 0 || currentclay > numclays)
			{				
				printf("The clay description number needs to be set with 'Clay = x'.\n");

				goto End;
			}

			loop += 2;
			Scene[currentclay].Init.y = htonl(atol(elements[loop]));
		}

		else if (!strcmp(elements[loop], "Init.z"))
		{
			
			if (currentclay < 0 || currentclay > numclays)
			{				
				printf("The clay description number needs to be set with 'Clay = x'.\n");

				goto End;
			}

			loop += 2;
			Scene[currentclay].Init.z = htonl(atol(elements[loop]));
		}

		else if (!strcmp(elements[loop], "Rot.x"))
		{
			
			if (currentclay < 0 || currentclay > numclays)
			{				
				printf("The clay description number needs to be set with 'Clay = x'.\n");

				goto End;
			}

			loop += 2;
			Scene[currentclay].Rot.x = htonl(atol(elements[loop]));
		}

		else if (!strcmp(elements[loop], "Rot.y"))
		{
			
			if (currentclay < 0 || currentclay > numclays)
			{				
				printf("The clay description number needs to be set with 'Clay = x'.\n");

				goto End;
			}

			loop += 2;
			Scene[currentclay].Rot.y = htonl(atol(elements[loop]));
		}

		else if (!strcmp(elements[loop], "Rot.z"))
		{
			
			if (currentclay < 0 || currentclay > numclays)
			{				
				printf("The clay description number needs to be set with 'Clay = x'.\n");

				goto End;
			}

			loop += 2;
			Scene[currentclay].Rot.z = htonl(atol(elements[loop]));
		}

		else if (!strcmp(elements[loop], "Scale.x"))
		{
			
			if (currentclay < 0 || currentclay > numclays)
			{				
				printf("The clay description number needs to be set with 'Clay = x'.\n");

				goto End;
			}

			loop += 2;
			Scene[currentclay].Scale.x = htonl(atol(elements[loop]));
		}

		else if (!strcmp(elements[loop], "Scale.y"))
		{
			
			if (currentclay < 0 || currentclay > numclays)
			{				
				printf("The clay description number needs to be set with 'Clay = x'.\n");

				goto End;
			}

			loop += 2;
			Scene[currentclay].Scale.y = htonl(atol(elements[loop]));
		}

		else if (!strcmp(elements[loop], "Scale.z"))
		{
			
			if (currentclay < 0 || currentclay > numclays)
			{				
				printf("The clay description number needs to be set with 'Clay = x'.\n");

				goto End;
			}

			loop += 2;
			Scene[currentclay].Scale.z = htonl(atol(elements[loop]));
		}

		else if (!strcmp(elements[loop], "Angle.y"))
		{
			
			if (currentclay < 0 || currentclay > numclays)
			{				
				printf("The clay description number needs to be set with 'Clay = x'.\n");

				goto End;
			}

			loop += 2;
			Scene[currentclay].AngleY = htonl(atol(elements[loop]));
		}

		else if (!strcmp(elements[loop], "Angle.z"))
		{
			
			if (currentclay < 0 || currentclay > numclays)
			{				
				printf("The clay description number needs to be set with 'Clay = x'.\n");

				goto End;
			}

			loop += 2;
			Scene[currentclay].AngleZ = htonl(atol(elements[loop]));
		}

		else if (!strcmp(elements[loop], "Rabbit"))
		{
			
			if (currentclay < 0 || currentclay > numclays)
			{				
				printf("The clay description number needs to be set with 'Clay = x'.\n");

				goto End;
			}

			loop += 2;

			if (!strcmp(elements[loop], "False"))
			{
				Scene[currentclay].Rabbit = htonl(FALSE);
			}

			else if (!strcmp(elements[loop], "True"))
			{
				Scene[currentclay].Rabbit = htonl(TRUE);
			}
		}

		else if (!strcmp(elements[loop], "Remove"))
		{
			
			if (currentclay < 0 || currentclay > numclays)
			{				
				printf("The clay description number needs to be set with 'Clay = x'.\n");

				goto End;
			}

			loop += 2;

			if (!strcmp(elements[loop], "False"))
			{
				Scene[currentclay].Remove = htonl(FALSE);
			}

			else if (!strcmp(elements[loop], "True"))
			{
				Scene[currentclay].Remove = htonl(TRUE);
			}
		}

		else if (!strcmp(elements[loop], "Water"))
		{
			
			if (currentclay < 0 || currentclay > numclays)
			{				
				printf("The clay description number needs to be set with 'Clay = x'.\n");

				goto End;
			}

			loop += 2;

			if (!strcmp(elements[loop], "False"))
			{
				Scene[currentclay].Water = htonl(FALSE);
			}

			else if (!strcmp(elements[loop], "True"))
			{
				Scene[currentclay].Water = htonl(TRUE);
			}
		}
	}

	strcpy(destfile, resfilepath);
a.
	if (TRUE == subdir)
	{
		strcat(destfile, resfilesubpath);
	}

	strcat(destfile, "scene");
	ltoa(levelnum, level, 10);
	strcat(destfile, level);
	strcat(destfile, ".scn");
	
	handle = fopen(destfile, "wb");

	if (NULL == handle)
	{
		goto End;
	}

	version		= htonl(version);
	numclays	= htonl(numclays);
	floor		= htonl(floor);
	zlimit		= htonl(zlimit);
	zoffscreen	= htonl(zoffscreen);
	OnReport	= htonl(OnReport);

	fwrite(&version, sizeof(version), 1, handle);
	fwrite(&numclays, sizeof(numclays), 1, handle);
	fwrite(&floor, sizeof(floor), 1, handle);
	fwrite(&zlimit, sizeof(zlimit), 1, handle);
	fwrite(&zoffscreen, sizeof(zoffscreen), 1, handle);
	fwrite(&OnReport, sizeof(OnReport), 1, handle);
	fwrite(Scene, sizeof(SceneStruct), htonl(numclays), handle);

	fclose(handle);

End:
	if (buffer)
	{
		delete buffer;
		buffer = NULL;
	}

	if (binary)
	{
		delete binary;
		binary = NULL;
	}

	return TRUE;
}

BOOL WriteRects(char *srcfile, long levelnum, BOOL savetores, BOOL subdir)
{
	char	elements[1024][MAXELEMENTSIZE];							
	char	destfile[NAMELEN + PATHLEN];
	char	level[4];
	char	line[512];
	char	image[FILENAMELEN];
	char	*buffer			= NULL;
	char	*binary			= NULL;
	DWORD	numelements		= 0;
	DWORD	pos				= 0;
	DWORD	pos2			= 0;
	DWORD	loop;
	DWORD	length;
	long	numrects		= 0;
	long	currentrect		= -1;
	DWORD	version			= VERSION;
	
	RECT rects[MAXRECTS];
		
	memset(image, 0, sizeof(image));
	FILE	*handle	= fopen(srcfile, "rb");

	if (NULL == handle)
	{
		goto End;
	}

	fseek(handle, 0, SEEK_END);
	length = ftell(handle);
	rewind(handle);

	if (length <= 0)
	{
		fclose(handle);
		printf("%s is not a valid file\n.", srcfile);

		goto End;
	}

  // Allocate memory for file and resulting image.
	buffer = new char[length];

	if (NULL == buffer)
	{
		goto End;
	}

	binary = new char[length];
	
	if (NULL == binary)
	{
		goto End;
	}

	memset(binary, 0, length);
	fread(buffer, length, 1, handle);
	fclose(handle);

	while (pos < length)
	{
		ReadLine(buffer, line, &pos, length);
		pos2 = 0;

		while (TRUE == BreakupLine(line, elements[numelements], &pos2))
		{
			if (!strcmp(elements[numelements], "//"))
			{
				continue;
			}

			numelements++;
		}
	}
	
	for (loop = 0; loop < numelements; loop++)
	{
		if (!strcmp(elements[loop], "Rects"))
		{
			loop += 2;
			numrects = atol(elements[loop]);
		
			if (numrects < 0 || numrects >= MAXRECTS)
			{
				printf("Number of rects must be between 0 and %i.\n", MAXRECTS);

				goto End;
			}
		}

		if (!strcmp(elements[loop], "Image"))
		{
			loop += 2;
			
			strcpy(image, elements[loop]);

			if (strlen(elements[loop]) <= 0)
			{
				goto End;
			}
		}

		else if (!strcmp(elements[loop], "Rect"))
		{
			loop += 2;
			currentrect = atol(elements[loop]);
		
			if (currentrect >= numrects)
			{
				printf("Number of rects descriptions in file is greater than number of clays specified by 'Clays ='.\n");

				goto End;
			}
		}

		else if (!strcmp(elements[loop], "Rect.top"))
		{
			
			if (currentrect < 0 || currentrect > numrects)
			{				
				printf("The rect description number needs to be set with 'Clay = x'.\n");

				goto End;
			}

			loop += 2;
			rects[currentrect].top = atol(elements[loop]);
		}

		else if (!strcmp(elements[loop], "Rect.left"))
		{
			
			if (currentrect < 0 || currentrect > numrects)
			{				
				printf("The rect description number needs to be set with 'Clay = x'.\n");

				goto End;
			}

			loop += 2;
			rects[currentrect].left = atol(elements[loop]);
		}

		else if (!strcmp(elements[loop], "Rect.bottom"))
		{
			
			if (currentrect < 0 || currentrect > numrects)
			{				
				printf("The rect description number needs to be set with 'Clay = x'.\n");

				goto End;
			}

			loop += 2;
			rects[currentrect].bottom = atol(elements[loop]);
		}

		else if (!strcmp(elements[loop], "Rect.right"))
		{
			
			if (currentrect < 0 || currentrect > numrects)
			{				
				printf("The rect description number needs to be set with 'Clay = x'.\n");

				goto End;
			}

			loop += 2;
			rects[currentrect].right = atol(elements[loop]);
		}
	}

	strcpy(destfile, resfilepath);

	if (TRUE == subdir)
	{
		strcat(destfile, resfilesubpath);
	}

	strcat(destfile, "rect");
	ltoa(levelnum, level, 10);
	strcat(destfile, level);
	strcat(destfile, ".rct");
	
	handle = fopen(destfile, "wb");

	if (NULL == handle)
	{
		goto End;
	}

	fwrite(&version, sizeof(DWORD), 1, handle);
	fwrite(&numrects, sizeof(long), 1, handle);
	fwrite(&image, 1, FILENAMELEN, handle);
	fwrite(rects, sizeof(RECT), numrects, handle);

	fclose(handle);

End:
	if (buffer)
	{
		delete buffer;
		buffer = NULL;
	}

	if (binary)
	{
		delete binary;
		binary = NULL;
	}

	return TRUE;
}
