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

/*
 *	Allocator.c:	Version 1.11
 *	Author:		Alan Cox
 *	Last Changed:	14/12/90
 *
 *	Purpose:
 *		Provides basic low level functions for memory allocation in
 *	the system, and also maintains and allocates entries in the item
 *	arrays.
 *
 *	Bugs & Limits
 *
 *	Functions Provided:
 *		NObs,emalloc,estralloc,TrashItem,AddObject,AddRoom,AddPlayer,
 *	SetName,SetIOH,SetLong,SetState,SetMaxState,SetDesc.
 *
 *	Functions Used:
 *		None.
 */
 
struct Object **ItemArray;
static tag max_Object=0;
static tag next_Object=0;

/*
 *	Return the number of items in the game
 */
 
tag NObs()
{
	return(next_Object);
}

/*
 *	Allocate a block of memory and zero its contents
 */

char *emalloc(x)
int x;
{
	char *t=(char *)malloc(x);
	if(!t)
	{
		fprintf(stderr,"[SYSTEM ERROR] : emalloc out of memory\n");
		exit(1);
	}
	bzero(t,x);
	return(t);
}

/*
 *	Allocate a copy of a given string.
 *	Currently allocating NULL gives you a null string. This is a hack
 *	until the parser is tidied up properly.
 */
 
char *estralloc(x)
char *x;
{
	char *y;
	if(x==NULL) x="";
	y=emalloc(strlen(x)+1);
	strcpy(y,x);
	return(y);
}


/*
 *	Item number allocation
 */

/*
 *	Private types for maintaining free item stack
 */

typedef struct freestack FSTACK;

struct freestack
{
	FSTACK *Next;
	tag Number;
};

static FSTACK *FreeStack=NULL;

extern void FreeSlot(tag);
extern tag NextSlot(void);

static void FreeSlot(n)
tag n;
{
	FSTACK *f=(FSTACK *)malloc(sizeof(FSTACK));
	if(f==NULL)
	{
		fprintf(stderr,"FreeSlot: Out Of Memory\n");
		exit(0);
	}
	f->Number=n;
	f->Next=FreeStack;
	FreeStack=f;
}

static tag NextSlot()
{
	/*
	 *	See if we have any free item slots stacked. Such item slots
	 *	already exist but are NULL ptrs for items.
	 */	
	if(FreeStack)
	{
		tag i=FreeStack->Number;
		FSTACK *f=FreeStack;
		FreeStack=FreeStack->Next;
		free(f);
		return(i);
	}
	if(next_Object==max_Object)
	{
		if(max_Object)
		{
			max_Object+=100;
			ItemArray=(struct Object **)realloc(ItemArray,max_Object*sizeof(char *));
			if(ItemArray==NULL)
			{
				fprintf(stderr,"PANIC! No memory\n");
				exit(1);
			}
			return(next_Object++);
		}
		else
		{
			max_Object=100;
			ItemArray=(struct Object **)malloc(100*sizeof(char *));
			return(next_Object++);
		}
	}
	return(next_Object++);
}

void TrashItem(i)
tag i;
{
	int ct;
	switch(TYPE(i))
	{
		case FL_PLAYER:free(PLAYER(i)->pl_IOH[0]);
			       free(PLAYER(i)->pl_IOH[1]);
			       free(PLAYER(i)->pl_IOH[2]);
			       break;
		case FL_OBJECT:ct=0;
			       while(ct<MAXSTATE(i))
			       {
					free(OBJECT(i)->ob_Desc[ct]);
					ct++;
			       }
			       break;
		case FL_ROOM:  free(ROOM(i)->rm_Long);
			       break;
		default:fprintf(stderr,"Panic: Item '%s' not valid class\n",NAME(i));
	}
	free(NAME(i));
	free(ItemArray[VAL(i)]);
	ItemArray[VAL(i)]=NULL;
	FreeSlot(VAL(i));
}

tag AddObject()
{
	tag n=NextSlot();
	ItemArray[n]=(struct Object *)emalloc(sizeof(struct Object));
	n=ISOBJ(n);
	LOC(n)=ISOBJ(-1);
	FLAGS(n)=FL_OBJECT;
	OBJECT(n)->ob_Name=estralloc("null");
	OBJECT(n)->ob_Data[2]=0x00;
	OBJECT(n)->ob_Desc=(char **)emalloc(sizeof(char *));
	SETSIZE(n,0);
	SETWEIGHT(n,0);
	CLASS(n)=0;
	return(n);
}

tag AddRoom()
{
	ITEM n=NextSlot();
	ItemArray[n]=(struct Object *)emalloc(sizeof(struct Room));
	n=ISOBJ(n);
	FLAGS(n)=FL_ROOM;
	ROOM(n)->rm_Name=estralloc("null");
	ROOM(n)->rm_Long=estralloc("null");
/*	if(EXIT(n,0)!=0||EXIT(n,1)!=0)
	{
		fprintf(stderr,"Allocation botched up!\n");
		exit(0);
	}*/
	return(n);
}

tag AddPlayer()
{
	tag n=NextSlot();
	ItemArray[n]=(struct Object *)emalloc(sizeof(struct Player));
	n=ISOBJ(n);
/*	if(IFOBJ(n)==0)
		printf("IFOBJ/ISOBJ wrong\n");
*/	PLAYER(n)->pl_Name=estralloc("null");
	FLAGS(n)=FL_PLAYER;
	LOC(n)=ISOBJ(-1);
	PLAYER(n)->pl_IOH[0]=estralloc("is here");
	PLAYER(n)->pl_IOH[1]=estralloc("leaves");
	PLAYER(n)->pl_IOH[2]=estralloc("arrives");
	return(n);
}

void SetName(n,x)
tag n;
char *x;
{
	free(OBJECT(n)->ob_Name);
	OBJECT(n)->ob_Name=estralloc(x);
}

void SetIOH(n,s,x)
tag n;
int s;
char *x;
{
	if(TYPE(n)!=FL_PLAYER)
	{
		log_error("[ERROR]: SetIOH, not a player.\n",NULL);
		panic();
	}
	free(PLAYER(n)->pl_IOH[s]);
	PLAYER(n)->pl_IOH[s]=estralloc(x);
}

void SetLong(n,x)
tag n;
char *x;
{
	if(TYPE(n)!=FL_ROOM)
	{
		log_error("[ERROR]: SetLong, not a room.\n",NULL);
		panic();
	}
	free(ROOM(n)->rm_Long);
	ROOM(n)->rm_Long=estralloc(x);
}

void SetState(n,m)
tag n;
int m;
{
	if(m<0||m>=MAXSTATE(n))
	{
		log_error("[ERROR]: SetState, state out of range\n",NULL);
		panic();
	}
	if(TYPE(n)!=FL_OBJECT)
	{
		log_error("[ERROR]: SetState, not an object.\n",NULL);
		panic();
	}
	OBJECT(n)->ob_Data[2]&=~15;
	OBJECT(n)->ob_Data[2]|=m;
	MoveItem(n,-2);
}

void SetMaxState(n,f)
tag n;
int f;
{
	int l;
	if(f<1||f>16)
	{
		log_error("[ERROR]: MaxState must range from 1-16.\n",NULL);
		panic();
	}
	if(TYPE(n)!=FL_OBJECT)
	{
		log_error("[ERROR]: SetMaxState, not an object.\n",NULL);
		panic();
	}
	l=MAXSTATE(n);
	OBJECT(n)->ob_Desc=(char **)realloc(OBJECT(n)->ob_Desc,
				sizeof(char *)*f);
	while(l<f)
	{
		OBJECT(n)->ob_Desc[l]=estralloc("(unset)");
		l++;
	}
	if(STATE(n)>=f)
		SetState(n,0);
	OBJECT(n)->ob_Data[2]=(f<<4)+STATE(n);
}

void SetDesc(n,m,x)
tag n;
int m;
char *x;
{
	if(TYPE(n)!=FL_OBJECT)
	{
		log_error("[ERROR]: SetDec, not an object.\n",NULL);
		panic();
	}
	if(m<0||m>=MAXSTATE(n))
	{
		log_error("SetDesc: State invalid for %s\n",NAME(n));
		panic();
	}
	free(OBJECT(n)->ob_Desc[m]);
	OBJECT(n)->ob_Desc[m]=estralloc(x);
}