/*
 * dmalloc.c
 * (C) Christian Magnusson 1999, Email: d1mag@dtek.chalmers.se
 * This program is copyright, and covered by the Gnu Public License.
 * The Q bot.
 */
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include "hash.h"
#include "dmalloc.h"
#include "bottypes.h"

#ifdef DMALLOC

extern void log_malloc(const char *format, ...);

HashTable *mem_block = NULL;
int tot_malloc;

List *NewList_malloc(int type)
{
	/* initialize a new list */
	List *list = (List *) malloc(sizeof(List));
	if (list) {
		list->head = NULL;
		list->tail = NULL;
		list->type = type;
	}
	return list;
}

HashTable *NewHashTable_malloc(int sz, int type)
{
	/* initialize a new hashtable */
	int i;
	/* List *(* list)[]; */
	HashTable *hash = malloc(sizeof(HashTable));
	if (hash) {
		hash->sz = sz;
		hash->hash_type = H_MEMBLOCK;
		hash->list = (List * (*)[])malloc(sz * sizeof(List *));
		for (i = 0; i < sz; i++) {
			(*hash->list)[i] = (List *) NewList_malloc(H_MEMBLOCK);
		}
	}
	return hash;
}

ListElem *NewElem_malloc(void *val)
{
	ListElem *p = (ListElem *) malloc(sizeof(ListElem));
	if (p) {
		p->prev = NULL;
		p->next = NULL;
		p->list = NULL;
		p->data = val;
	} else {
#if DEBUG
		printf("*** NewElem malloc error\n");
#endif
	}
	return p;
}

void FreeElem_malloc(ListElem * el)
{
	if (!el) {
#if DEBUG
		printf("*** FreeElem_malloc: NULL pointer ???\n");
		log_malloc("*** FreeElem_malloc: NULL pointer ???\n");
#endif
		return;
	}
	if (el->next) {
#if DEBUG
		log_malloc("FreeElem_malloc: el->next!=NULL\n");
		printf("FreeElem_malloc: el->next!=NULL\n");
#endif
	}
	if (el->prev) {
#if DEBUG
		log_malloc("FreeElem_malloc: el->prev!=NULL\n");
		printf("FreeElem_malloc: el->prev!=NULL\n");
#endif
	}
	if (el->data) {
#ifdef DMALLOC_SAVEMEM
		free((((mb *) el->data)->ptr)->func);
		(((mb *) el->data)->ptr)->func = NULL;
#endif
		free(((mb *) el->data)->ptr);
		((mb *) el->data)->ptr = NULL;

		free(el->data);
		el->data = NULL;
	} else {
#if DEBUG
		log_malloc("FreeElem_malloc: el->data==NULL\n");
		printf("FreeElem_malloc: el->data==NULL\n");
#endif
	}
	el->list = NULL;
	free(el);
	return;
}

void FreeList_malloc(List * list)
{
	ListElem *el, *el2;
	/* Free up a list and all the nodes in it */
	if (!list) {
#if DEBUG
		log_malloc("FreeList_malloc: list==NULL\n");
		printf("FreeList_malloc: list==NULL\n");
#endif
		return;
	}
	el = list->head;
	while (el) {
		el2 = el->next;
		Remove(el);
#ifdef DMALLOC_SAVEMEM
		free(((mbstat *) el->data)->func);
#endif
		FreeElem_malloc(el);
		el = el2;
	}
	free(list);
	return;
}

void *Malloc2(int sz, char *func, int line)
{
	ListElem *el;
	mb *m;
	if (!mem_block) {
		/* 
		   Allocate the hash table first time we call malloc */
		mem_block = NewHashTable_malloc(500, H_MEMBLOCK);
		tot_malloc = 0;
	}
	m = malloc(sizeof(mb));
	m->ptr = malloc(sz);
	m->sz = sz;
#ifdef DMALLOC_SAVEMEM
	m->func = (char *) malloc(strlen(func) + 1);
#endif
	strcpy(m->func, func);
	m->line = line;

	tot_malloc += sz;

	el = NewElem_malloc(m);
	AddElem(mem_block, el);
	return m->ptr;
}

void Free2(void *ptr)
{
	ListElem *el;
	el = Find(mem_block, ptr, compare_pointer);
	if (el) {
		tot_malloc -= ((mb *) el->data)->sz;
		/* 
		   Unlink the object from the malloc list. After it's done, free the memory. */
		Remove(el);
		if (((mb *) el->data)->ptr != ptr) {
#if DEBUG
			log_malloc("Free2: ERROR ptr=%ld??\n", (long) ((mb *) el->data)->ptr);
			printf("Free2: ERROR ptr=%ld??\n", (long) ((mb *) el->data)->ptr);
#endif
		}
		FreeElem_malloc(el);
	} else {
#if DEBUG
		log_malloc("DMALLOC: Free pointer not found\n");
#endif
	}
	return;
}


struct StructEntry
{
	char *name;
	unsigned int size;
};

struct StructEntry strtab[] = {
	{"List", sizeof(List)}
	,
	{"ListElem", sizeof(ListElem)}
	,
	{"HashTable", sizeof(HashTable)}
	,
	{"struct dbaseuser", sizeof(struct dbaseuser)},
	{"struct ban", sizeof(struct ban)},
	{"struct chanpriv", sizeof(struct chanpriv)},
	{"struct chanmod", sizeof(struct chanmod)},
	{"struct chan_stat", sizeof(struct chan_stat)},
	{"struct activeuser", sizeof(struct activeuser)},
	{"struct chanopts", sizeof(struct chanopts)},
	{NULL, 0}
};


/*
  This function will add 10-20 new objects to the total
  allocated objects.
*/
void Malloc_stat(void)
{
	int found, i, nr;
	ListElem *el, *s;
	mbstat *mbs;
	mb *m;
	List *memsz = NewList_malloc(L_MEMSTAT);

	el = GetFirstH(mem_block);
	while (el) {
		found = 0;
		s = GetHead(memsz);
		while (s) {
#if 0
			if ((((mb *) el->data)->sz) == ((mbstat *) s->data)->sz) {
				((mbstat *) s->data)->nr += 1;
#endif
				if ((((mb *) el->data)->sz) == (((mbstat *) (((mb *) s->data)->ptr))->sz)) {
					((mbstat *) (((mb *) s->data)->ptr))->nr += 1;
					found = 1;
					break;
				}
				s = GetSucc(s);
			}
			if (!found) {
				m = malloc(sizeof(mb));
				m->ptr = malloc(sizeof(mbstat));
				m->sz = 0;
#ifdef DMALLOC_SAVEMEM
				m->func = (char *) malloc(strlen(func) + 1);
#endif
				m->func[0] = '\0';
				m->line = 0;

				mbs = m->ptr;
				mbs->sz = ((mb *) el->data)->sz;
				mbs->nr = 1;
#ifdef DMALLOC_SAVEMEM
				mbs->func = (char *) malloc(strlen(((mb *) el->data)->func) + 1);
#endif
				strcpy(mbs->func, ((mb *) el->data)->func);
				s = NewElem_malloc(m);
				AddTail(memsz, s);

#if 0
				mbs = malloc(sizeof(mbstat));
				mbs->sz = ((mb *) el->data)->sz;
				mbs->nr = 1;
#ifdef DMALLOC_SAVEMEM
				mbs->func = (char *) malloc(strlen(((mb *) el->data)->func) + 1);
#endif
				strcpy(mbs->func, ((mb *) el->data)->func);
				s = NewElem_malloc(mbs);
				AddTail(memsz, s);
#endif
			}
			el = GetSuccH(mem_block, el);
		}

		log_malloc("Memblock struct sizes\n");
		i = 0;
		while (strtab[i].name) {
			log_malloc("[%20s]  %6d\n", strtab[i].name, strtab[i].size);
			i++;
		}

		log_malloc("Memblocks allocated Total=%d\n", tot_malloc);
		log_malloc("Bytes   Nr.\n");
		s = GetHead(memsz);

		while (s) {
#if 1
			log_malloc("%6d %5d %s\n", ((mbstat *) (((mb *) s->data)->ptr))->sz,
				((mbstat *) (((mb *) s->data)->ptr))->nr, ((mbstat *) (((mb *) s->data)->ptr))->func);
#else
			log_malloc("%6d %5d %s\n", ((mbstat *) s->data)->sz, ((mbstat *) s->data)->nr,
				((mbstat *) s->data)->func);
#endif
			s = GetSucc(s);
		}

		s = GetFirstH(auser_yxx);
		nr = 0;
		while (s) {
			nr++;
			s = GetSuccH(auser_yxx, s);
		}
		log_malloc("%7d objects in ausers_yxx\n", nr);
		s = GetFirstH(auser_nick);
		nr = 0;
		while (s) {
			nr++;
			s = GetSuccH(auser_nick, s);
		}
		log_malloc("%7d objects in auser_nick\n", nr);
		s = GetFirstH(channels);
		nr = 0;
		while (s) {
			nr++;
			s = GetSuccH(channels, s);
		}
		log_malloc("%7d objects in channels\n", nr);
		s = GetFirstH(dbusers_nick);
		nr = 0;
		while (s) {
			nr++;
			s = GetSuccH(dbusers_nick, s);
		}
		log_malloc("%7d objects in dbusers_nick\n", nr);
		s = GetFirstH(dbusers_lastyxx);
		nr = 0;
		while (s) {
			nr++;
			s = GetSuccH(dbusers_lastyxx, s);
		}
		log_malloc("%7d objects in dbusers_lastyxx\n", nr);
		s = GetFirstH(dbusers_userid);
		nr = 0;
		while (s) {
			nr++;
			s = GetSuccH(dbusers_userid, s);
		}
		log_malloc("%7d objects in dbusers_userid\n", nr);

		s = GetHead(auser_list);
		nr = 0;
		while (s) {
			nr++;
			s = GetSucc(s);
		}
		log_malloc("%7d objects in auser_list\n", nr);
		s = GetHead(channel_list);
		nr = 0;
		while (s) {
			nr++;
			s = GetSucc(s);
		}
		log_malloc("%7d objects in channel_list\n", nr);
		s = GetHead(dbuser_list);
		nr = 0;
		while (s) {
			nr++;
			s = GetSucc(s);
		}
		log_malloc("%7d objects in dbuser_list\n", nr);


		log_malloc("Complete list of all malloc's.\n");
		log_malloc("Bytes   Line   Func\n");
		s = GetFirstH(mem_block);
		while (s) {
			log_malloc("%6d %5d %s\n", ((mb *) s->data)->sz, ((mb *) s->data)->line,
				((mb *) s->data)->func);
			s = GetSuccH(mem_block, s);
		}

		FreeList_malloc(memsz);
		return;
	}

#else

void Malloc_stat(void)
{
	return;
}

#endif
