/****************************************************************************\
 *									      *
 *			C R E A T O R    O F   L E G E N D S		      *
 *				(AberMud Version 5)			      *
 *									      *
 *  The Creator Of Legends System is (C) Copyright 1989 Alan Cox, All Rights  *
 *  Reserved.		  						      *
 *									      *
 \****************************************************************************/

#include "System.h"
Module	"Table Compiler";
Version "1.10";
Author	"Alan Cox";


/*
 *	1.00	AGC	Created this file
 *	1.01	AGC	Added support for {$}
 *	1.02	AGC	Added $RM $ME $1 $2
 *	1.03	AGC	Added {$2}
 *	1.04	AGC	Added NONE
 *	1.05	AGC	Added reverse compiler for table editing
 *	1.06	AGC	Added the code to handle named flags
 *	1.07	AGC	Added #item
 *	1.08	AGC	Classes
 *	1.09	AGC	Added the BitName fields
 */

/*
 *	Routines to convert between source and line data
 *
 */


static unsigned short DataBuffer[1024];	/* A Line Buffer */
static short DPtr=0;
static char LineBuffer[512];
static char *LinePtr=LineBuffer;

static char *GetText2()
{
	extern char *strtok2();
	if(LinePtr==LineBuffer)
	{
		LinePtr++;
		return(strtok2(LineBuffer,"\t {","}"));
	}
	return(strtok2(NULL," \t{","}"));
}


static char *GetText()
{
	char *a;
l1:	a=GetText2();
	if(a==NULL)
		return(a);
	if(a==(char *)-1)
		return(a);
	if(strlen(a)==0)
		goto l1;
	return(a);
}
	
static char *GetToken2()
{
	extern char *strtok2();
	if(LinePtr==LineBuffer)
	{
		LinePtr++;
		return(strtok2(LineBuffer," \n\t,"," \n\t,"));
	}
	return(strtok2(NULL," \n\t,"," \n\t,"));
}

static char *GetToken()
{
	char *a;
l1:	a=GetToken2();
	if(a==NULL)
		return(a);
	if(strlen(a)==0) goto l1;
	return(a);
}

static int WriteDb(n)
int n;
{
	if(DPtr==1024)
		return(-1);
	DataBuffer[DPtr++]=n;
	return(0);
}

static int EncodeFlag(i)
ITEM *i;
{
	char *a=GetToken();
	int b;
	int shf=0;
	if(a==NULL)
	{
		SendItem(i,"Missing Numeric Argument.\n");
		return(-1);
	}
	if((*a=='F')||(*a=='f'))
	{
		a++;
		shf=30000;
	}
	if(*a=='@')
	{
		b=GetFlagByName(a);
		if(b==-1)
		{
			SendItem(i,"Invalid Flag Name - %s.\n",a);
			return(-1);
		}
	}
	else
	{
		if(sscanf(a,"%d",&b)==0)
		{
			SendItem(i,"Invalid Numeric Argument - %s.\n",a);
			return(-1);
		}
	}
	if(WriteDb(b+shf)==-1)
	{
		SendItem(i,"Line Too Complex.\n");
		return(-1);
	}
	return(0);
}
	
static int EncodeClass(i)
ITEM *i;
{
	char *a=GetToken();
	short b=WhichClass(a);
	if(stricmp(a,"0")==0)
	{
		if(WriteDb(-1)==-1)
		{
			SendItem(i,"Line Too Complex.\n");
			return(-1);
		}
		return(0);
	}
	if(b==-1)
	{
		SendItem(i,"Unknown class %s.\n",a);
		return(-1);
	}
	if(WriteDb(b)==-1)
	{
		SendItem(i,"Line Too Complex.\n");
		return(-1);
	}
	return(0);
}	

static int EncodeRFlag(i)
ITEM *i;
{
	char *a=GetToken();
	int b;
	if(a==NULL)
	{
		SendItem(i,"Missing Room Bit Flag.\n");
		return(-1);
	}
	b=FindRBit(a);
	if(b==-1)
	{
		SendItem(i,"Unknown Room Bit Flag '%s'.\n",a);
		return(-1);
	}
	if(WriteDb(b)==-1)
	{
		SendItem(i,"Line Too Complex.\n");
		return(-1);
	}
	return(0);
}

static int EncodeOFlag(i)
ITEM *i;
{
	char *a=GetToken();
	int b;
	if(a==NULL)
	{
		SendItem(i,"Missing Object Bit Flag.\n");
		return(-1);
	}
	b=FindOBit(a);
	if(b==-1)
	{
		SendItem(i,"Unknown Object Bit Flag '%s'.\n",a);
		return(-1);
	}
	if(WriteDb(b)==-1)
	{
		SendItem(i,"Line Too Complex.\n");
		return(-1);
	}
	return(0);
}

static int EncodePFlag(i)
ITEM *i;
{
	char *a=GetToken();
	int b;
	if(a==NULL)
	{
		SendItem(i,"Missing Player Bit Flag.\n");
		return(-1);
	}
	b=FindPBit(a);
	if(b==-1)
	{
		SendItem(i,"Unknown Player Bit Flag '%s'.\n",a);
		return(-1);
	}
	if(WriteDb(b)==-1)
	{
		SendItem(i,"Line Too Complex.\n");
		return(-1);
	}
	return(0);
}

static int EncodeCFlag(i)
ITEM *i;
{
	char *a=GetToken();
	int b;
	if(a==NULL)
	{
		SendItem(i,"Missing Container Bit Flag.\n");
		return(-1);
	}
	b=FindCBit(a);
	if(b==-1)
	{
		SendItem(i,"Unknown Container Bit Flag '%s'.\n",a);
		return(-1);
	}
	if(WriteDb(b)==-1)
	{
		SendItem(i,"Line Too Complex.\n");
		return(-1);
	}
	return(0);
}
	
static int EncodeNumber(i)
ITEM *i;
{
	char *a=GetToken();
	int b;
	int shf=0;
	if(a==NULL)
	{
		SendItem(i,"Missing Numeric Argument.\n");
		return(-1);
	}
	if((*a=='F')||(*a=='f'))
	{
		a++;
		shf=30000;
	}
	if(sscanf(a,"%d",&b)==0)
	{
		SendItem(i,"Invalid Numeric Argument - %s.\n",a);
		return(-1);
	}
	if(WriteDb(b+shf)==-1)
	{
		SendItem(i,"Line Too Complex.\n");
		return(-1);
	}
	return(0);
}

static int EncodeProcess(i)
ITEM *i;
{
	char *a=GetToken();
	int b;
	int shf=0;
	if(a==NULL)
	{
		SendItem(i,"Missing Table Name/Number.\n");
		return(-1);
	}
	if((*a=='F'||*a=='f')&&isdigit(a[1]))
	{
		a++;
		shf=30000;
	}
	if(!shf&&!isdigit(*a))
	{
		b=FindTableByName(a);
		if(b==-1)
		{
			SendItem(i,"Unknown table %s\n",a);
			return(-1);
		}
		if(WriteDb(b)==-1)
		{
			SendItem(i,"Line Too Complex.\n");
			return(-1);
		}
		return(0);
	}
	if(sscanf(a,"%d",&b)==0)
	{
		SendItem(i,"Invalid Numeric Argument - %s.\n",a);
		return(-1);
	}
	if(WriteDb(b+shf)==-1)
	{
		SendItem(i,"Line Too Complex.\n");
		return(-1);
	}
	return(0);
}

static char *WorType[]=
{
	"",
	"noun","preposition","pronoun","class","verb","adjective","noise word"
};
	
static int EncodeWord(i,type)
ITEM *i;
short type;
{
	char *a=GetToken();
	WLIST *b;
	if(a==NULL)
	{
		SendItem(i,"Missing %s argument.\n",WorType[type]);
		return(-1);
	}
	if(stricmp(a,"ANY")==0)
	{
		if(WriteDb(-1)==-1)
		{
			SendItem(i,"Line Too Complex.\n");
			return(-1);
		}
		return(0);
	}
	if(stricmp(a,"NONE")==0)
	{
		if(WriteDb(-2)==-1)
		{
			SendItem(i,"Line Too Complex.\n");
			return(-1);
		}
		return(0);
	}
	if(type==-1)
	{
		b=FindInList(WordList,a,WD_VERB);
		if(!b)
			b=FindInList(WordList,a,WD_ADJ);
	}
	else
		b=FindInList(WordList,a,type);
	if(b==NULL)
	{
		SendItem(i,"Unknown %s '%s'.\n",WorType[type],a);
		return(-1);
	}
	if(WriteDb(b->wd_Code)==-1)
	{
		SendItem(i,"Line Too Complex.\n");
		return(-1);
	}
	return(0);
}	

static ITEM *LockList[256];
static short LockCount;

int RememberToLockItem(i)
ITEM *i;
{
	if(LockCount==256) return(-1);
	LockList[LockCount++]=i;
	return(0);
}

void LockLockList()
{
	short x=0;
	while(x<LockCount)
		LockItem(LockList[x++]);
}

static int EncodeItem(i)
ITEM *i;
{
	char *a=GetToken();
	char *b;
	ITEM *x;
	WLIST *ad,*no;
	int refid=1;
	static WLIST DummyNullWord={"ANY",WD_ADJ,-1,NULL};
	if(a==NULL)
	{
		SendItem(i,"Object expected.\n");
		return(-1);
	}
	if(stricmp(a,"$1")==0)
	{
		x=(ITEM *)1;
		goto l1;
	}
	if(stricmp(a,"$2")==0)
	{
		x=(ITEM *)3;
		goto l1;
	}
	if(stricmp(a,"$ME")==0)
	{
		x=(ITEM *)5;
		goto l1;
	}
	if(stricmp(a,"$AC")==0)
	{
		x=(ITEM *)7;
		goto l1;
	}
	if(stricmp(a,"$RM")==0)
	{
		x=(ITEM *)9;
		goto l1;
	}
	b=GetToken();
	if(*a=='#')
	{
		if(sscanf(a,"#%d",&refid)==0)
		{
			SendItem(i,"Bad # identity %s.\n",a);
			return(-1);
		}
		a=b;
		b=GetToken();
	}
	if(a==NULL||b==NULL)
	{
		SendItem(i,"Object expected.\n");
		return(-1);
	}
	if(stricmp(a,"ANY")==0)
		ad=&DummyNullWord;
	else
		ad=FindInList(WordList,a,WD_ADJ);
	if(ad==NULL)
	{
		SendItem(i,"Unknown Word %s.\n",a);
		return(-1);
	}
	no=FindInList(WordList,b,WD_NOUN);
	if(no==NULL)
	{
		SendItem(i,"Unknown Word %s.\n",b);
		return(-1);
	}
	x=FindMaster(32767,ad->wd_Code,no->wd_Code);
	if(refid>1)
	{
		while((--refid)&&(x))
			x=NextMaster(32767,x,ad->wd_Code,no->wd_Code);
		if(x==NULL)
			SendItem(i,"# out of range for item %s %s.\n",a,b);
	}
	if(x==NULL)
	{
		SendItem(i,"Unknown Item %s %s.\n",a,b);
		return(-1);
	}
	if(RememberToLockItem(x))
		return(-1);	/* Lock the reference */
l1:	if(WriteDb((unsigned short)(((unsigned int)(x))/65536L))==-1)
	{
		SendItem(i,"Line Too Complex.\n");
		return(-1);
	}
	if(WriteDb((unsigned short)(((unsigned int)(x))%65536L))==-1)
	{
		SendItem(i,"Line Too Complex.\n");
		return(-1);
	}
	return(0);
}



static int EncodeText(i,n)
ITEM *i;
int n;
{
	TPTR a;
	char *b;
	b=GetText();
	if(b==NULL)
	{
		SendItem(i,"Text Argument Missing.\n");
		return(-1);
	}
	if(b==(char *)-1)
	{
		SendItem(i,"{ expected.\n");
		return(-1);
	}
	if(stricmp(b,"$")==0)
		a=(TPTR)1;
	else
	{
		if(stricmp(b,"$2")==0)
			a=(TPTR)3;
		else
		{
			if(n)
			
				a=AllocComment(b);
			else
				a=AllocText(b);
		}
	}
	if(WriteDb((unsigned short)(((unsigned int)(a))/65536L))==-1)
	{
		SendItem(i,"Line Too Complex.\n");
		return(-1);
	}
	if(WriteDb((unsigned short)(((unsigned int)(a))%65536L))==-1)
	{
		SendItem(i,"Line Too Complex.\n");
		return(-1);
	}
	return(0);
}

static int EncodeCommand(i,n)
ITEM *i;
short n;	/* Command Code */
{
	int v;
	char *d=Cnd_Table[n];
	if(WriteDb(n)==-1)
	{
		SendItem(i,"Line Too Complex.\n");
		return(-1);
	}
	while(*d!=' ')
	{
		switch(*d)
		{
			case 'w':v=EncodeWord(i,-1);break;
			case 'W':v=EncodeNumber(i);break;
			case 'Z':v=EncodeNumber(i);break;
			case 'I':v=EncodeItem(i);break;
			case 'F':v=EncodeFlag(i);break;
			case 'N':v=EncodeNumber(i);break;
			case 'B':v=EncodeNumber(i);break;
			case 'a':v=EncodeWord(i,WD_ADJ);break;
			case 'n':v=EncodeWord(i,WD_NOUN);break;
			case 'p':v=EncodeWord(i,WD_PREP);break;
			case 'v':v=EncodeWord(i,WD_VERB);break;
			case 'T':v=EncodeText(i,0);break;
			case '$':v=EncodeText(i,1);break;
			case 't':v=EncodeProcess(i);break;
			case 'C':v=EncodeClass(i);break;
			case 'c':v=EncodeCFlag(i);break;
			case 'R':v=EncodeRFlag(i);break;
			case 'O':v=EncodeOFlag(i);break;
			case 'P':v=EncodePFlag(i);break;
			default:
				Error("Invalid Entry in Cnd_Table");
		}
		if(v==-1)
			return(-1);
		d++;
	}
	return(0);
}

static int EncodeLine(i)
ITEM *i;
{
	char *a;
	short c;
	DPtr=0;
	LockCount=0;
	if(EncodeWord(i,WD_VERB)==-1)
		return(-1);
	if(EncodeWord(i,WD_NOUN)==-1)
		return(-1);
	if(EncodeWord(i,WD_NOUN)==-1)
		return(-1);
	while((a=GetToken())!=NULL)
	{
		if(!strlen(a))
			continue;
		c=FindCnd(a);
		if(c==-1)
		{
			SendItem(i,"Unknown Condition/Action '%s'.\n",a);
			return(-1);
		}
		if(EncodeCommand(i,c)==-1)
			return(-1);
	}
	WriteDb(CMD_EOL);
	LockLockList();
	return(0);
}

void LoadLineBuffer(x)
char *x;
{
	strcpy(LineBuffer,x);
	LinePtr=LineBuffer;
}

int EncodeEntry(i,l)
ITEM *i;
LINE *l;
{
	int ct=3;
	if(EncodeLine(i)==-1)
		return(-1);
	free((char *)l->li_Data);
	l->li_Data=(unsigned short *)malloc((DPtr-3)*sizeof(unsigned short));
	if(l->li_Data==NULL)
	{
		SendItem(i,"Out Of Memory.\n");
		return(-1);
	}
	l->li_Verb=DataBuffer[0];
	l->li_Noun1=DataBuffer[1];
	l->li_Noun2=DataBuffer[2];
	while(ct<DPtr)
	{
		l->li_Data[ct-3]=DataBuffer[ct];
		ct++;
	}
	return(0);
}

int EncodeTable(i,t,f)
ITEM *i;
TABLE *t;
FILE *f;
{
	LINE *l;
	char *lp=LineBuffer;
	while(fgets(lp,511,f))
	{
l3:		if((*lp)&&(lp[strlen(lp)-2]=='+'))
		{
			lp+=strlen(lp)-2;		/* Continuation */
			if(lp-LineBuffer>400) goto l2;
			if(!fgets(lp,511,f))
				goto l2;
			goto l3;
		}
l2:		LineBuffer[511]=0;
		LinePtr=LineBuffer;
		lp=LineBuffer;
		l=NewLine(t,32767);
		if(EncodeEntry(i,l)==-1)
			return(-1);
	}
	return(0);
}

static void DiscItem(dp)
unsigned short *dp;
{
	extern unsigned long PairArg();
	ITEM *a=(ITEM *)PairArg(dp);
	if((a!=(ITEM *)1)&&(a!=(ITEM *)3)&&(a!=(ITEM *)5)
			 &&(a!=(ITEM *)7)&&(a!=(ITEM *)9))
		UnlockItem(a);
}

static void DiscText(dp,n)
unsigned short *dp;
int n;
{
	TPTR a=(TPTR )(*dp*65536L+(*(dp+1)));
	if(a==(TPTR )1)
		return;
	if(a==(TPTR )3)
		return;
	if(n)
		FreeComment(a);
	else
		FreeText(a);
}

void WipeLine(l)
LINE *l;
{
	unsigned short *dp=l->li_Data;
	char *a;
	while(*dp!=CMD_EOL)
	{
		a=Cnd_Table[*dp];
		dp++;
		while(*a!=' ')
		{
			switch(*a++)
			{
				case 'W':;
				case 'w':;
				case 'Z':;
				case 'R':dp++;break;
				case 'O':dp++;break;
				case 'P':dp++;break;
				case 'c':dp++;break;
				case 'C':dp++;break;
				case 'N':dp++;break;
				case 'F':dp++;break;
				case 'B':dp++;break;
				case 'a':dp++;break;
				case 'n':dp++;break;
				case 'p':dp++;break;
				case 'v':dp++;break;
				case 't':dp++;break;
				case 'I':DiscItem(dp);
					 dp+=2;
					 break;
				case 'T':DiscText(dp,0);
					dp+=2;
					break;
				case '$':DiscText(dp,1);
					dp+=2;
					break;
				default:
					Error("Cnd_Table: Invalid Entry");
			}
		}
	}
}


void DeleteTable(t)	/* Free Up A Table Entry, except for header */
TABLE *t;
{
	LINE *l=t->tb_First;
	LINE *n;
	t->tb_First=NULL;
	FreeText(t->tb_Name);
	t->tb_Name=AllocText("(unset)");
	while(l)
	{
		n=l->li_Next;
		WipeLine(l);
		free((char *)l->li_Data);
		free((char *)l);
		l=n;
	}
}

/*
 *	Free a table header. Only use this on item bound tables, with
 *	no table data!
 */

void FreeTableHeader(t)
TABLE *t;
{
	FreeText(t->tb_Name);
	free((char *)t);
}

unsigned long PairArg(x)
unsigned short *x;
{
	return((*x)<<16|x[1]);
}

char *NumText(n)
int n;
{
	static char x[16];
	if(n>29999)
		sprintf(x,"F%d",n-30000);
	else
		sprintf(x,"%d",n);
	return(x);
}

/*
 *	BUG:
 *
 * 	This routine should check the line does not overflow the 512 char
 *	buffer. What it does if this happens now is crash, what it should
 *	do is also a problem.
 */
	
void Decompress(line,buffer)
LINE *line;
char *buffer;
{
	register char *TLine;
	unsigned short *x;
	TABLE *tmp;
	sprintf(buffer,"%s %s %s ",FindWText(line->li_Verb,WD_VERB),
				  FindWText(line->li_Noun1,WD_NOUN),
				  FindWText(line->li_Noun2,WD_NOUN));
	x=(unsigned short *)(line->li_Data);
	while(*x!=CMD_EOL)
	{
		int n=*x++;
		int ct=0;
		strcat(buffer,Cnd_Table[n]+8);
		strcat(buffer," ");
		TLine=Cnd_Table[n];
		while(TLine[ct]!=' ')
		{
			ITEM *i;
			TPTR t;
			switch(TLine[ct])
			{
				case 'I':
					i=(ITEM *)PairArg(x);
					x+=2;
					switch((int)i)
					{
						case 1:strcat(buffer,"$1");
							break;
						case 3:strcat(buffer,"$2");
							break;
						case 5:strcat(buffer,"$ME");
							break;
						case 7:strcat(buffer,"$AC");
							break;
						case 9:strcat(buffer,"$RM");
							break;
						default:
						if(!IsUnique(32766,
							i->it_Adjective,
							i->it_Noun))
						sprintf(buffer+strlen(buffer),"#%d ",ItemNumber(32766,
							i));
						if(i->it_Adjective!=-1)
						{
							strcat(buffer,FindWText(i->it_Adjective,WD_ADJ));
							strcat(buffer," ");
						}
						else
							strcat(buffer,"ANY ");
						strcat(buffer,FindWText(i->it_Noun,WD_NOUN));
					}
					break;
				case '$':;
				case 'T':t=(TPTR )PairArg(x);
					 x+=2;
					 if(t==(TPTR )1)
						strcat(buffer,"{$}");
					 else
					 {
						if(t==(TPTR) 3)
							strcat(buffer,"{$2}");
						else
						 {
							strcat(buffer,"{");
							strcat(buffer,TextOf(t));
							strcat(buffer,"}");
						 }
					 }
					 break;
				case 'F':strcat(buffer,GetFlagName(*x++));
					 break;
				case 'N':;
				case 'W':;
				case 'w':;	/* THIS IS WRONG FOR NOW */
				case 'Z':;
				case 'B':strcat(buffer,NumText(*x++));
					 break;
				case 'p':strcat(buffer,FindWText((short)*x++,WD_PREP));
					 break;
				case 'v':strcat(buffer,FindWText((short)*x++,WD_VERB));
					 break;
				case 'a':strcat(buffer,FindWText((short)*x++,WD_ADJ));
					 break;
				case 'n':strcat(buffer,FindWText((short)*x++,WD_NOUN));
					 break;
				case 'C':strcat(buffer,GetClassName(*x++));
					 break;
				case 'c':strcat(buffer,CBitName(*x++));
					 break;
				case 'O':strcat(buffer,OBitName(*x++));
					 break;
				case 'R':strcat(buffer,RBitName(*x++));
					 break;
				case 'P':strcat(buffer,PBitName(*x++));
					 break;
				case 't':tmp=FindTable(*x);
					 if(tmp&&
						strcmp(TextOf(tmp->tb_Name),
							"(unset)"))
						strcat(buffer,
							TextOf(tmp->tb_Name));
					 else
						strcat(buffer,NumText(*x));
					 x++;
					 break;
				default:
					Error("Invalid Entry In Cnd_Table");
			}
			strcat(buffer," ");
			ct++;
		}
	}
}