#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <time.h>
#include <sys/stat.h>
#include <errno.h>

#include "data/bs_c.c"
#include "data/bm_c.c"
#include "data/bv_c.c"
#include "data/sk_c.c"
#include "data/report_h.c"
#include "data/report_c.c"
#include "data/mk.c"

#define	TF(x)	((x)?"TRUE":"FALSE")

#define	DIR_SEPARATOR	'/'

#ifdef	AMIGA
#define	CURRENT_DIR	""
#else
#define	CURRENT_DIR	"."
#endif

#define	Rnd(a)	(rand()%a)
#define	InitRand(a)	srand((unsigned int)a)

#define	YES	'Y'
#define	NO		'N'

#define	DEFY	"[Y/n]"
#define	DEFN	"[y/N]"

#define	MN_IGN	0
#define	MN_STR	1
#define	MN_INT	2
#define	MN_BOL	3

#define	DEF_GENERATIONS	10

#define	DEF_BRVLEN	3

#define	DEF_NAME	"Manhattan"

#define	NUM_DEFNAMES	11

char	*includes[] = {
	"stdio",
	"stdlib",
	"string",
	"math",
	"time",
	"GAP",
	NULL
};

char	*SelectNames[] = {
	"Double Random",
	"Fitness Proportionate",
	"Sigma Scaled",
	"Tournament",
	"Inorder",
	"Boltzmann",
	"Universal",
	NULL
};

char	*RepDefs[] = {
	"AVERAGE",
	"MEDIAN",
	"TYPECOUNT",
	"MAX",
	"MIN",
	"STDDEV"
};

char	*SelectDefs[] = {
	"DRANDOM",
	"FITPROP",
	"SIGMA",
	"TOURNAMENT",
	"INORDER",
	"TEMPERATURE",
	"UNIVERSAL"
};

char	*CrossoverNames[] = {
	"Singlepoint",
	"Multipoint",
	"Custom",
	NULL
};

char	*InitNames[] = {
	"Random init",
	"Zero init",
	"Custom init",
	NULL
};

char	*CLNames[] = {
	"None",
	"Normal",
	"Verbose",
	"Irritating",
	"Overwhelming",
	NULL
};

char	**CLMenu = CLNames;

#define	PTYPE_Bitstring	0
#define	PTYPE_Bitmatrix	1
#define	PTYPE_BRVector		2
#define	PTYPE_Custom		3

char	*GenomeNames[] = {
	"Bitstring",
	"Bitmatrix",
	"Bounded double vector",
	"Custom",
	NULL
};

char	**GenomeMenu = GenomeNames;

char	*DefNames[] = {
	"Tourist",
	"Hacker",
	"Gnome",
	"Borg",
	"Bug",
	"Zool",
	"Ghost",
	"Hobbit",
	"Haddock",
	"Dinosaur",
	"Polyphant",
	NULL
};

struct VDisp {
	int	Type;
	long	Val;
};

char	*Menu1[] = {
	"Project parameters",
	"Population parameters",
	"Make project",
	"Save configuration",
	"Load configuration",
	"Quit",
	NULL
};

char	*PrParms[] = {
	"Project name",
	"No. of populations",
	"Comment level",
	"Exit",
	NULL
};

struct VDisp PrVals[] = {
	{MN_STR,0},
	{MN_INT,0},
	{MN_STR,0},
	{MN_IGN,0}
};

char	*PoParms[] = {
	"Change name",
	"Change size",
	"Genome type",
	"Environment parameters",
	"Report parameters",
	"Exit",
	NULL
};

struct VDisp PoVals[] = {
	{MN_STR,0},
	{MN_INT,0},
	{MN_STR,0},
	{MN_IGN,0},
	{MN_IGN,0},
	{MN_IGN,0}
};

char	*Flags[] = {
	"Init type",
	"Selection method",
	"Crossover",
	"Crowding",
	"Mutation",
	"Pre-mutate",
	"Elitism",
	"Dump worst",
	"Init dumped",
	"Newbies",
	"Erase best",
	"Make stats",
	"Exit",
	NULL
};

char	*FlagTags[] = {
	"EVL_Evaluator",
	"EVL_Select",
	"EVL_Crosser",
	"EVL_Crowding",
	"EVL_Mutator",
	"EVL_PreMutate",
	"EVL_Elite",
	"EVL_Dump",
	"EVL_InitDumped",
	"EVL_Newbies",
	"EVL_EraseBest",
	"EVL_Stats"
};

struct VDisp FlagVals[] = {
	{MN_STR,0},
	{MN_STR,0},
	{MN_STR,0},
	{MN_BOL,0},
	{MN_BOL,0},
	{MN_BOL,0},
	{MN_INT,0},
	{MN_INT,0},
	{MN_BOL,0},
	{MN_INT,0},
	{MN_BOL,0},
	{MN_BOL,0},
	{MN_IGN,0}
};

char	*RFlagMenu[] = {
	"Average",
	"Median",
	"Typecount",
	"Max",
	"Min",
	"Standard Deviation",
	"Toggle All",
	"Exit",
	NULL
};

struct VDisp RFlagVals[] = {
	{MN_BOL,0},
	{MN_BOL,0},
	{MN_BOL,0},
	{MN_BOL,0},
	{MN_BOL,0},
	{MN_BOL,0},
	{MN_IGN,0},
	{MN_IGN,0}
};

struct NPop {
	char	*Name;
	int	Size;
	int	Type;
	int	Flags[11];
	int	RFlags[6];
};

struct {
	char	*Name;
	int	NumPops,CLevel;
	struct NPop *Pops;
	int rep;
} Project;

int	mcount(char **);

int	menu(char *,char **,struct VDisp *);
int	msg(char *);
int	int_query(char *,int);
char *str_query(char *,char *);
int	bool_query(char *,int);
void	save_template(char *);
void	load_template(char *);
void	save_pop(FILE *,struct NPop);
void	load_pop(FILE *,int);
void	create_project(char *);
char *addpart(char *,char *);
void filtdump(FILE *,char *,int);


int main(int cnt,char *arg[])
{
char	z,buf[255],*tmenu[16],*p;
int	i,n;

InitRand(time(NULL));

Project.Name=malloc(strlen(DEF_NAME)+1);
strcpy(Project.Name,DEF_NAME);
Project.NumPops=1;
Project.Pops = NULL;
Project.CLevel = 1;
PrVals[0].Val = (long)Project.Name;
PrVals[1].Val = 1;
PrVals[2].Val = (long)CLNames[1];
Project.Pops = malloc(sizeof(struct NPop));
memset(Project.Pops,0,sizeof(struct NPop));
Project.Pops[0].Name = malloc(10);
strcpy(Project.Pops[0].Name,"Polyphant");
Project.Pops[0].Flags[4] = 1;	/* Do mutate */
Project.Pops[0].Size = 20;

switch(cnt) {
case	1:
	z=msg("\x1b" "c\nWelcome! This is the GAP Conjurer.\n\nConjure a skeleton? " DEFY " : ");
break;

case	2:
	if(strcmp(arg[1],"-h") || strcmp(arg[1],"--help")) {
		fprintf(stderr,"%s [Template] [Dest. Dir]\n",arg[0]);
		goto tixE;
	}
	load_template(arg[1]);
	z = 1;
break;

case	3:
	load_template(arg[1]);
	create_project(arg[2]);
	goto tixE;
break;

default:
	fprintf(stderr,"%s [Template] [Dest. Dir]\n",arg[0]);
	goto tixE;
}

if(toupper(z)!=NO) {
	do {
		i=menu("Main Menu",Menu1,NULL);
		switch(i) {
		case	1:
			do {
				i = menu("Project parameters",PrParms,PrVals);
				switch(i) {
				case	1:	/* Project Name */
					free(Project.Name);
					Project.Name = str_query("Project name [" DEF_NAME "] ?",DEF_NAME);
					PrVals[0].Val = (long)Project.Name;
				break;

				case	2:	/* # of pops */
					i = int_query("How many populations in project [1] ?",1);
					if(i>0) {
						for(n=0;n!=Project.NumPops;n++) {
							if(Project.Pops[n].Name!=NULL) {
								free(Project.Pops[n].Name);
							}
						}
						Project.NumPops = i;
						PrVals[1].Val = Project.NumPops;
						if(Project.Pops!=NULL) free(Project.Pops);
						Project.Pops = malloc(Project.NumPops*sizeof(struct NPop));
						memset(Project.Pops,0,Project.NumPops*sizeof(struct NPop));
						n=Rnd(NUM_DEFNAMES);
						for(i=0;i!=Project.NumPops;i++) {
							sprintf(buf,"Name for population #%d [%s]? :",i,DefNames[(i+n)%NUM_DEFNAMES]);
							Project.Pops[i].Name = str_query(buf,DefNames[(i+n)%NUM_DEFNAMES]);
							Project.Pops[i].Flags[4] = 1;	/* Do mutate */
							Project.Pops[i].Size = 20;
						}
					}
				break;

				case	3:	/* Comment level */
					Project.CLevel = menu("Comment level",CLMenu,NULL)-1;
					PrVals[2].Val = (long)CLNames[Project.CLevel];
				break;
				}
			} while(i!=mcount(PrParms));
			i=1;
		break;

		case	2:	/* Pop params */
			if(Project.NumPops>0) {
				do {
					if(Project.NumPops>1) {
						for(i=0;i!=Project.NumPops;i++) {
							tmenu[i] = Project.Pops[i].Name;
						}
						tmenu[i] = "Exit";
						tmenu[i+1] = 0;
						n = menu("Select Population",tmenu,NULL)-1;
						if(n==(mcount(tmenu)-1)) {
							break;
						}
					} else {
						n = 0;
					}
					PoVals[0].Val = (long)Project.Pops[n].Name;
					PoVals[1].Val = Project.Pops[n].Size;
					PoVals[2].Val = (long)GenomeNames[Project.Pops[n].Type];
					do {
						sprintf(buf,"Parameters for %s population",Project.Pops[n].Name);
						i = menu(buf,PoParms,PoVals);
						switch(i) {
						case	1:	/* Name */
							strcpy(buf,Project.Pops[n].Name);
							free(Project.Pops[n].Name);
							Project.Pops[n].Name = str_query("New name: ",buf);
							PoVals[0].Val = (long)Project.Pops[n].Name;
						break;

						case	2:	/* Size */
							sprintf(buf,"New population size [%d]? : ",Project.Pops[n].Size);
							Project.Pops[n].Size = int_query(buf,Project.Pops[n].Size);
							PoVals[1].Val = Project.Pops[n].Size;
						break;

						case	3: /* Genome type */
							i = menu("Select genome type",GenomeMenu,NULL);
							Project.Pops[n].Type = i-1;
							PoVals[2].Val=(long)GenomeNames[i-1];
						break;

						case	4: /* Flags */
							sprintf(buf,"Flags for %s population",Project.Pops[n].Name);
							do {
								FlagVals[0].Val = (long)InitNames[Project.Pops[n].Flags[0]];
								FlagVals[1].Val = (long)SelectNames[Project.Pops[n].Flags[1]];
								FlagVals[2].Val = (long)CrossoverNames[Project.Pops[n].Flags[2]];
								FlagVals[3].Val = Project.Pops[n].Flags[3];
								FlagVals[4].Val = Project.Pops[n].Flags[4];
								FlagVals[5].Val = Project.Pops[n].Flags[5];
								FlagVals[6].Val = Project.Pops[n].Flags[6];
								FlagVals[7].Val = Project.Pops[n].Flags[7];
								FlagVals[8].Val = Project.Pops[n].Flags[8];
								FlagVals[9].Val = Project.Pops[n].Flags[9];
								FlagVals[10].Val = Project.Pops[n].Flags[10];
								FlagVals[11].Val = Project.Pops[n].Flags[11];
								FlagVals[12].Val = Project.Pops[n].Flags[12];
								i = menu(buf,Flags,FlagVals);
								switch(i) {
								case	1:	/* Init */
									Project.Pops[n].Flags[0] = menu("Select initialization type",InitNames,NULL)-1;
								break;
								case	2:	/* Select */
									Project.Pops[n].Flags[1] = menu("Select selection type",SelectNames,NULL)-1;
								break;
								case	3:	/* Crossover */
									Project.Pops[n].Flags[2] = menu("Select crossover type",CrossoverNames,NULL)-1;
								break;
								case	4:	/* Crowding */
									Project.Pops[n].Flags[3] ^= 1;
 								break;
								case	5:	/* Mutation */
									Project.Pops[n].Flags[4] ^= 1;
								break;
								case	6:	/* Pre-Mutation */
									Project.Pops[n].Flags[5] ^= 1;
								break;
								case	7:	/* Elitism */
									Project.Pops[n].Flags[6] = int_query("Elite individuals [0]?",0);
								break;
								case	8:	/* Dump Worst */
									Project.Pops[n].Flags[7] = int_query("Individuals to remove [0]?",0);
								break;
								case	9:	/* Init Dumped */
									Project.Pops[n].Flags[8] ^= 1;
								break;
								case	10:	/* Newbies */
									Project.Pops[n].Flags[9] = int_query("Individuals to regenerate [0]?",0);
								break;
								case	11:	/* Erase Best */
									Project.Pops[n].Flags[10] ^= 1;
								break;
								case	12:	/* Make Stats */
									Project.Pops[n].Flags[11] ^= 1;
								break;
								}
							} while(i!=mcount(Flags));
						break;

						case	5: /* Report */
							sprintf(buf,"Report generation for %s population",Project.Pops[n].Name);
							do {
								for(i=0;i!=6;i++) {
									RFlagVals[i].Val = Project.Pops[n].RFlags[i];
								}
								i = menu(buf,RFlagMenu,RFlagVals);
								switch(i) {
								case	1:
								case	2:
								case	3:
								case	4:
								case	5:
								case	6:
									Project.Pops[n].RFlags[i-1]^=1;
								break;
								case	7:
									for(i=0;i!=6;i++) {
										Project.Pops[n].RFlags[i]^=1;
									}
								break;
								}
							} while(i!=mcount(RFlagMenu));
						break;
						}
					} while(i!=mcount(PoParms));
				} while(Project.NumPops>1);
			} else {
				msg("Please set number of populations >= 1. [ENTER]");
			}
			i=2;
		break;

		case	3:	/* Make project */
			sprintf(buf,"Destination directory? [%s]? : ",CURRENT_DIR);
			p = str_query(buf,CURRENT_DIR);
			create_project(p);
			free(p);
		break;

		case	4:	/* Save */
			sprintf(buf,"Save as [%s]? : ",Project.Name);
			p = str_query(buf,Project.Name);
			save_template(p);
			free(p);
		break;

		case	5:	/* Load */
			p = str_query("Load project [" DEF_NAME "]? : ",DEF_NAME);
			load_template(p);
			free(p);
		break;

		case	6: /* Quit */
			if(!bool_query("Really quit " DEFN "? :",NO)) {
				i=0;
			}
		break;
		}	/* switch() */
	} while(i!=mcount(Menu1));
} else {
	printf("Too bad.\n");
}

tixE:;

if(Project.Pops!=NULL) {
	for(i=0;i!=Project.NumPops;i++) {
		if(Project.Pops[i].Name!=NULL) {
			free(Project.Pops[i].Name);
		}
	}
	free(Project.Pops);
}
if(Project.Name!=NULL) free(Project.Name);

return(0);
}

int	menu(char *title,char **items,struct VDisp *vs)
{
int i,n;
char buf[255];

do {
	printf("\x1b" "c\n \x1b[4m%s\x1b[0m\n\n",title);
	i=0;
	while(items[i]!=NULL) {
		printf("  %2d) %-24s",i+1,items[i]);
		if(vs!=NULL) {
			switch(vs[i].Type) {
			case	MN_IGN:
			break;

			case	MN_INT:
				printf("(%ld)",vs[i].Val);
			break;

			case	MN_STR:
				if(vs[i].Val!=0) {
					printf("(%s)",(char *)vs[i].Val);
				}
			break;

			case	MN_BOL:
				printf("(%s)",(vs[i].Val)?"Yes":"No");
			break;

			default:
				fprintf(stderr,"menu: BUG!\n");
			}
		}
		printf("\n");
		i++;
	}

	printf("\n==> ");
	fflush(stdout);

	fgets(buf,255,stdin);
	n = atol(buf);

} while(n<1 || n>i);

return(n);
}

int msg(char *m)
{
char	buf[255];
fflush(stdin);
printf(m);
fflush(stdout);
fgets(buf,255,stdin);
return((int)buf[0]);
}

int int_query(char *m,int def)
{
int	i;
char	buf[255],*p;
fflush(stdin);
printf(m);
fflush(stdout);
fgets(buf,255,stdin);
i = strtol(buf,&p,0);
if(p==buf) {
	i = def;
}
return(i);
}

char *str_query(char *m,char *def)
{
char	buf[255],*p;
printf(m);
fflush(stdout);
fgets(buf,255,stdin);
if(buf[0]=='\n') {
	if(def!=NULL) {
		strcpy(buf,def);
	} else {
		return(NULL);
	}
}
if(strchr(buf,'\n')!=NULL) {
	strchr(buf,'\n')[0]=0;
}
p = malloc(strlen(buf)+1);
strcpy(p,buf);
return(p);
}

int mcount(char **s)
{
int i;
i = -1;
while(s[++i]!=0);
return(i);
}

int bool_query(char *s,int d)
{
int v;
v = msg(s);
switch(d) {

case	YES:
	if(toupper(v)!=NO) {
		v=1;
	}
break;

case	NO:
	if(toupper(v)!=YES) {
		v=0;
	}
break;

default:
	fprintf(stderr,"bool_query: BUG!\n");
}

return(v);
}

void save_template(char *fname)
{
FILE *fp;
struct stat State;
int	r,i;

r=stat(fname,&State);

if(r==0) {
	if(bool_query("File exists, overwrite? [y/N] : ",NO)) {
		errno=ENOENT;
	} else {
		return;
	}
}

if(errno==ENOENT) {
	fp = fopen(fname,"wb");
	fprintf(fp,"This file is a product of the Frobozz Magic File Company.\n\n");
	fprintf(fp,"Project: %s\n",Project.Name);
	fprintf(fp,"Populations: %d\n",Project.NumPops);
	fprintf(fp,"CommentLevel: %d\n\n",Project.CLevel);
	for(i=0;i!=Project.NumPops;i++) {
		save_pop(fp,Project.Pops[i]);
	}
	fclose(fp);
} else {
	msg("save_template: General forseen non-fatal error occured. [ENTER]");
}
return;
}

void save_pop(FILE *fp,struct NPop pop)
{
int i;

fprintf(fp,"And now, the %s\n",pop.Name);
fprintf(fp,"\t%d,%d,",pop.Size,pop.Type);
for(i=0;i!=11;i++) {
	fprintf(fp,"%d,",pop.Flags[i]);
}
for(i=0;i!=6;i++) {
	fprintf(fp,"%d,",pop.RFlags[i]);
}
fprintf(fp,"*\n\n");
}

void load_template(char *fname)
{
FILE *fp;
struct stat State;
int	r,i;
char	buf[255];

r=stat(fname,&State);

if(r==0) {
	fp = fopen(fname,"rb");
	fgets(buf,255,fp);
	if(!strcmp(buf,"This file is a product of the Frobozz Magic File Company.\n")) {

		if(Project.Pops!=NULL) {
			for(i=0;i!=Project.NumPops;i++) {
				if(Project.Pops[i].Name!=NULL) {
					free(Project.Pops[i].Name);
				}
			}
			free(Project.Pops);
		}

		if(Project.Name!=NULL) {
			free(Project.Name);
		}

		while(fgets(buf,255,fp),buf[0]=='\n');

		strchr(buf,'\n')[0]=0;
		Project.Name = malloc(strlen(buf)-8);
		strcpy(Project.Name,&buf[9]);

		while(fgets(buf,255,fp),buf[0]=='\n');
		Project.NumPops = strtol(&buf[13],NULL,0);

		while(fgets(buf,255,fp),buf[0]=='\n');
		Project.CLevel = strtol(&buf[14],NULL,0);

		Project.Pops = malloc(Project.NumPops*sizeof(struct NPop));

		for(i=0;i!=Project.NumPops;i++) {
			load_pop(fp,i);
		}

		PrVals[0].Val = (long)Project.Name;
		PrVals[1].Val = Project.NumPops;
		PrVals[2].Val = (long)CLNames[Project.CLevel];
	} else {
		msg("Unrecognized fileformat. [ENTER]");
	}
	fclose(fp);
} else {
	msg("load_template: Unable to open file. [ENTER]");
}
return;

}

void load_pop(FILE *fp,int n)
{
int i;
char buf[255];

while(fgets(buf,255,fp),strncmp(buf,"And now, the ",12));
strchr(buf,'\n')[0]=0;

Project.Pops[n].Name = malloc(strlen(buf)-11);
strcpy(Project.Pops[n].Name,&buf[13]);

fscanf(fp,"\t%d,%d,",&Project.Pops[n].Size,&Project.Pops[n].Type);

for(i=0;i!=11;i++) {
	fscanf(fp,"%d,",&Project.Pops[n].Flags[i]);
}
for(i=0;i!=6;i++) {
	fscanf(fp,"%d,",&Project.Pops[n].RFlags[i]);
}
return;
}

void	create_project(char *dstdir)
{
FILE	*fp;
char	*buf;
struct NPop *Pops;
struct stat State;
int r,rep;
int i,n;
 
buf = malloc(2048);

Pops = Project.Pops;
strcpy(buf,dstdir);
addpart(buf,Project.Name);
strcat(buf,".c");

r=stat(buf,&State);

if(r==0) {
	if(!bool_query("Project exists, overwrite? [y/N] :",NO)) {
		free(buf);
		return;
	}
}

rep=0;
for(i=0;i!=Project.NumPops;i++) {
	for(n=0;n!=6;n++) {
		rep |= Pops[i].RFlags[n];
	}
}

Project.rep = rep;

fp = fopen(buf,"wb");

if(fp!=NULL) {

	switch(Project.CLevel) {
	case	1:
		fprintf(fp,"/*\n *\t%s.c\n *\tCreated by the GAP Conjurer.\n *\n */\n\n",Project.Name);
	break;

	case	2:
	case	3:
		fprintf(fp,"/*\n *\t%s.c\n *\tCreated by the GAP Conjurer.\n *\n *\n * Populations:\n",Project.Name);
		for(i=0;i!=Project.NumPops;i++) {
			fprintf(fp," *\t%s population.\n",Pops[i].Name);
		}
		fprintf(fp," */\n\n");
	break;

	case	4:
		fprintf(fp,"/*\n *\t%s.c\n *\tCreated by the GAP Conjurer.\n *\n *\n * Populations:\n",Project.Name);
		for(i=0;i!=Project.NumPops;i++) {
			fprintf(fp," *\t%s population. (%d individuals initially)\n",Pops[i].Name,Pops[i].Size);
			fprintf(fp," *\t\t %-20s(%s)\n",Flags[0],InitNames[Pops[i].Flags[0]]);
			fprintf(fp," *\t\t %-20s(%s)\n",Flags[1],SelectNames[Pops[i].Flags[1]]);
			fprintf(fp," *\t\t %-20s(%s)\n",Flags[2],CrossoverNames[Pops[i].Flags[2]]);
			fprintf(fp," *\t\t %-20s(%s)\n",Flags[3],(Pops[i].Flags[3])?"Yes":"No");
			fprintf(fp," *\t\t %-20s(%s)\n",Flags[4],(Pops[i].Flags[4])?"Yes":"No");
			fprintf(fp," *\t\t %-20s(%s)\n",Flags[5],(Pops[i].Flags[5])?"Yes":"No");
			fprintf(fp," *\t\t %-20s(%d)\n",Flags[6],Pops[i].Flags[6]);
			fprintf(fp," *\t\t %-20s(%d)\n",Flags[7],Pops[i].Flags[7]);
			fprintf(fp," *\t\t %-20s(%s)\n",Flags[8],(Pops[i].Flags[8])?"Yes":"No");
			fprintf(fp," *\t\t %-20s(%d)\n",Flags[9],Pops[i].Flags[9]);
			fprintf(fp," *\t\t %-20s(%s)\n",Flags[10],(Pops[i].Flags[10])?"Yes":"No");
			fprintf(fp," *\t\t %-20s(%s)\n",Flags[11],(Pops[i].Flags[11])?"Yes":"No");
			fprintf(fp," *\n");
		}
		fprintf(fp," */\n\n");
	break;

	default:
		;
	}


	if(Project.CLevel>2) {
		fprintf(fp,"/* Includes follow */\n");
	}


	i = 0;
	while(includes[i]!=NULL) {
		fprintf(fp,"#include <%s.h>\n",includes[i++]);
	}

	if(rep!=0) {
		fprintf(fp,"#include \"report.h\"\n");
	}

	fprintf(fp,"\n%s",(Project.CLevel>0)?"/* Population defines. */\n\n":"");

	buf[0]=0;
	for(i=0;i!=Project.NumPops;i++) {
		if(Project.CLevel>1) {
			sprintf(buf,"\t/* Initial size of %s population. */",Pops[i].Name);
		}
		fprintf(fp,"#define\t%sSIZE\t%d%s\n",Pops[i].Name,Pops[i].Size,buf);
	}

	fprintf(fp,"\n%s",(Project.CLevel>0)?"/* Population structures. */\n\n":"");

	for(i=0;i!=Project.NumPops;i++) {

		fprintf(fp,"\n/*===== %s =====*/\n\n",Pops[i].Name);

		fprintf(fp,"#%s\tZINIT%d\n",(Pops[i].Flags[0]==1)?"define":"undef",i);
		fprintf(fp,"#%s\tMPCROSS%d\n\n",(Pops[i].Flags[2]==1)?"define":"undef",i);

		switch(Pops[i].Type) {
		case	PTYPE_Bitstring:
			fprintf(fp,"#define\tSIZE%d\t%d%s\n\n",i,1,(Project.CLevel>1)?"\t/* _BYTE_ length of bitstring. */":"");
		break;

		case	PTYPE_Bitmatrix:
			fprintf(fp,"#define\tWIDTH%d\t%d\n",i,8);
			fprintf(fp,"#define\tHEIGHT%d\t%d\n",i,8);
			fprintf(fp,"#define\tXBYTES%d\t((WIDTH%d+7)>>3)\n\n",i,i);
		break;

		case	PTYPE_BRVector:
			fprintf(fp,"#define\tVLENGTH%d\t%d\n\nstatic double Constraints%d[VLENGTH%d][2] = {%s\n",i,DEF_BRVLEN,i,i,(Project.CLevel>0)?" /* Constraint ranges. */":"");
			for(n=0;n!=DEF_BRVLEN-1;n++) {
				fprintf(fp,"   {-1.0,1.0},%s\n",(Project.CLevel>2)?"\t/* This comment is here just to irritate you. */":"");
			}
				fprintf(fp,"   {-1.0,1.0}\n};\n\n");
		break;

		case	PTYPE_Custom:
		break;

		default:
			fprintf(fp,"/* Error in population type. */\n\n");
		}

		fprintf(fp,"struct %s {\n",Pops[i].Name);
		switch(Pops[i].Type) {
		case	PTYPE_Bitstring:
			fprintf(fp,"   unsigned char Data[SIZE%d];\n",i);
		break;

		case	PTYPE_Bitmatrix:
			fprintf(fp,"   unsigned char matrix[HEIGHT%d][XBYTES%d];\n   int x,y,xb;\n",i,i);
		break;

		case	PTYPE_BRVector:
			fprintf(fp,"   double\tv[%d];\n",DEF_BRVLEN);
		break;

		case	PTYPE_Custom:
			if(Project.CLevel>0) {
				fprintf(fp,"   /* Define custom data for %s here. */\n",Pops[i].Name);
			}
		break;

		default:
			fprintf(fp,"/* Error in population type. */\n");
		}
		fprintf(fp,"};\n\n");
	}

	if(Project.CLevel>0) {
		fprintf(fp,"/* Function prototypes follow. */\n\n");
	}

	if(Project.CLevel>1) {
		fprintf(fp,"/* Fitness functions. */\n\n");
	}

	for(i=0;i!=Project.NumPops;i++) {
		fprintf(fp,"double %sFitness(struct %s *);\n",Pops[i].Name,Pops[i].Name);
	}

	fprintf(fp,"\n");
	if(Project.CLevel>1) {
		fprintf(fp,"/* Other functions. */\n\n");
	}

	for(i=0;i!=Project.NumPops;i++) {
		fprintf(fp,"void Init%s(struct %s *);\n",Pops[i].Name,Pops[i].Name);
		if(Pops[i].Flags[4]!=0) {
			fprintf(fp,"void Mutate%s(struct %s *);\n",Pops[i].Name,Pops[i].Name);
		}
		fprintf(fp,"void Cross%s(struct %s *,struct %s *);\n",Pops[i].Name,Pops[i].Name,Pops[i].Name);
		fprintf(fp,"double Compare%s(struct %s *,struct %s *,int);\n",Pops[i].Name,Pops[i].Name,Pops[i].Name);
		fprintf(fp,"void Kill%s(struct %s *);\n",Pops[i].Name,Pops[i].Name);
		fprintf(fp,"\n");
	}

/* Create main() */

	if(Project.CLevel>0) {
		fprintf(fp,"/*=== Main program starts here. ===*/\n\n");
	}

	fprintf(fp,"int main(int cnt,char *arg[])\n{\nint\ti;\nint\tGenerations=10;\n");

	for(i=0;i!=Project.NumPops;i++) {
		fprintf(fp,"struct Population *%sPop;\n",Pops[i].Name);
	}

	if(rep!=0) {
		for(i=0;i!=Project.NumPops;i++) {
			for(n=0;n!=6;n++) {
				if(Pops[i].RFlags[n]!=0) {
					fprintf(fp,"struct Report *%sRep;\n",Pops[i].Name);
					break;
				}
			}
		}
	}

	fprintf(fp,"\n");	

	for(i=0;i!=Project.NumPops;i++) {
		fprintf(fp,"struct TagItem %sInitTags[] = {\n"\
		"   {POP_Init,(IPTR)Init%s},\n"\
		"   {POP_Destruct,(IPTR)Kill%s},\n"\
		"   {POP_Cache,TRUE},\n"\
		"   {TAG_DONE,0L}\n};\n\n",Pops[i].Name,Pops[i].Name,Pops[i].Name);
	}

	for(i=0;i!=Project.NumPops;i++) {
		fprintf(fp,"struct TagItem %sEvolveTags[] = {",Pops[i].Name);
		for(n=0;n!=12;n++) {
			fprintf(fp,"\n   {%s,",FlagTags[n]);
			switch(n) {
			case	0:
				fprintf(fp,"(IPTR)%sFitness},",Pops[i].Name);
			break;
			case	1:
				fprintf(fp,"%s},",SelectDefs[Pops[i].Flags[n]]);
			break;
			case	2:
				fprintf(fp,"(IPTR)Cross%s},",Pops[i].Name);
			break;
			case	3:
				fprintf(fp,"%s},",TF(Pops[i].Flags[n]));
			break;
			case	4:
				if(Pops[i].Flags[n]!=0) {
					fprintf(fp,"(IPTR)Mutate%s},",Pops[i].Name);
				} else {
					fprintf(fp,"NULL},");
				}
			break;
			case	5:
				fprintf(fp,"%s},",TF(Pops[i].Flags[n]));
			break;
			case	6:
				fprintf(fp,"%d},",Pops[i].Flags[n]);
			break;
			case	7:
				fprintf(fp,"%d},",Pops[i].Flags[n]);
			break;
			case	8:
				fprintf(fp,"%s},",TF(Pops[i].Flags[n]));
			break;
			case	9:
				fprintf(fp,"%d},",Pops[i].Flags[n]);
			break;
			case	10:
				fprintf(fp,"%s},",TF(Pops[i].Flags[n]));
			break;
			case	11:
				fprintf(fp,"%s},",TF(Pops[i].Flags[n]));
			break;
			default:
				fprintf(fp,"/* Tag creation error. */\n");
			}
		}
		fprintf(fp,"\n   {TAG_DONE,0L}\n};\n\n");
	}

	fprintf(fp,"if(cnt>1) {\n   if(strcmp(arg[1],\"-h\")==0 || strcmp(arg[1],\"--help\")==0) {\n"\
		"      fprintf(stderr,\"Useage:\\n\\n%%s <Generations>\\n\\n\",arg[0]);\n      return(0);\n   }\n"\
		"   Generations = atoi(arg[1]);\n}\n\nif(Generations<1) {\n   Generations=10;\n}\n\n");

	fprintf(fp,"InitRand(time(NULL));\n\n");

	for(i=0;i!=Project.NumPops;i++) {
		fprintf(fp,"%sPop = CreatePopulation(%sSIZE,sizeof(struct %s),%sInitTags);\n",Pops[i].Name,Pops[i].Name,Pops[i].Name,Pops[i].Name);
	}

	fprintf(fp,"\n");

	if(rep!=0) {
		for(i=0;i!=Project.NumPops;i++) {
			for(n=0;n!=6;n++) {
				if(Pops[i].RFlags[n]!=0) {
					fprintf(fp,"%sRep = MakeReport(\"%s\");\n",Pops[i].Name,Pops[i].Name);
					break;
				}
			}
		}
	}

	fprintf(fp,"\nif(");
	for(i=0;i!=(Project.NumPops-1);i++) {
		fprintf(fp,"%sPop!=NULL && ",Pops[i].Name);
	}
	fprintf(fp,"%sPop!=NULL) {\n",Pops[i].Name);

	fprintf(fp,"\tfor(i=0;i!=Generations;i++) {\n");

	for(i=0;i!=Project.NumPops;i++) {
		fprintf(fp,"\t\t%sPop = Evolve(%sPop,%sEvolveTags);\n",Pops[i].Name,Pops[i].Name,Pops[i].Name);
	}

	if(rep!=0) {
		fprintf(fp,"\n");
		for(i=0;i!=Project.NumPops;i++) {
			buf[0]=0;
			for(n=0;n!=6;n++) {
				if(Pops[i].RFlags[n]!=0) {
					strcat(buf,RepDefs[n]);
					strcat(buf,"|");
				}
			}
			if(buf[0]!=0) {
				buf[strlen(buf)-1]=0;
				fprintf(fp,"\t\tDoReport(%sRep,%s);\n",Pops[i].Name,buf);
			}
		}
	}

	fprintf(fp,"\t}\n}\n\n");

	if(rep!=0) {
		for(i=0;i!=Project.NumPops;i++) {
			for(n=0;n!=6;n++) {
				if(Pops[i].RFlags[n]!=0) {
					fprintf(fp,"if(%sRep!=NULL)EndReport(%sRep);\n",Pops[i].Name,Pops[i].Name);
					break;
				}
			}
		}
	}

	for(i=0;i!=Project.NumPops;i++) {
		fprintf(fp,"if(%sPop!=NULL)DeletePopulation(%sPop);\n",Pops[i].Name,Pops[i].Name);
	}

	fprintf(fp,"\nreturn(0);\n}\n\n");

/* == End of main() writing code == */

if(Project.CLevel>0) {
	fprintf(fp,"/*=== Fitness functions ===*/\n");
	if(Project.CLevel>2) {
		fprintf(fp,"/* Below here are the functions which define how\n * fit an individual is - commponly known as fitness functions.\n */\n\n");
	}
}

for(i=0;i!=Project.NumPops;i++) {
	fprintf(fp,"double %sFitness(struct %s *Polly)\n{\ndouble fitness=0.0;\n",Pops[i].Name,Pops[i].Name);
	if(Project.CLevel>0) {
		fprintf(fp,"/* Write fitness function for %s here. */\n",Pops[i].Name);
	}
	fprintf(fp,"return(fitness);\n}\n\n");
}

if(Project.CLevel>0) {
	fprintf(fp,"/*=== Crossover, Mutation etc. functions. ===*/\n\n");
}

/* Functions */

for(i=0;i!=Project.NumPops;i++) {
	switch(Pops[i].Type) {
	case	PTYPE_Bitstring:
		filtdump(fp,Bitstring_c,i);
	break;

	case PTYPE_Bitmatrix:
		filtdump(fp,Bitmatrix_c,i);
	break;

	case PTYPE_BRVector:
		filtdump(fp,BRVector_c,i);
	break;

	case PTYPE_Custom:
		filtdump(fp,Skeleton_c,i);
	break;

	}
}

/* == magic == */

	if(Project.CLevel>0) {
		fprintf(fp,"\n/* vi: set ts=3: */ /* vim magic */\n");
	}

	fclose(fp);


/* Report files. */

if(rep!=0) {
	if((fp=fopen("report.h","wb"))!=NULL) {
		fwrite(report_h,report_h_SIZE,1,fp);
		fclose(fp);
	}
	if((fp=fopen("report.c","wb"))!=NULL) {
		fwrite(report_c,report_c_SIZE,1,fp);
		fclose(fp);
	}	
}


/* Makefile */

if((fp=fopen("Makefile","wb"))!=NULL) {
	filtdump(fp,mk,0);
	fclose(fp);
}

} else {
	msg("create_project: Unable to open file for writing. [ENTER]");
}

free(buf);

return;
}

char *addpart(char *buf,char *fname)
{
int l;

l = strlen(buf);

#ifdef	AMIGA
if(buf[l-1]==DIR_SEPARATOR || buf[l-1]==':' || buf[0]==0) {
#else
if(buf[l-1]==DIR_SEPARATOR || buf[0]==0) {
#endif
	strcat(buf,fname);
} else {
	buf[l]=DIR_SEPARATOR;
	buf[l+1]=0;
	strcat(buf,fname);
}
return(buf);
}

void filtdump(FILE *fp,char *data,int indx)
{
int	i,n;
int	z;

i = -1;
while((z=data[++i])) {
	if(z=='$') {
		z=data[++i];
		switch(z) {
		case	'1':
		case	'2':
		case	'3':
		case	'4':
			n = z-'1';
			if(Project.CLevel>n) {
				while((z=data[++i])!='$') {
					fputc(z,fp);
				}
			} else {
				while((z=data[++i])!='$');
			}
		break;

		case	'N':
			fprintf(fp,Project.Pops[indx].Name);
		break;

		case	'I':
			fprintf(fp,"%d",indx);
		break;		

		case	'$':
			fputc('$',fp);
		break;

		case	'P':
			fprintf(fp,Project.Name);
		break;

		case	'R':
			if(Project.rep!=0) {
				while((z=data[++i])!='$' || data[i+1]=='$') {
					if(z=='$') {
						i++;
						fputc('$',fp);
					} else {
						fputc(z,fp);
					}
				}
			} else {
				while((z=data[++i])!='$' || data[i+1]=='$') {
					if(z=='$') {
						i++;
					}
				}
			}
		break;

		default:
			fprintf(stderr,"Illegal control token, corrupted memory?\n");
		}
	} else {
		fputc(z,fp);
	}
};

}
