#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include "dmem.h"
#include "lib.h"
#include "debug.h"
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE (!FALSE)
#endif
#ifdef WIN32
#define vsnprintf _vsnprintf
#endif

/*
#undef calloc
#undef malloc
#undef free
#define calloc real_calloc
#define malloc real_malloc
#define free real_free
*/

//typedef struct DMEMLIST {char *data; int size; int used; struct DMEMLIST *next;} Dmemlist;
//typedef struct DMEM {Dmemlist *d; Dmemlist *last; int len; int blocksize;} Dmem;
Dmemlist *dmem_add_block(Dmem *d, int wanted);

int dmem_new(Dmem **dd)
{
	Dmem *d;
	d = calloc(1,sizeof(Dmem));
	*dd = d;
	return TRUE;
}
int dmem_free(Dmem **dd)
{
	Dmem *d=*dd;
	Dmemlist *m,*next;
	if (d==NULL) return TRUE;
	for (m=d->d;m!=NULL;m=next) {
		next = m->next;
		free(m->data);
		free(m);
	}	
	free(d);
	*dd = NULL;
	return TRUE;
}
int dmem_readfile(Dmem *d, FILE *f)
{
	int r;
	char bf[BFSZ];
	for (;!feof(f);) {
		r = fread(bf,1,BFSZ-1,f);
		if (r<=0) break;
		dmem_write(d,bf,r);
	}
	return TRUE;
}
int dmem_printf(Dmem *d,const char * arg_list, ...)
{
	va_list arg_ptr;
	char *format;
	int len;
	char output[2000];
	va_start(arg_ptr, arg_list);
	format = (char *) arg_list;
	len = vsnprintf(output,1000-10, format, arg_ptr);
	output[900] = '\0';
	dmem_write(d,output,strlen(output));
	return 0;
}
int dmem_insert(Dmem *d, char *data, int len)
{
	Dmemlist *m;
	int want = len;
	if (want<8000) want = 8000;
	m = calloc(1,sizeof(Dmemlist));
	m->data = malloc(want+10); /* scummy but helps later on when used we can fiddle with end ok and add nullls */
	if (m->data==NULL) {
		lib_abort();
	}
	m->size = want;
	memcpy(m->data,data,len);
	m->used = len;

	d->len += len;
	m->next = d->d;
	d->d = m;
	return TRUE;
}

int dmem_write(Dmem *d, char *data, int len)
{
	int space;
	int use;
	Dmemlist *m;

//printf("dmem_write (%.*s) \n",len,data);
	if (d==NULL) return TRUE;
	d->len += len;
	if (d->last==NULL) dmem_add_block(d,len);
	m = d->last;
	for (;len>0;) {	
		space = m->size - m->used;
		if (space==0) m = dmem_add_block(d,len);
		space = m->size - m->used;
		if (space>0) {
			use = space;
			if (use>len) use = len;
			if (m->data==NULL) {
				//emsg("dmem: failed as suddenly null %p %d \n",m->data,m->size);
				lib_abort();
			}
			memcpy(m->data+m->used,data,use);
			data += use;
			len -= use;
			m->used += use;
			m->data[m->used] = 0;
		}
	}
	return TRUE;
}
#define DBLOCK 30000  // MUST NOT BE INCREASED, BREAKS OTHER THINGS.... (POSTING FEED)
Dmemlist *dmem_add_block(Dmem *d, int wanted)
{
	Dmemlist *m;
	int want = DBLOCK;
	if (d->last==NULL) want = DBLOCK; // Used to be 8000, but this way the header is pretty much gauranteed to be totally in the first block.
	if (wanted>8000) want = DBLOCK;
	m = calloc(1,sizeof(Dmemlist));
	m->data = malloc(want+10); /* scummy but helps later on when used we can fiddle with end ok and add nullls */
	if (m->data==NULL) {
//		emsg("dmem: malloc failed\n");
		lib_abort();
	}
	m->size = want;
	if (d->last!=NULL) {
		d->last->next = m;
	}
	d->last = m;
	if (d->d==NULL) d->d = m;
	return m;
}
int dmem_fwrite(FILE *f, Dmem *d)
{
	Dmemlist *m;
	int tot=0;
	for (m=d->d; m!=NULL; m = m->next) {
		tot += fwrite(m->data,1,m->used,f);
	}
	return tot;
}
