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

char *stpblk(x)
char *x;
{
	while(*x==' '||*x=='\t')
		x++;
	if(*x)
		return(x);
	return("");
}


char *GetName(x,b)
char *x;
char *b;
{
	int ct=0;
	x=stpblk(x);
	if(x==NULL)
		return(NULL);
	while(*x)
	{
		if(!isspace(*x))
		{
			ct++;
			if(ct==32)
			{
				log_error("[ERROR]: Name Too Long '%s'.\n",LineBuf);
				return(NULL);
			}
			*b++= *x++;
		}
		else
			break;
	}
	*b=0;
	return(x);
}

char *GetToken(x,b)
char *x;
char *b;
{
	x=stpblk(x);
	if(x==NULL)
		return(NULL);
	if(strlen(x)==0)
		return(NULL);
	while(*x)
	{
		if(!isspace(*x))
			*b++= *x++;
		else
			break;
		if(x[-1]=='{')
			break;
	}
	*b=0;
	return(x);
}

char *Get_Block(x,buf)
char *x;
char *buf;
{
	while(*x)
	{
		if(*x=='}')
			break;
		else
			*buf++= *x++;
	}
	*buf=0;
	if(!*x)
		return(NULL);
	return(x+1);
}

extern int LineNumber;

char *ReadLongString(f)
FILE *f;
{
	extern char *estralloc();
	char bigbuffer[2048];
	int c;
	int bp=0;
	f=GetFP();
	while((c=fgetc(f))!=EOF)
	{
		if(bp==2047)
		{
			bigbuffer[bp]=0;
			ELine();
			log_error("[ERROR]: Over long, or unterminated string.\n%s\n",bigbuffer);
			panic();
		}
		if(c=='^')
			break;
		if(c=='\n')
			LineNumber++;
		bigbuffer[bp++]=c;
	}
	bigbuffer[bp]=0;
	while(!feof(f)&&fgetc(f)!='\n');
	LineNumber++;
	return(estralloc(bigbuffer));
}

int GetFlagLine(l)
char *l[];
{
	char a[256];
	int ct=0;
	int v=0;
	char *p=LineBuf;
	while(1)
	{
		p=GetToken(p,a);
		if(p==NULL)
			return(v);
		ct=2;
		while(ct<16&&l[ct])
		{
			if(strcmp(l[ct],a)==0)
			{
				v|=(1<<ct);
				goto next;
			}
			ct++;
		}
		ELine();
		log_error("[ERROR]:Unknown Flag '%s'.\n",a);
		return(-1);
		next:;
	}
}
		
LoadOneRoom(f,d)
FILE *f;
int d;
{
	char wdbf[256];
	char namebuf[32];
	char name2buf[32];
	tag r;
	short v;
	int ct;
	tag flagarray[128];
	int ad,no;
	char *p=GetName(LineBuf,namebuf);
	if(!p)
	{
		ELine();
		log_error("[ERROR]: Invalid Name '%s'.\n",LineBuf);
		return(1);
	}
	if(d)
	{
		printf("Processing Room '%s'\n",namebuf);
	}
	p=stpblk(p);
	if(p==NULL)
	{
		ELine();
		log_error("[ERROR]: Room '%s' has no description.\n",namebuf);
		return(1);
	}
	r=AddRoom();
	SetName(r,p);
	p=ReadLongString(f);
	if(p==NULL)
		return(1);
	free(ROOM(r)->rm_Long);
	ROOM(r)->rm_Long=p;
	if(GetLine(f)==NULL)
	{
		ELine();
		log_error("[ERROR]: Unexpected EOF.\n");
		return(1);
	}
	p=GetToken(LineBuf,wdbf);
	if(p==NULL)
	{
		ELine();
		log_error("[ERROR]: Adjective missing in room '%s'.\n",namebuf);
		return(1);
	}
	if(strcmp(wdbf,"$ANY")==0)
	{
		ADJ(r)= -1;
	}
	else
	{
		ad=FindCWord(wdbf,2);
		if(ad==-1)
		{
			AddWord(wdbf,(ad=AllocWC()),2);
		}
		ADJ(r)=ad;
	}
	p=GetToken(p,wdbf);
	if(!p)
	{
		ELine();
		log_error("[ERROR]: Noun missing in room '%s'.\n",namebuf);
		return(1);
	}
	if(strcmp(wdbf,"$ANY")==0)
	{
		NOUN(r)= -1;
	}
	else
	{
		no=FindCWord(wdbf,3);
		if(no==-1)
		{
			AddWord(wdbf,(no=AllocWC()),3);
		}
		NOUN(r)=no;
	}
	if(GetLine(f)==NULL)
	{
		ELine();
		log_error("[ERROR]: Unexpected EOF.\n");
		return(1);
	}
	v=GetFlagLine(RFlagList);
	if(v==-1)
		return(1);
	FLAGS(r)=v|FL_ROOM;
	if(GetLine(f)==NULL)
	{
		ELine();
		log_error("[ERROR]: Unexpected EOF.\n");
		return(1);
	}
	BuildFlagImage(f,flagarray,r,14);
	ct=0;
	while(ct<16-14)
	{
		RUSER(r,ct)=flagarray[ct];
		ct++;
	}
	LOC(r)= ISOBJ(-1);
	OBJECT(r)->ob_Child= -1;
	OBJECT(r)->ob_Next= -1;
	if(v==11&&strlen(name2buf))
	{
		struct Word *dr=FindName(name2buf,1);
		if(!dr)
		{
			ELine();
			log_error("[ERROR]: Unknown room '%s'.\n",name2buf);
			return(1);
		}
		DROPTO(r)=dr->wd_Code;	/* obsolete */
	}
	else
		DROPTO(r)= -1;
	AddName(namebuf,r,1);
	return(0);
}

LoadRoom(f,d)
FILE *f;
int d;
{
	int v=0;
	do
	{
		if(GetLine(f)==NULL)
		{
			ELine();
			log_error("[ERROR]: Unexpected EOF.\n");
			return(1);
		}
		if(*LineBuf!='@')
			v=LoadOneRoom(f,d);
	}
	while(v==0&&*LineBuf!='@');
	return(v);
}

LoadOneObject(f,d)
FILE *f;
int d;
{
	char wdbf[256];
	char namebuf[32];
	char name2buf[32];
	int ad,no;
	int ct=0;
	tag flagarray[128];
	tag r;
	short v;
	int st,mst,sz,wt;
	char *p=GetName(LineBuf,namebuf);
	struct Word *dr;
	if(!p)
	{
		ELine();
		log_error("[ERROR]: Invalid Name '%s'.\n",LineBuf);
		return(1);
	}
	if(d)
	{
		printf("Processing Object '%s'\n",namebuf);
	}
	p=stpblk(p);
	if(p==NULL)
	{
		ELine();
		log_error("[ERROR]: Object '%s' has no description.\n",namebuf);
		return(1);
	}
	r=AddObject();
	SetName(r,p);
	if(GetLine(f)==NULL)
	{
		ELine();
		log_error("[ERROR]: Unexpected EOF.\n");
		return(1);
	}
	p=GetToken(LineBuf,wdbf);
	if(p==NULL)
	{
		ELine();
		log_error("[ERROR]: Adjective missing in object '%s'.\n",namebuf);
		return(1);
	}
	if(strcmp(wdbf,"$ANY")==0)
	{
		ADJ(r)= -1;
	}
	else
	{
		ad=FindCWord(wdbf,2);
		if(ad==-1)
		{
			AddWord(wdbf,(ad=AllocWC()),2);
		}
		ADJ(r)=ad;
	}
	p=GetToken(p,wdbf);
	if(!p)
	{
		ELine();
		log_error("[ERROR]: Noun missing in object '%s'.\n",namebuf);
		return(1);
	}
	if(strcmp(wdbf,"$ANY")==0)
	{
		NOUN(r)= -1;
	}
	else
	{
		no=FindCWord(wdbf,3);
		if(no==-1)
		{
			AddWord(wdbf,(no=AllocWC()),3);
		}
		NOUN(r)=no;
	}
	if(GetLine(f)==NULL)
	{
		ELine();
		log_error("[ERROR]: Unexpected EOF.\n");
		return(1);
	}
	v=GetFlagLine(OFlagList);
	if(v==-1)
		return(1);
	FLAGS(r)=v|FL_OBJECT;
/*
 *	Now read LOC STATE MAXSTATE SIZE WEIGHT users....
 */
	if(GetLine(f)==NULL)
	{
		ELine();
		log_error("[ERROR]: Unexpected EOF.\n");
		return(1);
	}
	if(sscanf(LineBuf,"%31s %d %d %d %d",
		name2buf,&st,&mst,&sz,&wt)!= 5)
	{
		ELine();
		log_error("[ERROR]: Incomplete definition line in object '%s'.\n",
			namebuf);
		return(1);
	}
	if(GetLine(f)==NULL)
	{
		ELine();
		log_error("[ERROR]: Unexpected EOF.\n");
		return(1);
	}
	BuildFlagImage(f,flagarray,r,6);
	while(ct<16-6)
	{
		OUSER(r,ct)=flagarray[ct];
		ct++;
	}
	SetMaxState(r,mst);
	SetState(r,st);
	SETWEIGHT(r,wt);
	SETSIZE(r,sz);
	v=0;
/*
 *	State texts
 */
	while(v<mst)
	{
		p=ReadLongString(f);
		if(p==NULL)
			return(1);
		free(OBJECT(r)->ob_Desc[v]);
		OBJECT(r)->ob_Desc[v]=p;
		v++;
	}
	dr=FindName(name2buf,1);
	OBJECT(r)->ob_Child= -1;
	OBJECT(r)->ob_Next= -1;
	if(!dr)
	{
		Push_Resolution(r,1,name2buf);
	}
	else
	{
		LOC(r)=dr->wd_Code;
		Tree_Link(r,LOC(r));
	}
	AddName(namebuf,r,1);
	return(0);
}

LoadObject(f,d)
FILE *f;
int d;
{
	int v=0;
	do
	{
		if(GetLine(f)==NULL)
		{
			ELine();
			log_error("[ERROR]: Unexpected EOF.\n");
			return(1);
		}
		if(*LineBuf!='@')
			v=LoadOneObject(f,d);
	}
	while(v==0&&*LineBuf!='@');
	return(v);
}


LoadOnePlayer(f,d)
FILE *f;
int d;
{
	char wdbf[256];
	char namebuf[32];
	char name2buf[32];
	int ad,no;
	tag r;
	int v;
	int lv,sc,st;
	int ct;
	tag flagarray[128];
	char *p=GetName(LineBuf,namebuf);
	struct Word *dr;
	if(!p)
	{
		ELine();
		log_error("[ERROR]: Invalid Name '%s'.\n",LineBuf);
		return(1);
	}
	if(d)
	{
		printf("Processing Player '%s'\n",namebuf);
	}
	p=stpblk(p);
	if(p==NULL)
	{
		ELine();
		log_error("[ERROR]: Player '%s' has no description.\n",namebuf);
		return(1);
	}
	r=AddPlayer();
	SetName(r,p);
	if(GetLine(f)==NULL)
	{
		ELine();
		log_error("[ERROR]: Unexpected EOF.\n");
		return(1);
	}
	p=GetToken(LineBuf,wdbf);
	if(p==NULL)
	{
		ELine();
		log_error("[ERROR]: Adjective missing in player '%s'.\n",namebuf);
		return(1);
	}
	if(strcmp(wdbf,"$ANY")==0)
	{
		ADJ(r)= -1;
	}
	else
	{
		ad=FindCWord(wdbf,2);
		if(ad==-1)
		{
			AddWord(wdbf,(ad=AllocWC()),2);
		}
		ADJ(r)=ad;
	}
	p=GetToken(p,wdbf);
	if(!p)
	{
		ELine();
		log_error("[ERROR]: Noun missing in player '%s'.\n",namebuf);
		return(1);
	}
	if(strcmp(wdbf,"$ANY")==0)
	{
		NOUN(r)= -1;
	}
	else
	{
		no=FindCWord(wdbf,3);
		if(no==-1)
		{
			AddWord(wdbf,(no=AllocWC()),3);
		}
		NOUN(r)=no;
	}
	if(GetLine(f)==NULL)
	{
		ELine();
		log_error("[ERROR]: Unexpected EOF.\n");
		return(1);
	}
	v=GetFlagLine(PFlagList);
	if(v==-1)
		return(1);
	FLAGS(r)=v|FL_PLAYER;
/*
 *	Now read LEVEL SCORE STRENGTH VIS u1-u10
 */
	if(GetLine(f)==NULL)
	{
		ELine();
		log_error("[ERROR]: Unexpected EOF.\n");
		return(1);
	}
	if(sscanf(LineBuf,"%31s %d %d %d %d",name2buf,&lv,&sc,&st,&v)
			 != 5)
	{
		ELine();
		log_error("[ERROR]: Incomplete definition line in player '%s'.\n",
			namebuf);
		return(1);
	}
	if(GetLine(f)==NULL)
	{
		ELine();
		log_error("[ERROR]: Unexpected EOF.\n");
		return(1);
	}
	BuildFlagImage(f,flagarray,r,7);
	SETLEVEL(r,lv);
	SETSCORE(r,sc);
	SETSTRENGTH(r,st);
	SETSNOOP(r,-1);
	SETVIS(r,v);
	ct=0;
	while(ct<32-7)
	{
		PUSER(r,ct)=flagarray[ct];
		ct++;
	}
	v=0;
	dr=FindName(name2buf,1);
	OBJECT(r)->ob_Child= -1;
	OBJECT(r)->ob_Next= -1;
	if(!dr)
	{
		Push_Resolution(r,1,name2buf);	
	}
	else
	{
		LOC(r)=dr->wd_Code;
		Tree_Link(r,LOC(r));
	}
	free(PLAYER(r)->pl_IOH[0]);
	p=ReadLongString(f);
	if(!p)
	{
		ELine();
		log_error("[ERROR]: No Here string for player '%s'.\n",namebuf);
		return(1);
	}
	PLAYER(r)->pl_IOH[0]=p;
	free(PLAYER(r)->pl_IOH[1]);
	p=ReadLongString(f);
	if(!p)
	{
		ELine();
		log_error("[ERROR]: No Arrives string for player '%s'.\n",namebuf);
		return(1);
	}
	PLAYER(r)->pl_IOH[1]=p;
	free(PLAYER(r)->pl_IOH[2]);
	p=ReadLongString(f);
	if(!p)
	{
		ELine();
		log_error("[ERROR]: No Leaves string for player '%s'.\n",namebuf);
		return(1);
	}
	PLAYER(r)->pl_IOH[2]=p;
	AddName(namebuf,r,1);
	return(0);
}

LoadPlayer(f,d)
FILE *f;
int d;
{
	int v=0;
	do
	{
		if(GetLine(f)==NULL)
		{
			ELine();
			log_error("[ERROR]: Unexpected EOF.\n");
			return(1);
		}
		if(*LineBuf!='@')
			v=LoadOnePlayer(f,d);
	}
	while(v==0&&*LineBuf!='@');
	return(v);
}


LoadExit(f,d)
FILE *f;
int d;
{
	char nb1[32],nb2[32],xb[256];
	tag fr,tr;
	int v;
	char *p;
	struct Word *wd;
	while(1)
	{
		if(GetLine(f)==NULL)
		{
			ELine();
			log_error("[ERROR]: Unexpected EOF.\n");
			return(1);
		}
		if(*LineBuf=='@')
			return(0);
		p=GetName(LineBuf,nb1);
		if(p==NULL)
		{
			ELine();
			log_error("[ERROR]: Bad exit '%s'.\n",LineBuf);
			return(1);
		}
		p=GetToken(p,xb);
		if(p==NULL)
		{
			ELine();
			printf("[ERROR]: Bad exit '%s'.\n",LineBuf);
			return(1);
		}
		p=GetName(p,nb2);
		if(p==NULL)
		{
			ELine();
			log_error("[ERROR]: Bad exit '%s'.\n",LineBuf);
			return(1);
		}
		if(strcmp(xb,"NE")==0)
			v=6;
		else if(strcmp(xb,"NW")==0)
			v=7;
		else if(strcmp(xb,"SE")==0)
			v=8;
		else if(strcmp(xb,"SW")==0)
			v=9;
		else if(strcmp(xb,"IN")==0)
			v=10;
		else if(strcmp(xb,"OUT")==0)
			v=11;
		else switch(*xb)
		{
			case 'N':v=0;break;	
			case 'E':v=1;break;
			case 'S':v=2;break;
			case 'W':v=3;break;
			case 'U':v=4;break;
			case 'D':v=5;break;
			default:ELine();log_error("[ERROR] Bad exit name '%s'.\n",LineBuf);
			return(1);
		}
		if(strcmp(nb2,"$NONE")==0)
		{
			tr=0;
		}
		else
		{
			wd=FindName(nb2,0);
			if(!wd)
			{
				ELine();
				log_error("[ERROR]: Unknown item '%s'.\n",nb2);
				return(1);
			}
			tr=wd->wd_Code;
		}
		tr=ISOBJ(tr);
		wd=FindName(nb1,0);
		if(wd==NULL)
		{
			ELine();
			log_error("[ERROR]: Unknown item '%s'.\n",nb1);
			return(1);
		}
		fr=ISOBJ(wd->wd_Code);
		if(EXIT(fr,v))
		{
			ELine();
			log_error("[ERROR]: Exit multiply defined '%s' - goes to %s.\n",LineBuf,NAME(EXIT(fr,v)));
			return(1);
		}
		SETEXIT(fr,v,tr);
	}
}
		

void BuildFlagImage(f,x,i,o)
FILE *f;
tag x[];
ITEM i;
int o;
{
	int ct=0;
	char *t=LineBuf;
	char bf[256];
	while(ct<128)
		x[ct++]=0;
	ct=0;
	while(ct<128)
	{
		t=GetToken(t,bf);
		if(!t)
			return;
		if(strcmp(bf,"\\")==0)
		{
			if(GetLine(f)==NULL)
			{
				ELine();
				printf("Unexpected EOF.\n");
				exit(0);
			}
			t=LineBuf;
			continue;
		}
		if(index(bf,'='))
		{
			char *bp=index(bf,'=');
			*bp++=0;
			ct=ResolveConstant(bf);
			if(ct<0||ct>127)
			{
				ELine();
				log_error("[ERROR]: '=' element specifier too large.\n");
				exit(0);
			}
			x[ct]=ResolveOConstant(bp,i,o+ct);
		}
		else
			x[ct]=ResolveOConstant(bf,i,o+ct);
		ct++;
	}
}

static tag ConstVal[400];
static char *Constants[400];

LoadConstants(f)
FILE *f;
{
	char *bp;
	int n=0;
	while(1)
	{
		if(GetLine(f)==NULL)
		{
			ELine();
			log_error("Unexpected EOF.\n");
			panic();
		}
		if(*LineBuf=='@')
			return;
		bp=index(LineBuf,'=');
		if(!bp)
		{
			ELine();
			log_error("Missing '='.\n");
			panic();
		}
		*bp++=0;
		/*
		 *	Crude... should be a list really
		 */
		if(n==400)
		{
			ELine();
			log_error("Too many constants.\n");
			panic();
		}
		Constants[n]=estralloc(LineBuf);
		ConstVal[n]=ResolveConstant(bp);
		n++;
	}
}

tag ResolveOConstant(x,i,o)
char *x;
tag i;
int o;
{
	if(*x=='@')
	{
		struct Word *dr=FindName(x+1,0);
		if(!dr)
		{
			Push_Resolution(i,o,x+1);
			return(-1);
		}
		else
			return(ISOBJ(dr->wd_Code));
	}
	return(ResolveConstant(x));
}

tag ResolveConstant(x)
char *x;
{
	int v;
	if(isdigit(*x)||*x=='-')
	{
		if(sscanf(x,"%d",&v)!=1)
		{
			ELine();
			log_error("Bad number '%s'.\n",x);
			panic();
		}	
		return(v);
	}
	v=0;
	while(Constants[v])
	{
		if(strcmp(Constants[v],x)==0)
			return(ConstVal[v]);
		v++;
	}
	ELine();
	log_error("Unknown constant '%s'\n",x);
	panic();
	return(0); /* Just to keep compiler happy */
}