/*
 * The GAP Conjurer (C)1999 Peter Bengtsson
 *
 * History:
 *
 *		22/4-1999: v1.0
 *
 *
 */

#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"

const char *__v__="$VER: GAP-Conjurer 1.0 (22.4.99) ©1999 Peter Bengtsson";

/* #define	CLEAR	"\x1b" "c"  */

#define	CLEAR	"\x1b[H\x1b[J"

#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	WIZFLG_PLOT		(1<<0)
#define	WIZFLG_PSPLOT	(1<<1)
#define	WIZFLG_MPASS	(1<<2)

#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 *RepNames[] = {
	"Average",
	"Median",
	"Typecount",
	"Maximum",
	"Minimum",
	"Standard Deviation"
};

char *RepExts[] = {
	"avg",
	"med",
	"typ",
	"max",
	"min",
	"dev"
};

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",
	"Multiple sourcefiles",
	"GNUPlot script",
	"PostScript plots",
	"Exit",
	NULL
};

struct VDisp PrVals[] = {
	{MN_STR,0},
	{MN_INT,0},
	{MN_STR,0},
	{MN_BOL,0},
	{MN_BOL,0},
	{MN_BOL,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",
	"Runs to average",
	"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_INT,0},
	{MN_IGN,0}
};

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

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

char	*MOTD;

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	file_write(char *);


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];
PrVals[3].Val = 0;
Project.sep=0;
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;
Project.flags=0;
Project.pass=1;

MOTD = malloc(1024);

strcpy(MOTD,"Welcome.");

switch(cnt) {
case	1:
	z=msg(CLEAR "\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;
					sprintf(MOTD,"Project name is now %s.",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;
						}
					}
					sprintf(MOTD,"%d populations created.",Project.NumPops);
				break;

				case	3:	/* Comment level */
					Project.CLevel = menu("Comment level",CLMenu,NULL)-1;
					PrVals[2].Val = (long)CLNames[Project.CLevel];
					sprintf(MOTD,"Comment level set to %s.",CLNames[Project.CLevel]);
				break;

				case	4:
					Project.sep^=1;
					PrVals[3].Val^=1;
				break;

				case	5:
					Project.flags ^= WIZFLG_PLOT;
					PrVals[4].Val = Project.flags&WIZFLG_PLOT;
				break;

				case	6:
					Project.flags ^= WIZFLG_PSPLOT;
					PrVals[5].Val = Project.flags&WIZFLG_PSPLOT;
				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;
							sprintf(MOTD,"Name changed to %s.",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;
							sprintf(MOTD,"Size changed to %d.",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];
							sprintf(MOTD,"Genome type set to %s.",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];
								}
								RFlagVals[7].Val = Project.pass;
								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;
								case	8:
									Project.flags &= ~WIZFLG_MPASS;
									i = int_query("Take average of how many passes [1] ?",1);
									if(i>1) {
										Project.flags |= WIZFLG_MPASS;
										Project.pass = i;
									} else {
										i = 1;
									}
									RFlagVals[7].Val = i;
									Project.pass = i;
								break;
								}
							} while(i!=mcount(RFlagMenu));
							for(i=0;i!=6;i++) {
								if(Project.Pops[n].RFlags[i]!=0 && Project.Pops[n].Flags[11]==0) {
									Project.Pops[n].Flags[11] = 1;
									strcpy(MOTD,"Auto-enabled statistics.");
									break;
								}
							}
							i=0;
						break;
						}
					} while(i!=mcount(PoParms));
				} while(Project.NumPops>1);
			} else {
				strcpy(MOTD,"Too few populations.");
				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);
}

if(MOTD) {
	free(MOTD);
}

return(0);
}

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

do {
	printf(CLEAR "\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==> ");

	if(MOTD!=NULL) {
		printf("\n\nLast Message: %s\n\x1b[3A\x1b[4C",MOTD);
	}

	fflush(stdout);

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

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

printf("\x1b[J");fflush(stdout);

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;
	}
}

strcpy(MOTD,"Error while saving project template.");

if(errno==ENOENT) {
	fp = fopen(fname,"wb");
	if(fp!=NULL) {
		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",Project.CLevel);
		fprintf(fp,"Multisource: %d\n",Project.sep);
		fprintf(fp,"Storyline: %d\n",Project.flags);
		fprintf(fp,"Passcount: %d\n\n",Project.pass);
		for(i=0;i!=Project.NumPops;i++) {
			save_pop(fp,Project.Pops[i]);
		}
		strcpy(MOTD,"Project template saved.");
		fclose(fp);
	} else {
		msg("save_template: General forseen non-fatal error occured. [ENTER]");
	}
} 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!=12;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");
	if(fp==NULL) {
		strcpy(MOTD,"Unable to read template.");
		msg("load_template: Irritating error, fopen() failed. [ENTER]");
		return;
	}
	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);

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

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

		while(fgets(buf,255,fp),buf[0]=='\n');
		Project.pass = strtol(&buf[11],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];
		PrVals[3].Val = Project.sep;
		PrVals[4].Val = Project.flags&WIZFLG_PLOT;
		PrVals[5].Val = Project.flags&WIZFLG_PSPLOT;
		sprintf(MOTD,"Template for the %s project loaded.",Project.Name);
	} else {
		strcpy(MOTD,"Unrecognized fileformat.");
		msg("Unrecognized fileformat. [ENTER]");
	}
	fclose(fp);
} else {
	strcpy(MOTD,"Unable to examine template.");
	msg("load_template: File inaccessible. [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!=12;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,*sp;
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(Project.sep) {
	strcpy(buf,dstdir);
	addpart(buf,Project.Name);
	strcat(buf,"Fitness.c");
	sp = fopen(buf,"wb");
} else {
	sp = fp;
}

if(fp!=NULL && sp!=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,"%sdouble %sFitness(struct %s *);\n",(Project.sep!=0)?"extern ":"",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%s;\nint\tGenerations=10;\n",(Project.flags&WIZFLG_MPASS)?",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");
	}

	if(Project.flags&WIZFLG_MPASS) {
		fprintf(fp,"struct TagItem ReportTags[] = {\n");
		fprintf(fp,"\t{REP_Generations,0},\n\t{REP_Multipass,TRUE},\n");
		fprintf(fp,"\t{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");

	if(Project.flags&WIZFLG_MPASS) {
		fprintf(fp,"ReportTags[0].ti_Data = (IPTR)Generations;\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,"%sRep = MakeReport(\"%s\",%s);\n",Pops[i].Name,Pops[i].Name,(Project.flags&WIZFLG_MPASS)?"ReportTags":"NULL");
					break;
				}
			}
		}
	}

	fprintf(fp,"\n");

	if(Project.flags&WIZFLG_MPASS) {
		fprintf(fp,"for(n=0;n!=%d;n++) {",Project.pass);
		if(Project.CLevel>2) {
			sprintf(buf,"\t/* Perform %d runs of this GA. */",Project.pass);
		}
		fprintf(fp,"\n\n");
	}

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

	fprintf(fp,"\n%sif(",(Project.flags&WIZFLG_MPASS)?"\t":"");
	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,"%s\tfor(i=0;i!=Generations;i++) {\n",(Project.flags&WIZFLG_MPASS)?"\t":"");

	for(i=0;i!=Project.NumPops;i++) {
		fprintf(fp,"%s\t\t%sPop = Evolve(%sPop,%sEvolveTags);\n",(Project.flags&WIZFLG_MPASS)?"\t":"",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,"%s\t\tDoReport(%sRep,%sPop,%s);\n",(Project.flags&WIZFLG_MPASS)?"\t":"",Pops[i].Name,Pops[i].Name,buf);
			}
		}
	}

	fprintf(fp,"%s\t}\n%s}\n\n",(Project.flags&WIZFLG_MPASS)?"\t":"",(Project.flags&WIZFLG_MPASS)?"\t":"");

	for(i=0;i!=Project.NumPops;i++) {
		fprintf(fp,"%sif(%sPop!=NULL)DeletePopulation(%sPop);\n",(Project.flags&WIZFLG_MPASS)?"\t":"",Pops[i].Name,Pops[i].Name);
	}

	if(Project.flags&WIZFLG_MPASS) {
		fprintf(fp,"}\n");
	}

	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,"if(%sRep!=NULL)EndReport(%sRep);\n",Pops[i].Name,Pops[i].Name);
					break;
				}
			}
		}
	}

	if(rep!=0) {
		if(Project.flags&WIZFLG_PSPLOT) {
			fprintf(fp,"\nsystem(\"gnuplot MakePS\");\n");
		}
		if(Project.flags&WIZFLG_PLOT) {
			fprintf(fp,"\nsystem(\"gnuplot ShowReport\");\n");
		}
	}

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

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

if(Project.sep!=0) {
	fprintf(sp,"#include <GAP.h>\n#include <math.h>\n\n");
	for(i=0;i!=Project.NumPops;i++) {
		fprintf(sp,"double %sFitness(struct %s *);\n",Pops[i].Name,Pops[i].Name);
	}
	fprintf(sp,"\n");
}

if(Project.CLevel>0) {
	fprintf(sp,"/*=== Fitness functions ===*/\n");
	if(Project.CLevel>2) {
		fprintf(sp,"/* 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(sp,"double %sFitness(struct %s *Polly)\n{\ndouble fitness=0.0;\n",Pops[i].Name,Pops[i].Name);
	if(Project.CLevel>0) {
		fprintf(sp,"/* Write fitness function for %s here. */\n",Pops[i].Name);
	}
	fprintf(sp,"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);
	if(fp!=sp) {
		fclose(sp);
	}


/* Report files. */

if(rep!=0) {
	strcpy(buf,dstdir);
	addpart(buf,"report.h");
	if(file_write(buf)) {
		if((fp=fopen(buf,"wb"))!=NULL) {
			fwrite(report_h,report_h_SIZE,1,fp);
			fclose(fp);
		}
	}

	strcpy(buf,dstdir);
	addpart(buf,"report.c");
	if(file_write(buf)) {
		if((fp=fopen(buf,"wb"))!=NULL) {
			fwrite(report_c,report_c_SIZE,1,fp);
			fclose(fp);
		}	
	}
}


/* Makefile */

strcpy(buf,dstdir);
addpart(buf,"Makefile");
if(file_write(buf)) {
	if((fp=fopen(buf,"wb"))!=NULL) {
		filtdump(fp,mk,0);
		fclose(fp);
	}
}

/* PostScript(tm) generation script */

if(Project.flags&WIZFLG_PSPLOT) {
	strcpy(buf,dstdir);
	addpart(buf,"MakePS");
	if(file_write(buf)) {
		if((fp=fopen(buf,"wb"))!=NULL) {
			fprintf(fp,"\n# PostScript(tm) generation script for GNUPlot.\n# Created by the GAP Conjurer.\n\n");
			fprintf(fp,"set terminal postscript landscape enhanced monochrome\n\n");
			for(i=0;i!=Project.NumPops;i++) {
				for(n=0;n!=6;n++) {
					if(Pops[i].RFlags[n]!=0) {
						fprintf(fp,"set output \"%s%s.ps\"\n",Pops[i].Name,RepDefs[n]);
						fprintf(fp,"set title \"%s %s\"\n",Pops[i].Name,RepNames[n]);
						fprintf(fp,"plot \"%s.%s\" with lines\n\n",Pops[i].Name,RepExts[n]);
					}
				}
			}
			fclose(fp);
		}
	}
}

if(Project.flags&WIZFLG_PLOT) {
	strcpy(buf,dstdir);
	addpart(buf,"ShowReport");
	if(file_write(buf)) {
		if((fp=fopen(buf,"wb"))!=NULL) {
			fprintf(fp,"\n# Report display script for GNUPlot.\n# Generated by the GAP Conjurer.\n\n");
			buf[0] = 0;
			for(i=0;i!=Project.NumPops;i++) {
				for(n=0;n!=6;n++) {
					if(Pops[i].RFlags[n]!=0) {
						sprintf(&buf[strlen(buf)],"\t\"%s.%s\" with lines,\\\n",Pops[i].Name,RepExts[n]);
					}
				}
			}
			buf[strlen(buf)-3] = 0;
			fprintf(fp,"plot \\\n%s",buf);
			fprintf(fp,"\npause -1 \"Press enter to continue...\"");
			fclose(fp);
		}
	}
}

	strcpy(MOTD,"Created project.");

} else {
	strcpy(MOTD,"Error creating project.");
	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	'U':
			if(Project.sep!=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;

		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);
	}
};

}

int file_write(char *fname)
{
int	r,i=0;
struct stat State;
char	buf[255];

r=stat(fname,&State);

if(r==0) {
	sprintf(buf,"%s exists, overwrite? " DEFN " : ",fname);
	if(bool_query(buf,NO)) {
		i = 1;
	}
} else {
	switch(errno) {

	case	ENOENT:
		i = 1;
	break;

	case	EBUSY:
		msg("File already in use. [ENTER]");
		i = 0;
	break;

	case	EPERM:
		msg("Access denied. [ENTER]");
		i = 0;
	break;

	default:
		sprintf(buf,"Unexpected forseen but unknown error on file \"%s\". [ENTER]",fname);
		msg(buf);
		i = 0;
	}
}

return(i);
}

