#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "sc.h"
#include "sctracker.h"

memuse_list_t *heapusage = NULL;
memuse_list_t *stackusage = NULL;
funcenum_t *firstenum = NULL;
funcenum_t *lastenum = NULL;
pstruct_t *firststruct = NULL;
pstruct_t *laststruct = NULL;

structarg_t *pstructs_getarg(pstruct_t *pstruct, const char *member)
{
	int i;

	for (i=0; i<pstruct->argcount; i++)
	{
		if (strcmp(pstruct->args[i]->name, member) == 0)
		{
			return pstruct->args[i];
		}
	}

	return NULL;
}

pstruct_t *pstructs_add(const char *name)
{
	pstruct_t *p = (pstruct_t *)malloc(sizeof(pstruct_t));
	
	memset(p, 0, sizeof(pstruct_t));
	strcpy(p->name, name);
	
	if (!firststruct)
	{
		firststruct = p;
		laststruct = p;
	} else {
		laststruct->next = p;
		laststruct = p;
	}

	return p;
}

void pstructs_free()
{
	pstruct_t *p, *next;

	p = firststruct;
	while (p)
	{
		while (p->argcount--)
		{
			free(p->args[p->argcount]);
		}
		free(p->args);
		next = p->next;
		free(p);
		p = next;
	}
	firststruct = NULL;
	laststruct = NULL;
}

pstruct_t *pstructs_find(const char *name)
{
	pstruct_t *p = firststruct;

	while (p)
	{
		if (strcmp(p->name, name) == 0)
		{
			return p;
		}
		p = p->next;
	}

	return NULL;
}

structarg_t *pstructs_addarg(pstruct_t *pstruct, const structarg_t *arg)
{
	structarg_t *newarg;
	int i;

	for (i=0; i<pstruct->argcount; i++)
	{
		if (strcmp(pstruct->args[i]->name, arg->name) == 0)
		{
			/* Don't allow dup names */
			return NULL;
		}
	}
	
	newarg = (structarg_t *)malloc(sizeof(structarg_t));

	memcpy(newarg, arg, sizeof(structarg_t));

	if (pstruct->argcount == 0)
	{
		pstruct->args = (structarg_t **)malloc(sizeof(structarg_t *) * 1);
	} else {
		pstruct->args = (structarg_t **)realloc(
							pstruct->args,
							sizeof(structarg_t *) * (pstruct->argcount + 1));
	}

	newarg->offs = pstruct->argcount * sizeof(cell);
	newarg->index = pstruct->argcount;
	pstruct->args[pstruct->argcount++] = newarg;

	return newarg;
}

void funcenums_free()
{
	funcenum_t *e, *next;

	e = firstenum;
	while (e)
	{
		functag_t *tag, *nexttag;
		tag = e->first;
		while (tag)
		{
			nexttag = tag->next;
			free(tag);
			tag = nexttag;
		}
		next = e->next;
		free(e);
		e = next;
	}

	firstenum = NULL;
	lastenum = NULL;
}

funcenum_t *funcenums_find_byval(int value)
{
	funcenum_t *e = firstenum;

	while (e)
	{
		if (e->value == value)
		{
			return e;
		}
		e = e->next;
	}

	return NULL;
}

funcenum_t *funcenums_add(const char *name)
{
	funcenum_t *e = (funcenum_t *)malloc(sizeof(funcenum_t));

	memset(e, 0, sizeof(funcenum_t));

	if (firstenum == NULL)
	{
		firstenum = e;
		lastenum = e;
	} else {
		lastenum->next = e;
		lastenum = e;
	}

	strcpy(e->name, name);
	e->value = pc_addfunctag((char *)name);

	return e;
}

functag_t *functags_add(funcenum_t *en, functag_t *src)
{
	functag_t *t = (functag_t *)malloc(sizeof(functag_t));
	
	memcpy(t, src, sizeof(functag_t));

	t->next = NULL;

	if (en->first == NULL)
	{
		en->first = t;
		en->last = t;
	} else {
		en->last->next = t;
		en->last = t;
	}

	return t;
}

/**
 * Creates a new mem usage tracker entry
 */
void _push_memlist(memuse_list_t **head)
{
  memuse_list_t *newlist = (memuse_list_t *)malloc(sizeof(memuse_list_t));
  if (*head != NULL)
  {
    newlist->list_id = (*head)->list_id + 1;
  } else {
    newlist->list_id = 0;
  }
  newlist->prev = *head;
  newlist->head = NULL;
  *head = newlist;
}

/**
 * Pops a heap list but does not free it.
 */
memuse_list_t *_pop_save_memlist(memuse_list_t **head)
{
  memuse_list_t *oldlist = *head;
  *head = (*head)->prev;
  return oldlist;
}

/**
 * Marks a memory usage on a memory list
 */
int _mark_memlist(memuse_list_t *head, int type, int size)
{
  memuse_t *use;
  if (type==MEMUSE_STATIC && size==0)
  {
    return 0;
  }
  use=head->head;
  if (use && (type==MEMUSE_STATIC) 
      && (use->type == type))
  {
    use->size += size;
  } else {
    use=(memuse_t *)malloc(sizeof(memuse_t));
    use->type=type;
    use->size=size;
    use->prev=head->head;
    head->head=use;
  }
  return size;
}

void _reset_memlist(memuse_list_t **head)
{
  memuse_list_t *curlist = *head;
  memuse_list_t *tmplist;
  while (curlist) {
    memuse_t *curuse = curlist->head;
    memuse_t *tmpuse;
    while (curuse) {
      tmpuse = curuse->prev;
      free(curuse);
      curuse = tmpuse;
    }
    tmplist = curlist->prev;
    free(curlist);
    curlist = tmplist;
  }
  *head = NULL;
}


/**
 * Wrapper for pushing the heap list
 */
void pushheaplist()
{
  _push_memlist(&heapusage);
}

/**
 * Wrapper for popping and saving the heap list
 */
memuse_list_t *popsaveheaplist()
{
  return _pop_save_memlist(&heapusage);
}

/**
 * Wrapper for marking the heap
 */
int markheap(int type, int size)
{
  return _mark_memlist(heapusage, type, size);
}

/**
 * Wrapper for pushing the stack list
 */
void pushstacklist()
{
  _push_memlist(&stackusage);
}

/**
 * Wrapper for marking the stack
 */
int markstack(int type, int size)
{
  return _mark_memlist(stackusage, type, size);
}

/**
 * Generates code to free all heap allocations on a tracker
 */
void _heap_freeusage(memuse_list_t *heap, int dofree)
{
  memuse_t *cur=heap->head;
  memuse_t *tmp;
  while (cur)
  {
    if (cur->type == MEMUSE_STATIC)
	{
      modheap((-1)*cur->size*sizeof(cell));
    } else {
      modheap_i();
    }
    if (dofree)
    {
      tmp=cur->prev;
      free(cur);
      cur=tmp;
    } else {
      cur=cur->prev;
    }
  }
  if (dofree)
  {
    heap->head=NULL;
  }
}

void _stack_genusage(memuse_list_t *stack, int dofree)
{
  memuse_t *cur = stack->head;
  memuse_t *tmp;
  while (cur)
  {
    if (cur->type == MEMUSE_DYNAMIC)
	{
      /* no idea yet */
      assert(0);
	} else {
      modstk(cur->size * sizeof(cell));
	}
	if (dofree)
	{
      tmp = cur->prev;
      free(cur);
      cur = tmp;
	} else {
      cur = cur->prev;
	}
  }
  if (dofree)
  {
    stack->head = NULL;
  }
}

/**
 * Pops a heap list and frees it.
 */
void popheaplist()
{
  memuse_list_t *oldlist;
  assert(heapusage!=NULL);

  _heap_freeusage(heapusage, 1);
  assert(heapusage->head==NULL);

  oldlist=heapusage->prev;
  free(heapusage);
  heapusage=oldlist;
}

void genstackfree(int stop_id)
{
  memuse_list_t *curlist = stackusage;
  while (curlist && curlist->list_id > stop_id)
  {
    _stack_genusage(curlist, 0);
    curlist = curlist->prev;
  }
}

void genheapfree(int stop_id)
{
  memuse_list_t *curlist = heapusage;
  while (curlist && curlist->list_id > stop_id)
  {
    _heap_freeusage(curlist, 0);
    curlist = curlist->prev;
  }
}

void popstacklist(int codegen)
{
  memuse_list_t *oldlist;
  assert(stackusage != NULL);

  if (codegen)
  {
    _stack_genusage(stackusage, 1);
    assert(stackusage->head==NULL);
  }

  oldlist = stackusage->prev;
  free(stackusage);
  stackusage = oldlist;
}

void resetstacklist()
{
  _reset_memlist(&stackusage);
}

void resetheaplist()
{
  _reset_memlist(&heapusage);
}