/***************************************************************************
 *  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
 *  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
 *                                                                         *
 *  Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael          *
 *  Chastain, Michael Quan, and Mitchell Tse.                              *
 *                                                                         *
 *  Ground ZERO improvements copyright pending 1994, 1995 by James Hilke   *
 *                                                                         *
 *  In order to use any part of this Merc Diku Mud, you must comply with   *
 *  both the original Diku license in 'license.doc' as well the Merc       *
 *  license in 'license.txt'.  In particular, you may not remove either of *
 *  these copyright notices.                                               *
 *                                                                         *
 *  Much time and thought has gone into this software and you are          *
 *  benefitting.  We hope that you share your changes too.  What goes      *
 *  around, comes around.                                                  *
 ***************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <time.h>
#if defined(macintosh)
#include <types.h>
#else
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#endif

#include "ground0.h"
#include "db.h"
#include "memory.h"

/* globals */
struct allocations_type *perm_alloc_list = NULL, *temp_alloc_list = NULL;
CHAR_DATA *		char_free;
NOTE_DATA *		note_free;
OBJ_DATA *		obj_free;
PC_DATA *		pcdata_free;

OBJ_INDEX_DATA *	obj_index_hash		[MAX_KEY_HASH];
ROOM_DATA *	        room_index_hash		[MAX_KEY_HASH];
char *			string_hash		[MAX_KEY_HASH];

char			string_space[MAX_STRING];
char *			top_string;
char			str_empty	[1];

int			top_area;
int			top_help;
int			top_mob_index;
int			top_obj_index;
int			top_reset;
int			top_room;
int			top_shop;

void *			rgFreeList	[MAX_MEM_LIST];
const int		rgSizeList	[MAX_MEM_LIST]	=
{
    16, 32, 64, 128, 256, 1024, 2048, 4096, 8192, 16384, 32768, 65536,
    131072
};

int			nAllocString;
int			sAllocString;
int			nAllocPerm;
int			sAllocPerm;
int                     game_seed;
int                     boot_seed;

/* prototypes */
void get_string_space args ( () );

void get_string_space ()
{
  top_string	= string_space;
}

/*
 * Free a character.
 */
void free_char( CHAR_DATA *ch )
{
    OBJ_DATA *obj;
    OBJ_DATA *obj_next;
    AFFECT_DATA *paf;
    AFFECT_DATA *paf_next;
    
    for ( obj = ch->carrying; obj != NULL; obj = obj_next )
    {
	obj_next = obj->next_content;
	extract_obj(obj, obj->extract_me);
    }
    
    free_string (ch->names);
    free_string (ch->where_start);
    free_string (ch->short_descript);
    ch->names =  ch->where_start = ch->short_descript = NULL;

    if ( ch->pcdata != NULL )
    {
      PC_DATA *p = ch->pcdata;

      free_string (p->password);
      free_string (p->poofin_msg);
      free_string (p->poofout_msg);
      free_string (p->title_line);
      free_string (p->account);
      p->password = p->poofin_msg = p->poofout_msg = p->title_line =
	p->account = NULL;
      p->next = pcdata_free;
      pcdata_free = p;
    }

    ch->next	     = char_free;
    char_free	     = ch;
    return;
}

void mark_alloc (void *ptr, int size, struct allocations_type **first_alloc,
		 char *filename, int line_num)
{
  struct allocations_type *creator;

  creator = malloc (sizeof (struct allocations_type));
  creator->next = NULL;
  creator->ptr = ptr;
  creator->size = size;
  creator->filename = filename;
  creator->line_num = line_num;
  if (!*first_alloc)
    *first_alloc = creator;
  else
    {
      creator->next = *first_alloc;
      *first_alloc = creator;
    }
}

void mark_free (void *ptr, struct allocations_type **first_alloc, 
		char *filename, int line_num)
{
  struct allocations_type *tracker,*previous;

  previous = *first_alloc;
  for (tracker = *first_alloc; tracker; tracker = tracker->next)
    {
      if (tracker->ptr == ptr)
	break;
      previous = tracker;
    }
  if (!tracker)
    {
      sprintf (log_buf, "BUG: attempt to free memory that is already free in "
	       "%s line %d.", filename, line_num);
      log_string (log_buf);
      exit (STATUS_ERROR);
    }
  else
    {
      if (tracker == *first_alloc)
	*first_alloc = (*first_alloc)->next;
      else
	previous->next = tracker->next;
      free (tracker);
    }
}

struct allocations_type *get_mem_reference (void *ptr, 
				      struct allocations_type **first_alloc)
{
  struct allocations_type *tracker;

  for (tracker = *first_alloc; tracker; tracker = tracker->next)
    if (tracker->ptr == ptr)
      return tracker;
  return NULL;
}

int size_correct (void *ptr, int size, struct allocations_type **first_alloc)
{
  struct allocations_type *tracker;

  if ((tracker = get_mem_reference (ptr, first_alloc)) != NULL)
    if (tracker->size == size)
      return 1;
  return 0;
}

/*
 * Allocate some ordinary memory,
 *   with the expectation of freeing it someday.
 */
void *f_alloc_mem( int sMem, char *filename, int line_num )
{
    void *pMem;
    int iList;

    for ( iList = 0; iList < MAX_MEM_LIST; iList++ )
    {
	if ( sMem <= rgSizeList[iList] )
	    break;
    }

    if ( iList == MAX_MEM_LIST )
    {
	sprintf (log_buf, "Alloc_mem: size %d too large in %s %d.", sMem, 
		 filename, line_num );
	log_string (log_buf);
	exit (STATUS_ERROR);
    }

    if ( rgFreeList[iList] == NULL )
    {
	pMem		  = alloc_perm( rgSizeList[iList] );
    }
    else
    {
	pMem              = rgFreeList[iList];
	rgFreeList[iList] = * ((void **) rgFreeList[iList]);
    }

#if 0
    {
      char *count;
      for (count = pMem; count < pMem + sMem; count++)
	*count = 'Z';
    }
#endif
    mark_alloc (pMem, rgSizeList[iList], &temp_alloc_list, filename, line_num);
    return pMem;
}



/*
 * Free some memory.
 * Recycle it back onto the free list for blocks of that size.
 */
void f_free_mem( void *pMem, int sMem, char *filename, int line_num )
{
    int iList;

    for ( iList = 0; iList < MAX_MEM_LIST; iList++ )
    {
	if ( sMem <= rgSizeList[iList] )
	    break;
    }

    if ( iList == MAX_MEM_LIST )
    {
	bug( "Free_mem: size %d too large.", sMem );
	exit (STATUS_ERROR);
    }

    {
      char *count;
      for (count = pMem; count < pMem + sMem; count++)
	*count = 'X';
    }

    if (size_correct (pMem, rgSizeList[iList], &temp_alloc_list))
      {
	* ((void **) pMem) = rgFreeList[iList];
	rgFreeList[iList]  = pMem;
	mark_free (pMem, &temp_alloc_list, filename, line_num);
      }
    else
      {
	sprintf (log_buf, "Attempt to free memory of incorrect size.\n"
		 "Allocation occurred in %s line %d.\nAttempted freeing in "
		 "%s line %d.",
		 (get_mem_reference(pMem, &temp_alloc_list))->filename, 
		 (get_mem_reference(pMem, &temp_alloc_list))->line_num,
		 filename, line_num);
	log_string (log_buf);
	*((char *) NULL) = 5;
	exit (STATUS_ERROR);
      }

    return;
}



/*
 * Allocate some permanent memory.
 * Permanent memory is never freed,
 *   pointers into it may be copied safely.
 */
void *f_alloc_perm( int sMem, char *filename, int line_num )
{
    static char *pMemPerm;
    static int iMemPerm;
    void *pMem;

    while ( sMem % sizeof(long) != 0 )
	sMem++;
    if ( sMem > MAX_PERM_BLOCK )
    {
	bug( "Alloc_perm: %d too large.", sMem );
	exit (STATUS_ERROR);
    }

    if ( pMemPerm == NULL || iMemPerm + sMem > MAX_PERM_BLOCK )
    {
      log_string ("allocating new block");
/*      if (pMemPerm)
	{
	  int count =0;
	  char c[2];

	  c[1] = 0;
	  log_string ("old block");
	  for (count = 0; count < iMemPerm; count++)
	    {
	      c[0] = pMemPerm[count];
	      log_string (c);
	    }
	}
*/
	iMemPerm = 0;
	if ( ( pMemPerm = calloc( 1, MAX_PERM_BLOCK ) ) == NULL )
	{
	    perror( "Alloc_perm" );
	    exit (STATUS_ERROR);
	}
    }

    pMem        = pMemPerm + iMemPerm;
    iMemPerm   += sMem;
    nAllocPerm += 1;
    sAllocPerm += sMem;
#if 0
    {
      char *count;
      for (count = pMem; count < pMem + sMem; count++)
	*count = 'Z';
    }
#endif
    mark_alloc (pMem, sMem, &perm_alloc_list, filename, line_num);
    return pMem;
}



/*
 * Duplicate a string into dynamic memory.
 * Fread_strings are read-only and shared.
 */
char *f_str_dup( const char *str, char *filename, int line_num)
{
    char *str_new;

    if ( str[0] == '\0' )
	return &str_empty[0];

    if ( str >= string_space && str < top_string )
	return (char *) str;

    str_new = alloc_mem( strlen(str) + 1 );
    strcpy( str_new, str );
    return str_new;
}


/*
 * Free a string.
 * Null is legal here to simplify callers.
 * Read-only shared strings are not touched.
 */
void f_free_string( char *pstr, char *filename, int line_num )
{
    if ( pstr == NULL
    ||   pstr == &str_empty[0]
    || ( pstr >= string_space && pstr < top_string ) )
	return;

    free_mem( pstr, strlen(pstr) + 1 );
    return;
}


void do_memory( CHAR_DATA *ch, char *argument )
{
    char buf[MAX_STRING_LENGTH];

/*
    sprintf( buf, "Areas   %5d\n\r", top_area      ); send_to_char( buf, ch );
    sprintf( buf, "Helps   %5d\n\r", top_help      ); send_to_char( buf, ch );
    sprintf( buf, "Socials %5d\n\r", social_count  ); send_to_char( buf, ch );
    sprintf( buf, "Mobs    %5d(%d new format)\n\r", top_mob_index,newmobs ); 
    send_to_char( buf, ch );
    sprintf( buf, "(in use)%5d\n\r", mobile_count  ); send_to_char( buf, ch );
    sprintf( buf, "Objs    %5d(%d new format)\n\r", top_obj_index,newobjs ); 
    send_to_char( buf, ch );
    sprintf( buf, "Resets  %5d\n\r", top_reset     ); send_to_char( buf, ch );
    sprintf( buf, "Rooms   %5d\n\r", top_room      ); send_to_char( buf, ch );
    sprintf( buf, "Shops   %5d\n\r", top_shop      ); send_to_char( buf, ch );

    sprintf( buf, "Strings %5d strings of %7d bytes (max %d).\n\r",
	nAllocString, sAllocString, MAX_STRING );
    send_to_char( buf, ch );

    sprintf( buf, "Perms   %5d blocks  of %7d bytes.\n\r",
	nAllocPerm, sAllocPerm );
    send_to_char( buf, ch );
*/

    sprintf( buf, "Helps   %5d\n\r", top_help      ); send_to_char( buf, ch );
    sprintf( buf, "Socials %5d\n\r", social_count  ); send_to_char( buf, ch );
    sprintf( buf, "Objs    %5d\n\r", top_obj_index); 
    send_to_char( buf, ch );
    sprintf( buf, "String space is %f%% filled.\n\r",
	(((float) (top_string - string_space)) / (float) MAX_STRING) * 100);
    send_to_char( buf, ch );

    return;
}

void do_dump( CHAR_DATA *ch, char *argument )
{
    int count,count2,num_pcs,aff_count;
    CHAR_DATA *fch;
    PC_DATA *pc;
    OBJ_DATA *obj;
    OBJ_INDEX_DATA *pObjIndex;
    ROOM_DATA *room;
    DESCRIPTOR_DATA *d;
    AFFECT_DATA *af;
    FILE *fp;
    int vnum,nMatch = 0;
    char *printer;

    /* open file */
    fclose(fpReserve);
    fp = fopen("mem.dmp","w");

    /* report use of data structures */
    
    num_pcs = 0;
    aff_count = 0;

    /* pcdata */
    count = 0;
    for (pc = pcdata_free; pc != NULL; pc = pc->next)
	count++; 

    fprintf(fp,"Pcdata	%4d (%8d bytes), %2d free (%d bytes)\n",
	num_pcs, num_pcs * (sizeof(*pc)), count, count * (sizeof(*pc)));

    /* descriptors */
    count = 0; count2 = 0;
    for (d = descriptor_list; d != NULL; d = d->next)
	count++;
    for (d= descriptor_free; d != NULL; d = d->next)
	count2++;

    fprintf(fp, "Descs	%4d (%8d bytes), %2d free (%d bytes)\n",
	count, count * (sizeof(*d)), count2, count2 * (sizeof(*d)));

    /* object prototypes */
    for ( vnum = 0; vnum < top_obj_index; vnum++ )
        if ( ( pObjIndex = get_obj_index( vnum ) ) != NULL )
        {
            nMatch++;
        }

    fprintf(fp,"ObjProt	%4d (%8d bytes)\n",
	top_obj_index, top_obj_index * (sizeof(*pObjIndex)));


    /* objects */
    count = 0;  count2 = 0;
    for (obj = object_list; obj != NULL; obj = obj->next)
    {
	count++;
    }
    for (obj = obj_free; obj != NULL; obj = obj->next)
	count2++;

    fprintf(fp,"Objs	%4d (%8d bytes), %2d free (%d bytes)\n",
	count, count * (sizeof(*obj)), count2, count2 * (sizeof(*obj)));
    fprintf (fp, "\n\n\nAll strings:\n\n");
    for (printer = string_space; printer < top_string; printer++)
      fprintf (fp, "%c", printer[0]);

    fclose(fp);


    /* start printing out object data */
    fp = fopen("obj.dmp","w");

    fprintf(fp,"\nObject Analysis\n");
    fprintf(fp,  "---------------\n");
    nMatch = 0;
    for (vnum = 0; vnum < top_obj_index; vnum++)
	if ((pObjIndex = get_obj_index(vnum)) != NULL)
	{
	    nMatch++;
	    fprintf(fp,"#%-4d %3d active      %s\n",
		pObjIndex->vnum,pObjIndex->count,
		pObjIndex->short_descr);
	}

    /* close file */
    fclose(fp);
    fpReserve = fopen( NULL_FILE, "r" );
}