#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "struct.h"

/*
 *	Bootstrap Loader Routine
 *
 */

extern char LineBuf[];	/* Holds input lines */

Load_Database(name,d)
char *name;
int d;
{
	FILE *f=fopen(name,"r");
	int er;
	if(!f)
	{
		printf("[ERROR]: Database not found.\n");
		exit(1);
	}
	er=Load_From_File(f,d);
	fclose(f);
	SysFlags[15]=ISOBJ(NObs());
	SysFlags[16]=ISOBJ(MAXU);
	return(er);
}

/*
 *	Perform the actual buisiness and load the thing
 */
 
Load_From_File(f,d)
FILE *f;
int d;
{
	extern char *GetLine();
	int er;
	tag ct=0;
	while(ct<MAXU)
	{
		fflush(stdout);
		AddPlayer();
		fflush(stdout);
		LOC(ISOBJ(ct))= -1;
		fflush(stdout);
		SETSNOOP(ISOBJ(ct),ISOBJ(-1));
		fflush(stdout);
		SETVIS(ISOBJ(ct),1000);
		fflush(stdout);
		FLAGS(ISOBJ(ct))|=FL_PLAYER;
		fflush(stdout);
		ct++;
	}
	er=Load_GameName(f,d);
	if(er)
		return(er);
	if(GetLine(f)==NULL)
	{
		ELine();
		printf("[ERROR]: Unexpected EOF.\n");
		return(1);
	}
	if(strcmp(LineBuf,"@ARCHID"))
	{
		ELine();
		printf("[ERROR]: ARCHID block expected.\n");
		return(1);
	}
	er=Load_ArchID(f,d);
	if(er)
		return(er);
	if(strcmp(LineBuf,"@ARCHNAME"))
	{
		ELine();
		printf("[ERROR]: ARCHNAME block expected.\n");
		return(1);
	}
	er=Load_ArchName(f,d);
	if(er)
		return(er);
	if(strcmp(LineBuf,"@RFLAG"))
	{
		ELine();
		printf("[ERROR]: RFLAG block expected.\n");
		return(1);
	}
	er=LoadRFlag(f,d);
	if(er)
		return(er);
	if(strcmp(LineBuf,"@OFLAG"))
	{
		ELine();
		printf("[ERROR]: OFLAG block expected.\n");
		return(1);
	}
	er=LoadOFlag(f,d);
	if(er)
		return(er);
	if(strcmp(LineBuf,"@PFLAG"))
	{
		ELine();
		printf("[ERROR]: PFLAG block expected.\n");
		return(1);
	}
	er=LoadPFlag(f,d);
	if(er)
		return(er);
/*
 *	Any of the routines from this point on can legitimately be called
 *	by users within the game system, by other means
 */		
	if(strcmp(LineBuf,"@CONSTANTS")==0)
	{
		LoadConstants(f);
	}
	if(strcmp(LineBuf,"@VOCAB"))
	{
		ELine();
		printf("[ERROR]: VOCAB block expected.\n");
		return(1);
	}
	er=LoadVocab(f,d);
	if(er)
		return(er);
	while(1)
	{
		if(strcmp(LineBuf,"@ROOM")==0)
		{
			er=LoadRoom(f,d);
			if(er)
				return(er);
		}
		else if(strcmp(LineBuf,"@PLAYER")==0)
		{
			er=LoadPlayer(f,d);
			if(er)
				return(er);
		}
		else if(strcmp(LineBuf,"@OBJECT")==0)
		{
			er=LoadObject(f,d);
			if(er)
				return(er);
		}
		else break;
	}
	ResolveReferences();	/* sort out forward item references */
	if(strcmp(LineBuf,"@EXIT"))
	{
		ELine();
		printf("[ERROR]: EXIT block expected.\n");
		return(1);
	}
	er=LoadExit(f,d);
	if(er)
		return(er);
	if(strcmp(LineBuf,"@MESSAGE"))
	{
		ELine();
		printf("[ERROR]: MESSAGE block expected.\n");
		return(1);
	}
	er=LoadMessage(f,d);
	if(er)
		return(er);
	if(strcmp(LineBuf,"@ITEMTABLES")==0)
		er=LoadItemTables(f,d);
	if(er)
		return(er);
	if(strcmp(LineBuf,"@TABLES"))
	{
		ELine();
		printf("[ERROR]: TABLES block expected.\n");
		return(1);
	}
	er=LoadTables(f,d);
	if(er)
		return(er);
	if(GetLine(f)==NULL)
	{
		ELine();
		printf("[ERROR]: Unexpected EOF.\n");
		return(1);
	}
	if(strcmp(LineBuf,"@TABLE"))
	{
		ELine();
		printf("[ERROR]: TABLE block expected.\n");
		return(1);
	}
	er=LoadEachTable(f,d);
	if(er)
		return(er);
	return(0);
}

char LineBuf[256];	/* Input buffer */
int LineNumber=0;	/* Line reference */

void ELine()		/* Error formatting */
{
	log_error("%d:",LineNumber);
}

static int line_stack[32];	/* Include control */
static FILE *file_stack[32];
static int file_sp=0;


/*
 *	Do an include
 */
 
void Push_File(f)
FILE *f;
{
	if(file_sp==32)
	{
		log_error("Too many levels of include.\n",NULL);
		panic();
	}
	line_stack[file_sp]=LineNumber;
	LineNumber=0;
	file_stack[file_sp++]=f;
}

/*
 *	End an include
 */
FILE *act_f=NULL;
 
FILE *Pop_File()
{
	if(file_sp==0)
		return(NULL);
	if(act_f) fclose(act_f);
	LineNumber=line_stack[--file_sp];
	return(file_stack[file_sp]);
}


FILE *GetFP()
{
	return(act_f);
}

/*
 *	Line input routine, conceals comments, and includes from rest of game
 *	compilation system
 */
 
char *GetLine(f)
FILE *f;
{
	if(act_f==NULL)
		act_f=f;
loop:	do
	{
		if(fgets(LineBuf,255,act_f)==NULL)
		{
			if((act_f=Pop_File())==NULL)
				return(NULL);
			else
			{
				printf("end of include..\n");
				*LineBuf=';';
				continue;
			}
		}
		LineNumber++;
	}
	while(*LineBuf==';');
	if(*LineBuf=='<')
	{
		*index(LineBuf,'\n')=0;
		Push_File(act_f);
		act_f=fopen(LineBuf+1,"r");
		if(!act_f)
		{
			perror(LineBuf+1);
			exit(1);
		}
		printf("Including '%s'..\n",LineBuf+1);
		goto loop;
	}
	if(LineBuf[strlen(LineBuf)-1]=='\n')
		LineBuf[strlen(LineBuf)-1]=0;
	else
		printf("[WARNING]: Line too long.\n");
/*	printf(">>%s\n",LineBuf);	*/
	return(LineBuf);
}
		
int ArchID[16];		/* ArchID's (unused) */
char *ArchName[16];	/* Archwiz names */
char GameName[32];	/* The game name */
char *RFlagList[16]=
{
	"{res0}",
	"{res1}",
	"fall",		/* UNIMPLEMENTED */
	"dark",
	"small",	/* UNIMPLEMENTED */
	"death",	/* UNIMPLEMENTED */
	NULL
};

char *OFlagList[16]=
{
	"{res0}",
	"{res1}",
	"flannel",
	"lit0",
	"litall",
	"worn",
	NULL
};

char *PFlagList[16]=
{
	"{res0}",
	"{res1}",
	"brief",
	"male",
	"blind",
	"deaf",
	"asleep",
	"seedark",
	NULL
};


/*
 *	Look up %name stuff
 */
 
FindFlagListEntry(x)
char *x;
{
	int ct=0;
	while(ct<16&&PFlagList[ct])
	{
		if(strcmp(PFlagList[ct],x)==0)
			return(ct);
		ct++;
	}
	ct=0;
	while(ct<16&&RFlagList[ct])
	{
		if(strcmp(RFlagList[ct],x)==0)
			return(ct);
		ct++;
	}
	ct=0;
	while(ct<16&&OFlagList[ct])
	{
		if(strcmp(OFlagList[ct],x)==0)
			return(ct);
		ct++;
	}
	return(-1);
}

/*
 *	Read in archwizard identities
 */
	
Load_ArchID(f,d)
FILE *f;
int d;
{
	int ct=0;
	while(ct<16)
	{
		if(GetLine(f)==NULL)
			return(-1);
		if(*LineBuf=='@')
			return(0);
		if(d)
			printf("ArchID %d=%s\n",ct,LineBuf);
		if(sscanf(LineBuf,"%d",&ArchID[ct])==0)
		{
			ELine();
			printf("[ERROR]: Invalid ArchID\n");
			return(-1);
		}
		ct++;
	}
	ELine();
	printf("[ERROR]: Max ArchID is 16.\n");
	return(-1);
}

Load_ArchName(f,d)
FILE *f;
int d;
{
	int ct=0;
	while(ct<16)
	{
		if(GetLine(f)==NULL)
			return(-1);
		if(*LineBuf=='@')
			return(0);
		if(d)
			printf("ArchName %d=%s\n",ct,LineBuf);
		ArchName[ct]=(char *)estralloc(LineBuf);
		ct++;
	}
	ELine();
	printf("[ERROR]: Max ArchName is 16.\n");
	return(-1);
}

/*
 *	Obtain game name
 */
 
Load_GameName(f,d)
FILE *f;
int d;
{
	char *t;
	if(GetLine(f)==NULL)
	{
		ELine();
		printf("[ERROR]: No GameName field.\n");
		return(-1);
	}
	t=LineBuf;
	while(*t)
	{
		if(*t==':')
			break;
		else
			t++;
	}
	if(!*t)
	{
		ELine();
		printf("[ERROR]: GameName not specified.\n");
		return(-1);
	}
	t++;
	if(!*t)
	{
		ELine();
		printf("[ERROR]: Null GameName.\n");
		return(-1);
	}
	if(strlen(t)>31)
	{
		ELine();
		printf("[ERROR]: GameName too long.\n");
		return(-1);
	}
	strcpy(GameName,t);
	return(0);
}

/*
 *	Get room/object/player flags
 */

LoadRFlag(f,d)
FILE *f;
int d;
{
	return(LoadFlagSet(RFlagList,f,d,"Room"));
}

LoadOFlag(f,d)
FILE *f;
int d;
{
	return(LoadFlagSet(OFlagList,f,d,"Object"));
}

LoadPFlag(f,d)
FILE *f;
int d;
{
	return(LoadFlagSet(PFlagList,f,d,"Player"));
}

/*
 *	Load a bitflag set
 */
 
LoadFlagSet(list,f,d,n)
char **list;
FILE *f;
int d;
char *n;
{
	int ct=0;
	while(list[ct])
		ct++;
	while(ct<16)
	{
		if(GetLine(f)==NULL)
		{
			ELine();
			printf("[ERROR]: Unexpected EOF\n");
			return(-1);
		}
		if(*LineBuf=='@')
			return(0);
		list[ct]=(char *)estralloc(LineBuf);
		if(d)
		{
			printf("%s]%d) %s\n",n,ct,LineBuf);
		}
		ct++;
	}
	ELine();
	printf("[ERROR] %s flag list too long.\n",n);
	return(-1);
}

static int wv=0;	/* Word allocator */

AllocWC()
{
	return(++wv);
}

/*
 *	Load up the vocabulary, adds system .words but only first time!
 */
 
LoadVocab(f,d)
FILE *f;
int d;
{
	char *ep,*op;
	char wbuf[256];
	char wt='$';
	int lwv;
	char lwt;
	int wcode;
	static int dotdone=0;	/* uccky hack */
	int ticktick=0;
	if(!dotdone)
	{
		AddWord(".exit",RVC_EXIT,1);
		AddWord(".weight",RVC_WEIGHT,1);
		AddWord(".size",RVC_SIZE,1);
		AddWord(".level",RVC_LEVEL,1);
		AddWord(".score",RVC_SCORE,1);
		AddWord(".strength",RVC_STRENGTH,1);
		AddWord(".snoop",RVC_SNOOP,1);
		AddWord(".vis",RVC_VIS,1);
		AddWord(".ldesc",RVC_LDESC,1);
		AddWord(".name",RVC_NAME,1);
		AddWord(".odesc",RVC_ODESC,1);
		dotdone=1;
	}
	while(1)
	{
		if(ticktick%20==0)
		{
			printf("\r%c\r","|/-\\"[(ticktick/20)%4]);
		}
		ticktick++;
		if(GetLine(f)==NULL)
		{
			ELine();
			log_error("[ERROR]: Unexpected EOF.\n");
			return(-1);
		}
		if(*LineBuf=='@')
			return(0);
		lwv=wv;
		lwt=wt;
		ep=LineBuf;
		op=wbuf;
		while(*ep&&!isspace(*ep))
			*op++=*ep++;
		*op=0;
		if(!*ep)
		{
			ELine();
			log_error("[ERROR]: Malformed word '%s'.\n",LineBuf);
			return(-1);
		}
		ep=stpblk(ep);
		wt=*ep;
		if(wt=='S')
		{
			wt=lwt;
			wv=lwv;	
		}
		else
			wv++;
		switch(wt)
		{
			case 'V':wcode=1;break;
			case 'A':wcode=2;break;
			case 'N':wcode=3;break;
			case 'P':wcode=4;break;
			default:ELine();
				log_error("[ERROR]: Unknown word type '%c':'%s'.\n",wt,LineBuf);
				return(-1);
		}
		if(FindWord(wbuf,wcode)!=NULL)
		{
			ELine();
			log_error("[ERROR]: Duplicate word '%s'.\n",LineBuf);
			return(-1);
		}
		AddWord(wbuf,wv,wcode);
	}
}