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

#define CompileRFlag	CompileNumber
#define CompileOFlag	CompileNumber
#define CompilePFlag	CompileNumber


/*
 *	Database function definitions and types required
 *	M=Message I=Item N=Number
 *
 */
 
char *CmTab[]={
	"        DONE",
	"        OK",
	"M       MSG",
	"I       GET",
	"I       DROP",
	"II      PLACE",
	"N       CALL",
	"NN      LD",
	"NN      LDF",
	"N       PRINT",
	"NIN     LDO",
	"INN     SETO",
	"NIN     LDR",
	"INN     SETR",
	"NIN     LDP",
	"INN     SETP",
	"NN      MUL",
	"NN      DIV",
	"NN      ADD",
	"NN      SUB",
	"NN      SET",
	"NN      RES",
	"I       INC",
	"I       DEC",
	"IN      SETSTATE",
	"IMN     DOACT",
	"IMIN    DOTO",
	"        SCORE",
	"        LOOK",
	"I       LOBJL",
	"I       LOBJS",
	"I       PNAME",
	"IN      PDESC",
	"I       PLONG",
	"N       JP",
	"        CR",
	"M       MSGCR",
	"N       Z",
	"NN      EQ",
	"NN      GT",
	"NN      LT",
	"I       AT",
	"NN      BIT",
	"II      ISAT",
	"I       HERE",
	"I       PRES",
	"        DARK",
	"IN      STATE",
	"N       CHANCE",
	"IN      IS",
	"IO      OBIT",
	"IR      RBIT",
	"IP      PBIT",
	"II      CANSEE",
	"N       LEVEL",
	"I       ARCH",
	"IM      CALLED",
	"N       WEIGHS",
	"II      CANPUT",
	"I       GOT",
	"        EXIT",
	"        SAVE",
	"MN      NUMOF",
	"INN     NEIL",
	"INN     GETWORDS",
	"INNNN   NEXTMATCH",
	"NNNN    NEXTGMATCH",
	"M       PARSE",
	"N       TABTO",
	"M       PROMPT",
	"M       AUTOVERB",
	"MM      APPEND",
	"M       TYPE",
	"N       RANDOM",
	"        ZEBEDEE",
	"        PWCHANGE",
	"M       PWSET",
	"MN      MKPLAYER",
	"MN      MKROOM",
	"MN      MKOBJECT",
	"I       PCNAME",
	"MMMMIII MFORMAT",
	"INN     NEXT",
	"I       TRASH",
	"INM     SETDESC",
	"IN      SETMAXSTATE",
	"IM      SETLONG",
	"INM     SETIOH",
	"IM      SETNAME",
	"INN     SETVOCAB",
	"MN      LENGTH",
	"MNN     ADDWORD",
	"MNN     FINDWORD",
	"MN      DELWORD",
	"NNNNNNN TOD",
	"MNN     FLOAD",
	"MNN     FSAVE",
	"M       DELFILE",
	"MMNN    FINDSTR",
	"MMNN    SKIPSTR",
	"IM      LOADU",
	"IM      SAVEU",
	"N       DO",
	"N       LOOP",
	"MM      SUBSTR",
	"MMNN    EXTRACT",
	"NM      NUMT",
	"NNM     WTEXT",
	"MNN     FTRUENAME",
	"MMMN    NEWMSG",
	"N       DELMSG",
	"MM      FOPEN",
	"M       FWRITE",
	"M       FREAD",
	"        FCLOSE",
	"MM      FMOVE",
	"INNN    CONSULT",
	"NINN    FOR",
	"N       ENDFOR",
	"NN      FORUSER",
	"N       ENDUSER",
	"IN      ATTACHTABLE",
	NULL
};        

/*
 *	Jump vector to execute commands
 */

ExecCmd(n)
int n;
{
	extern int SeekFor;
	int v=1;
/*
 *	Skip over commands while in skip and seek;
 */
	if((SeekFor==1&&n!=118)||(SeekFor==2&&n!=120))
		return(1);
	SeekFor= 0;
	switch(n)
	{
		case 0:C_Done();break;
		case 1:C_Ok();break;
		case 2:C_Msg();break;
		case 3:v=Q_Get();break;
		case 4:C_Drop();break;
		case 5:C_Place();break;
		case 6:C_Call();break;
		case 7:C_Ld();break;
		case 8:C_LdF();break;
		case 9:C_Print();break;
		case 10:C_LdO();break;
		case 11:C_SetO();break;
		case 12:C_LdR();break;
		case 13:C_SetR();break;
		case 14:C_LdP();break;
		case 15:C_SetP();break;
		case 16:C_Mul();break;
		case 17:C_Div();break;
		case 18:C_Add();break;
		case 19:C_Sub();break;
		case 20:C_Set();break;
		case 21:C_Res();break;
		case 22:C_Inc();break;
		case 23:C_Dec();break;
		case 24:C_SetState();break;
		case 25:C_DoAct();break;
		case 26:C_DoTo();break;
		case 27:C_Score();break;
		case 28:C_Look();break;
		case 29:C_DoLobj();break;
		case 30:C_Lobjs();break;
		case 31:C_PName();break;
		case 32:C_PDesc();break;
		case 33:C_PLong();break;
		case 34:C_JP();break;
		case 35:C_CR();break;
		case 36:C_MsgCR();break;
		case 37:v=Q_Z();break;
		case 38:v=Q_Eq();break;
		case 39:v=Q_Gt();break;
		case 40:v=Q_Lt();break;
		case 41:v=Q_At();break;
		case 42:v=Q_Bit();break;
		case 43:v=Q_IsAt();break;
		case 44:v=Q_Here();break;
		case 45:v=Q_Pres();break;
		case 46:v=Q_Dark();break;
		case 47:v=Q_State();break;
		case 48:v=Q_Chance();break;
		case 49:v=Q_Is();break;
		case 50:v=Q_OBit();break;
		case 51:v=Q_RBit();break;
		case 52:v=Q_PBit();break;
		case 53:v=Q_CanSee();break;
		case 54:v=Q_Level();break;
		case 55:v=Q_Arch();break;
		case 56:v=Q_Called();break;
		case 57:v=Q_Weighs();break;
		case 58:v=Q_CanPut();break;
		case 59:v=Q_Got();break;
		case 60:C_Exit();break;
		case 61:C_Save();break;
		case 62:v=Q_NumOf();break;
		case 63:v=Q_Neil();break;
		case 64:C_GetWords();break;
		case 65:v=Q_NextMatch();break;
		case 66:v=Q_NextGMatch();break;
		case 67:C_Parse();break;
		case 68:C_TabTo();break;
		case 69:C_Prompt();break;
		case 70:C_AutoVerb();break;
		case 71:C_Append();break;
		case 72:v=Q_Type();break;
		case 73:C_Random();break;
		case 74:
			exit(0);
		case 75:C_PwChange();break;
		case 76:C_PwForce();break;
		case 77:C_MkPlayer();break;
		case 78:C_MkRoom();break;
		case 79:C_MkObject();break;
		case 80:C_PCName();break;
		case 81:C_MFormat();break;
		case 82:v=Q_Next();break;
		case 83:C_Trash();break;
		case 84:C_SetDesc();break;
		case 85:C_SetMaxState();break;
		case 86:C_SetLong();break;
		case 87:C_SetIOH();break;
		case 88:C_SetName();break;
		case 89:C_SetVocab();break;
		case 90:C_Length();break;
		case 91:C_AddWord();break;
		case 92:C_FindWord();break;
		case 93:C_DelWord();break;
		case 94:C_Tod();break;
		case 95:C_Fload();break;
		case 96:C_FSave();break;
		case 97:v=Q_DelFile();break;
		case 98:v=Q_FindStr();break;
		case 99:v=Q_SkipStr();break;
		case 100:v=Q_LoadU();break;
		case 101:v=Q_SaveU();break;
		case 102:C_Do();break;
		case 103:C_Loop();break;
		case 104:v=Q_SubStr();break;
		case 105:C_Extract();break;
		case 106:C_NumText();break;
		case 107:C_WText();break;
		case 108:C_FTrueName();break;
		case 109:C_NewMsg();break;
		case 110:C_DelMsg();break;
		case 111:v=Q_FOpen();break;
		case 112:v=Q_FWrite();break;
		case 113:v=Q_FRead();break;
		case 114:C_FClose();break;
		case 115:v=Q_Fmove();break;
		case 116:C_Consult();break;
		case 117:C_For();break;
		case 118:C_EndFor();break;
		case 119:C_ForUser();break;
		case 120:C_EndUser();break;
		case 121:C_AttachTable();break;
		default:log_error("[SYSTEM ERROR]: Unknown CMD\n",NULL);panic();
	}
	return(v);
}

/*
 *	String compare: a is uppercase b is mixed case
 */

strUcmp(a,b)
char *a,*b;
{
	while(*a)
	{
		if(islower(*b))
			*b=toupper(*b);
		if(*a!=*b)
			return(1);
		a++;
		b++;
	}
	return(*b==0?0:1);
}

/*
 *	Look up an action entry in the system table
 */

FindAction(x)
char *x;
{
	int ct=0;
	while(CmTab[ct])
	{
		if(strUcmp(CmTab[ct]+8,x)==0)
			return(ct);
		ct++;
	}
	return(-1);
}

/*
 *	Compilation Code Begins Here
 */

extern char *GetToken(),*Get_Block(),*estralloc();
extern char **Messages;
extern struct Word *FindName(),*FindWord();
int li_pt;
tag Li_Array[256];	/* Line building buffer */
tag *LinePtr[2048];	/* This should be variable sized */
int tb_pt;		/* Working table count */
int tb_num;
extern TABLE *Table;	/* List of system tables */
int Num_Tables;		/* Count of tables in system */
int UStk=0;		/* USed to stack FOR and FORUSER nests correctly */
int ForStk=0;

/*
 *	blank table buffer
 */
 
void Wipe_Table()
{
	bzero((char *)LinePtr,2048*sizeof(tag *));
}

/*
 *	Copy table buffer into table entry
 */
 
void Load_Table()
{
	if(tb_num<0)
	{
		ItemArray[-(VAL(tb_num))]->o_header.ih_Control=
			((tag **)emalloc(sizeof(tag *)*(tb_pt+1)));
		bcopy((char *)LinePtr,(char *)
			(ItemArray[-VAL(tb_num)]->o_header.ih_Control),
			sizeof(tag *)*(tb_pt+1));
	}
	else
	{
		Table[tb_num]=(tag **)emalloc(sizeof(tag *)*(tb_pt+1));
		bcopy((char *)LinePtr,(char *)(Table[tb_num]),sizeof(tag *)*(tb_pt+1));
	}
}

/*
 *	Start compiling this table
 */
 
void Init_Table(t)
int t;
{
	tb_num=t;
}

/*
 *	Set line to compile next
 */
 
void Set_Line(l)
int l;
{
	if(l>2047)
	{
		ELine();
		log_error("[ERROR]: Table too complex. I give up!\n",NULL);
		panic();
	}
	tb_pt=l;
}

/*
 *	Prepeare to compile a line
 */
 
void Init_Line()
{
	li_pt=0;
}

/*
 *	Write a line across into the table
 */
 
void Write_Line()
{
	Compile_Word(-1);
	LinePtr[tb_pt]=(tag *)emalloc(sizeof(tag)*li_pt);
	bcopy((char *)Li_Array,(char *)(LinePtr[tb_pt]),sizeof(tag)*li_pt);
}

/*
 *	Compile in a reference to an item
 */
 
char *CompileItem(x,f)
char *x;
tag f;
{
	char buf[256];
	struct Word *wd;
	char *p=x;
	x=GetToken(x,buf);
	if(x==NULL)
	{
		ELine();
		log_error("[ERROR]: Item expected.\n",NULL);
		return(NULL);
	}
	if(isdigit(*buf)||*buf=='?'||*buf=='!'||*buf=='#'||*buf=='-')
	{
		return(CompileNumber(p));
	}
	wd=FindName(buf,1);
	if(wd==NULL)
	{
		ELine();
		log_error("[ERROR]: Unknown item '%s'.\n",buf);
		return(NULL);
	}
	Queue_LockItem(f+wd->wd_Code);	/* Mark as table referenced (system item) */
	Compile_Word(ISOBJ(f+VAL(wd->wd_Code)));
	return(x);
}
		
char *CompileMessage(x,f)
char *x;
tag f;
{
	char buf[256];
	struct Word *wd;
	char *p=x;
	x=GetToken(x,buf);
	if(x==NULL)
	{
		ELine();
		log_error("[ERROR]: Message expected.\n",NULL);
		return(NULL);
	}
	if(isdigit(*buf)||*buf=='?'||*buf=='!'||*buf=='#')
	{
		return(CompileNumber(p));
	}
	if(strcmp(buf,"{")==0)
	{
		x=Get_Block(x,buf);
		if(x)
		{
			int v;
			v=Next_Msg();
			Messages[v]=estralloc(buf);
			Queue_LockMessage(v);
			Compile_Word(ISMSG(f+v));
			return(x);
		}		
		else
		{
			ELine();
			log_error("[ERROR]: Imbedded message non-terminated {%s}.\n",buf);
			return(NULL);
		}
	}	
	wd=FindName(buf,2);
	if(wd==NULL)
	{
		ELine();
		log_error("[ERROR]: Unknown Message '%s'.\n",buf);
		return(NULL);
	}
	Queue_LockMessage(f+wd->wd_Code);
	Compile_Word(ISMSG(f+wd->wd_Code));
	return(x);
}
		

char *CompileNumber(p)
char *p;
{
	char bufr[256];
	char *buf=bufr;
	int shf=0;
	int v;
	char *pp=p;
	if((p=GetToken(p,buf))==NULL)
	{
		ELine();
		log_error("[ERROR]: Number expected.\n",NULL);
		return(NULL);
	}
	if(*buf=='!')
	{
		shf=30000;
		buf++;
	}
	if(*buf=='?')
	{
		shf=31000;
		buf++;
	}
	if(*buf=='@')
	{
		return(CompileItem(pp+2,shf));
	}
	if(*buf=='&')
	{
		return(CompileMessage(pp+2,0));
	}
	if(*buf=='%')
	{
		v=FindFlagListEntry(buf+1);
		if(v==-1)
		{
			ELine();
			log_error("[ERROR]: Invalid bit name %s'.\n",buf);
			return(NULL);
		}
		Compile_Word(ISNUM(v+shf));
		return(p);
	}
	if(*buf=='#')
	{
		v=ResolveConstant(buf+1);
		Compile_Word(ISNUM(v+shf));
	}
	if(*buf=='\'')
	{
		v=ResolveWord(buf+1);
		Compile_Word(ISNUM(v+shf));
	}
	else
	{
		v=ResolveConstant(buf);
		Compile_Word(ISNUM(v+shf));
	}
	return(p);
}

void Compile_Word(x)
tag x;
{
	if(li_pt>255)
	{
		ELine();
		log_error("[ERROR]: Line too complex, I give up.\n",NULL);
		exit(0);
	}
	Li_Array[li_pt++]=x;
}

char *CompileAction(x)
char *x;
{
	char buf[256];
	char *p=GetToken(x,buf);
	char *lp;
	int ac;
	if(!p)
	{
		ELine();
		log_error("[ERROR]: Extra spaces on line.\n",NULL);
		return(NULL);
	}
	if(*buf=='-')
	{
		Compile_Word(255);
		ac=FindAction(buf+1);
	}
	else
		ac=FindAction(buf);
	if(ac==-1)
	{
		ELine();
		log_error("[ERROR]: No such action '%s'.\n",buf);
		return(NULL);
	}
	Compile_Word(ac);
	lp=CmTab[ac];
	while(*lp!=' ')
	{
		switch(*lp)
		{
			case 'R':p=CompileRFlag(p);break;
			case 'O':p=CompileOFlag(p);break;
			case 'P':p=CompilePFlag(p);break;
			case 'N':p=CompileNumber(p);break;
			case 'I':p=CompileItem(p,0);break;
			case 'M':p=CompileMessage(p,0);break;
			case '1':Compile_Word(ForStk++);break;
			case '2':Compile_Word(--ForStk);break;
			case '3':Compile_Word(UStk++);break;
			case '4':Compile_Word(--UStk);break;
			default:ELine();
				log_error("[PANIC]: Invalid CmTab Entry.\n",NULL);
				panic();
		}
		lp++;
	}
	return(p);
}



CompileLine(x)
char *x;
{
	char bf[256];
	struct Word *wp;
	static int lv= -1,ln1= -1,ln2= -1;
	Init_Line();
	Init_LockList();
	ForStk=0;
	UStk=0;
	if(*x=='\t')
	{
		Compile_Word(lv);
		Compile_Word(ln1);
		Compile_Word(ln2);
		x++;
	}
	else
	{
		if((x=GetToken(x,bf))==NULL)
		{
			ELine();
			log_error("[ERROR]: No verb on line.\n",NULL);
			return(1);
		}
		if(strcmp(bf,"$ANY")==0)
		{
			lv= -1;
			Compile_Word(-1);
		}
		else
		{
			if(strcmp(bf,"$NONE")==0)
			{
				lv= -2;
				Compile_Word(-2);
			}
			else
			{
				wp=FindWord(bf,1);
				if(!wp)
				{
					ELine();
					log_error("[ERROR]: Unknown verb '%s'.\n",bf);
					return(1);
				}
				Queue_LockWord(wp->wd_Code);
				lv=wp->wd_Code;
				Compile_Word(wp->wd_Code);
			}
		}
		if((x=GetToken(x,bf))==NULL)
		{
			ELine();
			log_error("[ERROR]: No noun on line.\n",NULL);
			return(1);
		}
		if(strcmp(bf,"$ANY")==0)
		{
			ln1= -1;
			Compile_Word(-1);
		}
		else
		{
			wp=FindWord(bf,3);
			if(!wp)
			{
				ELine();
				log_error("[ERROR]: Unknown noun '%s'.\n",bf);
				return(1);
			}
			Queue_LockWord(wp->wd_Code);
			ln1=wp->wd_Code;
			Compile_Word(wp->wd_Code);
		}
		if((x=GetToken(x,bf))==NULL)
		{
			ELine();
			log_error("[ERROR]: No second noun on line.\n",NULL);
			return(1);
		}
		if(strcmp(bf,"$ANY")==0)
		{
			ln2= -1;
			Compile_Word(-1);
		}
		else
		{
			wp=FindWord(bf,3);
			if(!wp)
			{
				ELine();
				log_error("[ERROR]: Unknown noun '%s'.\n",bf);
				return(1);
			}
			Compile_Word(wp->wd_Code);
			ln2=wp->wd_Code;
			Queue_LockWord(wp->wd_Code);
		}
	}
	while(*x)
	{
		x=CompileAction(x);
		if(!x)
		{
			return(1);
		}
	}
	Do_LockList();
	Write_Line();
	if(ForStk!=0)
		log_error("[WARNING]: Unbalanced 'FOR' constructs.\n",NULL);
	if(UStk!=0)
		log_error("[WARNING]: Unbalanced 'FORUSER' constructs.\n",NULL);
	return(0);
}

/*
 *	File reading functions
 */
 
extern char LineBuf[];
extern char *GetLine();

/*
 *	Compile up an entire table from the boot file
 */
 
Compile_Table(f,d,t)
FILE *f;
int d;
int t;
{
	int lct=0;
	Init_Table(t);
	while(1)
	{
		if(GetLine(f)==NULL)
		{
			ELine();
			log_error("[ERROR]: Unexpected EOF.\n",NULL);
			return(1);
		}
		if(*LineBuf=='@')
			break;
		if(d)printf("%d:%d> %s\n",t,lct,LineBuf);
		Set_Line(lct++);
		if(CompileLine(LineBuf))
			return(1);
	}
	Set_Line(lct++);
	if(CompileLine("$ANY $ANY $ANY DONE"))
	{
		log_error("[PANIC]: Automatic tail insertion failed.\n",NULL);
		panic();
	}
	Load_Table();
	Wipe_Table();
	return(0);
}

/*
 *	Load and process table header
 */
 
LoadTables(f,d)
FILE *f;
int d;
{
	char *p=GetLine(f);
	if(p==NULL)
	{
		ELine();
		log_error("[ERROR]: Unexpected EOF.\n",NULL);
		return(1);	
	}
	if(sscanf(LineBuf,"%d",&Num_Tables)==0)
	{
		ELine();
		log_error("[ERROR]: Invalid Number Of Tables.\n",NULL);
		return(1);
	}
	if(Num_Tables<7)
	{
		ELine();
		log_error("[ERROR]: Invalid Number Of Tables.\n",NULL);
		return(1);
	}
	Table=(TABLE *)emalloc(Num_Tables*sizeof(TABLE));
	return(0);
}

/*
 *	Compile all of the tables from the boot data
 */
 
LoadEachTable(f,d)
FILE *f;
int d;
{
	int er=0;
	int ct=0;
	while(!er&&ct<Num_Tables)
	{	
		if(d)
			printf("Compiling Table %d.\n",ct);
		er=Compile_Table(f,d,ct);
		ct++;
	}
	return(er);
}

LoadItemTables(f,d)
FILE *f;
int d;
{
	struct Word *w;
	char namebuf[33];
	int er=0;
	ITEM i;
	while(!er)
	{
		if(GetLine(f)==NULL)
		{
			ELine();
			log_error("[ERROR]: Unexected EOF.\n",NULL);
			return(er);
		}
		if(*LineBuf=='@')
			return(er);
		if(GetName(LineBuf,namebuf)==NULL)
		{
			ELine();
			log_error("[ERROR]: Invalid Name '%s'.\n",LineBuf);
			return(1);
		}
		w=FindName(namebuf,0);
		if(!w)
		{
			ELine();
			log_error("Unknown item '%s'.\n",namebuf);
			return(1);
		}
		i=ISOBJ(w->wd_Code);
		if((OBJECT(i)->o_header.ih_Control)!=NULL)
		{
			ELine();
			log_error("Multiple Table Declarations for '%s'\n",namebuf);
			return(1);
		}
		if(d)
			printf("Compiling Item Table %s.\n",OBJECT(i)->ob_Name);
		er=Compile_Table(f,d,-i);
	}
	return(er);
}