/************************************************************************
 * Copyright (C) 2002-2009, Xiph.org Foundation
 * Copyright (C) 2010, Robin Watts for Pinknoise Productions Ltd
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the names of the Xiph.org Foundation nor Pinknoise
 * Productions Ltd nor the names of its contributors may be used to
 * endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ************************************************************************/

#define HEAD_ALIGN 64
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define MISC_C
#include "misc.h"
//#include <sys/time.h>

static void **pointers=NULL;
static long *insertlist=NULL; /* We can't embed this in the pointer list;
			  a pointer can have any value... */

static char **files=NULL;
static long *file_bytes=NULL;
static int  filecount=0;

static int ptop=0;
static int palloced=0;
static int pinsert=0;

typedef struct {
  char *file;
  long line;
  long ptr;
  long bytes;
} head;

long global_bytes=0;
long start_time=-1;

static void *_insert(void *ptr,long bytes,char *file,long line){
  ((head *)ptr)->file=file;
  ((head *)ptr)->line=line;
  ((head *)ptr)->ptr=pinsert;
  ((head *)ptr)->bytes=bytes-HEAD_ALIGN;

  if(pinsert>=palloced){
    palloced+=64;
    if(pointers){
      pointers=(void **)realloc(pointers,sizeof(void **)*palloced);
      insertlist=(long *)realloc(insertlist,sizeof(long *)*palloced);
    }else{
      pointers=(void **)malloc(sizeof(void **)*palloced);
      insertlist=(long *)malloc(sizeof(long *)*palloced);
    }
  }

  pointers[pinsert]=ptr;

  if(pinsert==ptop)
    pinsert=++ptop;
  else
    pinsert=insertlist[pinsert];

#ifdef _VDBG_GRAPHFILE
  {
    FILE *out;
    struct timeval tv;
    static struct timezone tz;
    int i;
    char buffer[80];
    gettimeofday(&tv,&tz);

    for(i=0;i<filecount;i++)
      if(!strcmp(file,files[i]))break;

    if(i==filecount){
      filecount++;
      if(!files){
	files=malloc(filecount*sizeof(*files));
	file_bytes=malloc(filecount*sizeof(*file_bytes));
      }else{
	files=realloc(files,filecount*sizeof(*files));
	file_bytes=realloc(file_bytes,filecount*sizeof(*file_bytes));
      }
      files[i]=strdup(file);
      file_bytes[i]=0;
    }

    file_bytes[i]+=bytes-HEAD_ALIGN;

    if(start_time==-1)start_time=(tv.tv_sec*1000)+(tv.tv_usec/1000);

    snprintf(buffer,80,"%s",file);
    if(strchr(buffer,'.'))strchr(buffer,'.')[0]=0;
    strcat(buffer,_VDBG_GRAPHFILE);
    out=fopen(buffer,"a");
    fprintf(out,"%ld, %ld\n",-start_time+(tv.tv_sec*1000)+(tv.tv_usec/1000),
	    file_bytes[i]-(bytes-HEAD_ALIGN));
    fprintf(out,"%ld, %ld # FILE %s LINE %ld\n",
	    -start_time+(tv.tv_sec*1000)+(tv.tv_usec/1000),
	    file_bytes[i],file,line);
    fclose(out);

    out=fopen("total"_VDBG_GRAPHFILE,"a");
    fprintf(out,"%ld, %ld\n",-start_time+(tv.tv_sec*1000)+(tv.tv_usec/1000),
	    global_bytes);
    fprintf(out,"%ld, %ld\n",-start_time+(tv.tv_sec*1000)+(tv.tv_usec/1000),
	    global_bytes+(bytes-HEAD_ALIGN));
    fclose(out);
  }
#endif

  global_bytes+=(bytes-HEAD_ALIGN);

  return(void *)(((char *)ptr)+HEAD_ALIGN);
}

static void _ripremove(void *ptr){
  int insert;

#ifdef _VDBG_GRAPHFILE
  {
    FILE *out=fopen("total"_VDBG_GRAPHFILE,"a");
    struct timeval tv;
    static struct timezone tz;
    char buffer[80];
    char *file =((head *)ptr)->file;
    long bytes =((head *)ptr)->bytes;
    int i;

    gettimeofday(&tv,&tz);
    fprintf(out,"%ld, %ld\n",-start_time+(tv.tv_sec*1000)+(tv.tv_usec/1000),
	    global_bytes);
    fprintf(out,"%ld, %ld\n",-start_time+(tv.tv_sec*1000)+(tv.tv_usec/1000),
	    global_bytes-((head *)ptr)->bytes);
    fclose(out);

    for(i=0;i<filecount;i++)
      if(!strcmp(file,files[i]))break;

    snprintf(buffer,80,"%s",file);
    if(strchr(buffer,'.'))strchr(buffer,'.')[0]=0;
    strcat(buffer,_VDBG_GRAPHFILE);
    out=fopen(buffer,"a");
    fprintf(out,"%ld, %ld\n",-start_time+(tv.tv_sec*1000)+(tv.tv_usec/1000),
	    file_bytes[i]);
    fprintf(out,"%ld, %ld\n",-start_time+(tv.tv_sec*1000)+(tv.tv_usec/1000),
	    file_bytes[i]-bytes);
    fclose(out);

    file_bytes[i]-=bytes;

  }
#endif

  global_bytes-=((head *)ptr)->bytes;

  insert=((head *)ptr)->ptr;
  insertlist[insert]=pinsert;
  pinsert=insert;

  if(pointers[insert]==NULL){
    fprintf(stderr,"DEBUGGING MALLOC ERROR: freeing previously freed memory\n");
    fprintf(stderr,"\t%s %ld\n",((head *)ptr)->file,((head *)ptr)->line);
  }

  if(global_bytes<0){
    fprintf(stderr,"DEBUGGING MALLOC ERROR: freeing unmalloced memory\n");
  }

  pointers[insert]=NULL;
}

void _VDBG_dump(void){
  int i;
  for(i=0;i<ptop;i++){
    head *ptr=pointers[i];
    if(ptr)
      fprintf(stderr,"unfreed bytes from %s:%ld\n",
	      ptr->file,ptr->line);
  }

}

extern void *_VDBG_malloc(void *ptr,long bytes,char *file,long line){
  bytes+=HEAD_ALIGN;
  if(ptr){
    ptr=(void *)(((char *)ptr)-HEAD_ALIGN);
    _ripremove(ptr);
    ptr=realloc(ptr,bytes);
  }else{
    ptr=malloc(bytes);
    memset(ptr,0,bytes);
  }
  return _insert(ptr,bytes,file,line);
}

extern void _VDBG_free(void *ptr,char *file,long line){
  if(ptr){
    ptr=(void *)(((char *)ptr)-HEAD_ALIGN);
    _ripremove(ptr);
    free(ptr);
  }
}