#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <ctype.h>
#include <sys/time.h>
#include <strings.h>
#include <sys/time.h>
#include <sys/resource.h>

#include "hash.h"

static hash_table *mem_table = NULL;
static hash_link *mem_entry;
struct rusage myusage;

#ifdef WITH_LIB
#include "Mem.h"
#include <assert.h>
extern void sizeToPoolInit();
extern MemPool *sizeToPool(size_t size);
#endif
extern char *malloc_options;
void my_free(char *, int, void *);

FILE *fp;
char *fn;
int initsiz;
int maxsiz;
int minchunk;
HASHCMP ptrcmp;
char mbuf[256];
char abuf[32];
char *p;

int size;
void *addr;
int amt;

int i;
int a;
int run_stats=0;
void *my_xmalloc(size_t);
void *my_xcalloc(int, size_t);
int my_xfree(void *);

#define xmalloc my_xmalloc
#define xcalloc my_xcalloc
#define xfree my_xfree

int *size2id_array[2];
int size2id_len=0;
int size2id_alloc=0;

typedef struct {
	char orig_ptr[32];
	void *my_ptr;
#ifdef WITH_LIB
	MemPool *pool;
#endif
	int size;
} memitem;

struct {
	int mallocs,frees,callocs,reallocs;
} mstat;

memitem *mi;
void size2id(size_t, memitem *);
void badformat();
void init_stats(), print_stats();
void my_hash_insert(hash_table *h, const char *k, memitem *item);
static void *xmemAlloc(memitem *item);
static void xmemFree(memitem *item);

int 
ptrcmp(const void *a,const void *b) 
{
	return (strcmp(a,b));
}

main(int argc,char **argv)
{
    char c;
    extern char *optarg; 
    malloc_options = "A";
    a=0;
    while ((c = getopt(argc, argv, "f:i:M:l:m:r:N")) != -1) {
      switch (c) {
	case 'N':
	   mem_pools_on=0;
	   break;
	case 'r':
	  run_stats=atoi(optarg);
	  break;
	case 'f':
	  fn=strdup(optarg);
	  fp=fopen(fn,"r");
	  break;
	case 'i':
	  initsiz=atoi(optarg);
	  break;
	case 'l':
	  mem_max_size = atoi(optarg)*1024*1024;
	  break;
	case 'M':
	  maxsiz=atoi(optarg);
	  break;
	case 'm':
	  minchunk=atoi(optarg);
	  break;
	default:
  	  fprintf(stderr,
		"Usage: %s -f file -M maxsiz -i initsiz -m minchunk",argv[0]);
	  exit(1);
      }
	
    }
    if (!fp) {
	fprintf(stderr,
		"%s pummels %s\n%s . o O ( You't supply a valid tracefile.)\n",
			argv[0], getenv("USER"), argv[0]);
	exit(1);
    }
#ifdef WITH_LIB
    sizeToPoolInit();
#endif
    mem_table = hash_create(ptrcmp, 229, hash4);         /* small hash table */
    init_stats();
    while (fgets(mbuf, 256, fp)!=NULL) {
	if (run_stats>0 && (++a)%run_stats==0) print_stats();
	p=NULL;
	switch(mbuf[0]) {
	case 'm': /* malloc */
	   p=strtok(&mbuf[2],":");
	   if (!p) badformat();
	   size=atoi(p);
	   p=strtok(NULL,"\n");
	   if (!p) badformat();
	   mi=malloc(sizeof(memitem)); 
	   strcpy(mi->orig_ptr,p);
	   mi->size=size;
	   size2id(size,mi);
	   mi->my_ptr = xmemAlloc(mi); /* (void *)xmalloc(size); */
	   assert(mi->my_ptr);
	   my_hash_insert(mem_table, mi->orig_ptr, mi);
	   mstat.mallocs++;
	   break;
	case 'c': /* calloc */
	   p=strtok(&mbuf[2],":");
	   if (!p) badformat();
	   amt=atoi(p);
	   p=strtok(NULL,":");
	   if (!p) badformat();
	   size=atoi(p);
	   p=strtok(NULL,"\n");
	   if (!p) badformat();
           mi=malloc(sizeof(memitem));
	   strcpy(mi->orig_ptr,p);
	   size2id(size,mi);
           mi->size=amt*size;
           mi->my_ptr= xmemAlloc(mi); /*(void *)xmalloc(amt*size);*/
	   assert(mi->my_ptr);
	   my_hash_insert(mem_table, mi->orig_ptr, mi);
	   mstat.callocs++;
	   break;
	case 'r':
           p=strtok(&mbuf[2],":");
	   if (!p) badformat();
	   strcpy(abuf,p);
           p=strtok(NULL,":");
	   if (!p) badformat();
	   mem_entry=hash_lookup(mem_table, p);
           if (mem_entry==NULL) {
                fprintf(stderr,"invalid realloc (%s)!\n",p);
		break;
           }
	   mi=(memitem *)(mem_entry->item);
	   assert(mi->pool);
	   assert(mi->my_ptr);
	   xmemFree(mi); /* xfree(mi->my_ptr); */
	   size2id(atoi(p),mi);   /* we don't need it here I guess? */
           strcpy(mi->orig_ptr,abuf);
	   p=strtok(NULL,"\n");
	   if (!p) badformat();
	   mi->my_ptr= xmemAlloc(mi); /* (char *)xmalloc(atoi(p)); */
	   assert(mi->my_ptr);
	   mstat.reallocs++;
	   break;
	case 'f':
	   p=strtok(&mbuf[2],"\n");
	   mem_entry=hash_lookup(mem_table, p);		
	   if (mem_entry==NULL) {
		if (p[0]!='0')
		fprintf(stderr,"invalid free (%s) at line %d!\n",p,a);
		break;
	   }
	   mi=(memitem *)(mem_entry->item);
	   assert(mi->pool);
	   assert(mi->my_ptr);
	   xmemFree(mi); /* xfree(mi->my_ptr); */ 
	   hash_unlink(mem_table, mem_entry, 1);
	   free(mi);
	   mstat.frees++;
	   break;
	default:
		fprintf(stderr,"%s pummels %s.bad.format\n", argv[0],fn);
		exit(1);
	}

    }
    fclose(fp);
    print_stats();
}

void *
my_xmalloc(size_t a)
{
	return NULL;
}

void *
my_xcalloc(int a, size_t b)
{
	return NULL;
}

int
my_xfree(void *p)
{
	return 0;
}
void
init_stats()
{

}

void
print_stats()
{
#ifdef WITH_LIB
        memReport(stdout); 
#endif
	getrusage(RUSAGE_SELF, &myusage);
	printf("m/c/f/r=%d/%d/%d/%d\n",mstat.mallocs,mstat.callocs,
					mstat.frees, mstat.reallocs);
#if 0
	printf("types                 : %d\n",size2id_len);
#endif
	printf("user time used        : %d.%d\n", (int)myusage.ru_utime.tv_sec,
						(int)myusage.ru_utime.tv_usec);
	printf("system time used      : %d.%d\n", (int)myusage.ru_stime.tv_sec,
                                                (int)myusage.ru_stime.tv_usec);
	printf("max resident set size : %d\n",(int)myusage.ru_maxrss);
	printf("page faults           : %d\n", (int)myusage.ru_majflt);
}

void
size2id(size_t sz,memitem *mi)
{
#ifdef WITH_LIB
	mi->pool = sizeToPool(sz);
	assert(mi->pool);
#endif
	return;
}

void
badformat()
{
    fprintf(stderr,"pummel.bad.format\n");
    exit(1);
}

/* unused code, saved for parts */
const char *
make_nam(int id, int size)
{
    const char *buf = malloc(30); /* argh */
    sprintf((char *)buf, "pl:%d/%d", id, size);
    return buf;
}

void
my_hash_insert(hash_table *h, const char *k, memitem *item)
{	
	memitem *l;
	assert( item->pool);
	assert( item->my_ptr);	
	hash_insert(h,k,item);
}

static void *
xmemAlloc(memitem *item)
{
    extern MemPool *StringPool;
    assert(item && item->pool);
    if (StringPool == item->pool)
	return memStringAlloc(item->pool, item->size);
    else
	return memAlloc(item->pool);
}

static void
xmemFree(memitem *item)
{
    extern MemPool *StringPool;
    assert(item && item->pool);
    if (StringPool == item->pool)
        return memStringFree(item->pool, item->my_ptr, item->size);
    else
        return memFree(item->pool, item->my_ptr);
}

void my_free(char *file, int line, void *ptr)
{
#if 0
fprintf(stderr,"{%s:%d:%p",file,line,ptr);
#endif
free(ptr);
#if 0 
fprintf(stderr,"}\n");
#endif
}
