#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define DMID 14
#include "debug.h"
#include "keep.h"
#include "lib.h"
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE (!FALSE)
#endif
Kpairval *kpair_add_nodup(Kpair **pkp, char *var, char *val);
void keep_malloc_more(Keeplist **h, char *source, int line, int xmin);
#ifdef IS64
static int align=8;
static int align_mask=7;
#else
static int align=4;
static int align_mask=3;
#endif
Kpairval *kpair_find_raw(Kpair *kp, char *want);
void *keep_malloc2(int sz,Keeplist **h, char *source, int line)
{
	Keeplist *k;
	char *mem;
	if (*h==NULL) keep_malloc_more(h,source,line,sz);
	k = *h;
	if (k->sz - k->used < sz) keep_malloc_more(h,source,line,sz);			
	k = *h;
	mem = k->mem + k->used;
	if (k->sz - k->used < sz) lib_abort();
	k->used += sz;
	return mem;
}
void *keep_calloc2(Keeplist **h, int sz, int num, char *source, int line)
{
	void *a;
	a = keep_malloc_align2(sz*num,h,source,line);
	memset(a,0,sz*num);
	return a;
}
void *keep_realloc(Keeplist **h, void *x, int sz) // WARNING, THROWS AWAY THE OLD DATA IF IT GROWS...
{
	return keep_malloc2(sz,h,__FILE__,__LINE__);
}
void *keep_malloc_align2(int sz,Keeplist **h, char *source, int line)
{
	Keeplist *k;
	int add=0;
	char *mem;
	if (*h==NULL) keep_malloc_more(h,source,line,sz);
	k = *h;
	if (k->sz - k->used < sz+align) keep_malloc_more(h,source,line,sz);			
	k = *h;
	mem = k->mem + k->used;
	if ((((intptr_t) mem) & align_mask) != 0) {
		add = align - (((intptr_t) mem) & align_mask);
		sz += add;
	}
/*	if (add!=0) 	printf("Aligned %p to %p, added %d, mask %d\n",mem,mem+add,add,((unsigned int) mem) & align_mask);*/
	if (k->sz - k->used < sz) lib_abort();
	k->used += sz;
	return mem+add;
}
char *keep_strdup2(Keeplist **h, char *src, char *source, int line)
{
	char *m = keep_malloc2(lib_strlen(src)+1,h,source,line);
	strcpy(m,src);
	return m;
}
static int tot;
void keep_freeall(Keeplist **h)
{
	Keeplist *k,*knext;
	for (k=*h; k!=NULL; k=knext) {
		// Set to null in case surgeweb is still using freed blocks, and
		// this should likely make it crash sooner!
		memset(k->mem,0,k->sz);
		knext = k->next;
		tot -= k->sz;
		free(k->mem); k->mem = NULL; k->next = NULL;
		free(k);
	}
	*h = NULL;
}
#define CHUNKSZ 8000
void keep_malloc_more(Keeplist **h, char *source, int line, int xmin)
{
	Keeplist *k;
	int want = CHUNKSZ;
	if (h==NULL) want = 200;  // start with small one, then grow it...
//	printf("min %d, want %d\n",xmin,want);
	if ((xmin+100)>want) want = xmin+100;
#ifdef USE_DMALLOC
	k = d_callocid(DMID,1,sizeof(Keeplist),source,line);
	if (k==NULL) {
		d_dump_crash();
	}
	k->mem = d_callocid(DMID,want,1,source,line);		
	if (k->mem==NULL) d_dump_crash();
#else
	k = calloc(1,sizeof(Keeplist));
	k->mem = calloc(want,1);		
#endif
	k->sz = want;
	k->used = 0;
	tot += k->sz;
	k->next = *h;
	*h = k;	
}
int keep_total(void)
{
	return tot;
}

//typedef struct KPAIRVAL {char *var; char *val; struct KPAIRVAL *next;} Kpairval;
//typedef struct KPAIR {Keeplist *k; Kpairval *first;} Kpair;
int kpair_new(Kpair **pkp)
{
	Kpair *kp;
	kp = calloc(1,sizeof(Kpair));
	*pkp = kp;
	return TRUE;
}

int kpair_add_end(Kpair **pkp, char *var, char *val)
{
	Kpair *kp;
	Kpairval *p;
	int len;
	char *v;
	if (*pkp==NULL) kpair_new(pkp);
	kp = *pkp;
	if (lib_strlen(var)==0) {
		/* For news headers, add it to end of previous header with crlf ? */
		p = kp->last;
		if (p==NULL) return FALSE;
		len = lib_strlen(p->val)+lib_strlen(val);
		v = keep_malloc(len+10,&kp->k);
		memcpy(v,p->val,lib_strlen(p->val)+1);
		p->val = v;
		strcpy(p->val+lib_strlen(p->val),"\r\n ");
		strcpy(p->val+lib_strlen(p->val),val);
		return TRUE;
	}
	p = keep_malloc_align(sizeof(Kpair),&kp->k);
	p->next = NULL;
	p->var = keep_strdup(&kp->k,var);
	p->val = keep_strdup(&kp->k,val);
	if (kp->last==NULL) {
		kp->first = p;
	} else {
		kp->last->next = p;
	}
	kp->last = p;
	return TRUE;
}
int kpair_free(Kpair **pkp)
{
	Kpair *kp;
	if (*pkp==NULL) return TRUE;
	kp = *pkp;
	keep_freeall(&kp->k);
	free(kp);
	*pkp = NULL;
	return TRUE;
}
int kpair_removenc(Kpair *kp, char *var, char *val)
{
	Kpairval *p,*prev = NULL;
	if (kp==NULL) return FALSE;
	for (p = kp->first ;p != NULL; p = p->next) {
		if (strcmpnc(var,p->var) != 0) goto keepit;
		if (val && strcmpnc(val,p->val) != 0) goto keepit;

		if (prev) prev->next = p->next;
		else kp->first = p->next;
		continue;
keepit:
		prev = p; 
	}
	// reset the end of list variable. 
	kp->last = NULL;
	for (p = kp->first ;p != NULL; p = p->next) {
		kp->last = p;
	}
	return FALSE;
}

Kpairval *kpair_add2(Kpair **pkp, char *var, char *val, char *source, int line)
{
	Kpair *kp;
	Kpairval *p;
	if (*pkp==NULL) kpair_new(pkp);
	kp = *pkp;
	p = keep_malloc_align2(sizeof(Kpair),&kp->k,source,line);
	p->next = NULL;
	p->var = keep_strdup2(&kp->k,var,source,line);
	p->val = keep_strdup2(&kp->k,val,source,line);
	p->next = kp->first;
	kp->first = p;
	kp->last = NULL;
	return p;
}
Kpairval *kpair_add_nodup(Kpair **pkp, char *var, char *val)
{
	Kpairval *p;
	Kpair *kp;
	if (*pkp==NULL) kpair_new(pkp);
	kp = *pkp;
	p = kpair_find_raw(kp,var);
	if (p==NULL) p = kpair_add(pkp,var,val);
	else {
		p->val = NULL;  //keep so we can't free it...
		p->val = keep_strdup(&kp->k,val);
	}
	return p;
}
// Allow the use of a central keeplist
int kpair_add_k2(Kpair **pkp, char *var, char *val, Keeplist **k, char *source, int line)
{
	Kpair *kp;
	Kpairval *p;
	if (*pkp==NULL) *pkp = keep_malloc_align2(sizeof(Kpair),k,source,line);
	kp = *pkp;
	p = keep_malloc_align2(sizeof(Kpair),k,source,line);
	p->next = NULL;
	p->var = keep_strdup2(k,var,source,line);
	p->val = keep_strdup2(k,val,source,line);
	p->next = kp->first;
	kp->first = p;
	kp->last = NULL;
	// Copy just to keep actual kpair valid, but should not be used for freeing etc with central keeplist
	kp->k=*k;
	return TRUE;
}

int kpair_add_raw(Kpair **pkp, char *var, char *val)
{
	Kpair *kp;
	Kpairval *p;
	if (*pkp==NULL) kpair_new(pkp);
	kp = *pkp;
	p = keep_malloc_align(sizeof(Kpair),&kp->k);
	p->next = NULL;
	p->var = keep_strdup(&kp->k,var);
	p->val = val; //ALREADY ALLOCATED ON KEEP STRUCTURE....
	p->next = kp->first;
	kp->first = p;
	kp->last = NULL;
	return TRUE;
}
char *kpair_find_null(Kpair *kp, char *want)
{
	Kpairval *p = kpair_find_raw(kp,want);
	if (p==NULL) return NULL;
	return p->val;
}
Kpairval *kpair_find_raw(Kpair *kp, char *want)
{
	Kpairval *p;

	if (kp==NULL) return NULL;	
	for (p=kp->first;p!=NULL;p=p->next) {
		if (strcmp(want,p->var)==0) return p;
	}
	return NULL;
}
char *kpair_findnc_null(Kpair *kp, char *want)
{
	Kpairval *p;

	if (kp==NULL) return NULL;	
	for (p=kp->first;p!=NULL;p=p->next) {
		if (strcmpnc(want,p->var)==0) return p->val;
	}
	return NULL;
}
char *kpair_findnc(Kpair *kp, char *want)
{
	Kpairval *p;

	if (kp==NULL) return "";	
	for (p=kp->first;p!=NULL;p=p->next) {
		if (strcmpnc(want,p->var)==0) return p->val;
	}
	return "";
}

int kpair_new_reuse(Kpair **pkp, Keeplist**k)
{
	Kpair *kp;
	kp = keep_calloc(k,1,sizeof(Kpair));
	kp->k = *k;
	*pkp = kp;
	return TRUE;
}
char *kpair_find_nonull(Kpair *kp, char *want)
{
	char *s;
	s=kpair_find_null(kp,want);
	if (s==NULL) s="";	
	return s;
}
// MW: Function based on : qpair_token_split, function logic not reviewed on addition here
//  -- modified so it outputs escape encoded strings 
int kpair_token_split(char *s, Kpair **pairs,  char *tmp, int tmpsz, Keeplist **k)
{
	/* a="test" b="j  pb" c="end" d="a quoted \"word\" with a slash\\ " */
    int n=0;
	char *var;
	char *o=tmp;

    for (;*s!=0;) {
        s += strspn(s," ");  /* Skip spaces */
        if (*s==0) break;
        var = s;
        s += strcspn(s,"=");  /* Find end of word */
		if (*s==0) break;
		*s = 0; s++;
		if (*s=='"') s++;
		o=tmp;
		for (;*s!=0;s++) {
			if (o-tmp>tmpsz-10) break;
			if (*s=='"') break;
			if (*s=='\\' && *(s+1)!='\0') {
				*o++ = *s++;
			}
			*o++ = *s;
		}
		*o++ = 0;
		kpair_add_k(pairs,var,tmp,k);
		n++;
		if (*s) s++;
    }
    return n;
}

Kpair *lib_email_getheaders_keep(char *fname)
{
	char bf[BFSZ],*p,*n;
	Kpair *list = NULL;
	FILE *f;
	int len;
	char *firstcolon;
	char *firstspace;
	char *r;
	Kpairval *last=NULL;

	f = fopen(fname,"r");
	if (f==NULL) return list;

	while(fgets(bf,BFSZ-1,f)) {
		if (iseol(bf[0])) break;
		if (sisspace(bf[0])) {
			char *n;
			for(p = bf; sisspace(*p); p++);
			if (!last) break; //if it starts with a line containing spaces, it has no headers.
			len = lib_strlen(last->val) + lib_strlen(p) + 4;
			n = keep_malloc(len,&list->k);
			ncpy(n,last->val,len-1);
			last->val = n;
			ncat(last->val," ",len-1);
			ncat(last->val,p,len-1);
			continue;
		}
		firstspace = strchr(bf,' ');
		firstcolon = strchr(bf,':');
		if (firstcolon==NULL) break;
		if (firstspace!=NULL) if (firstspace<firstcolon) break;
		p = mystrtok(bf,":",&n);
		if (p==NULL) {
			if (!last) break; //not a header, and none found so far, must be no headers in this file/message
			continue; //malformed header? ignore..
		}
		r = mystrtok(NULL,"\r\n",&n);
		if (r==NULL) {
			last = kpair_add_nodup(&list,p,""); // ADD AN EMPTY ONE
			continue;
		}
		for(; sisspace(*r); r++);
		last = kpair_add_nodup(&list,p,r);

	}
	fclose(f);

	return list;
}
void kpair_copy(Kpair **dst, Kpair *src)
{
	Kpairval *k;
	if (src==NULL) { // if source null, copy null to dst...
		*dst = NULL;
		return; 
	}
	for(k=src->first; k!=NULL; k = k->next) {
		kpair_add(dst,k->var,k->val);
	}
}


/* kword, efficient version of word stuff */
void kword_add(Kwordlist *k, char *w)
{
	Kword *a = keep_calloc(&k->keep,1,sizeof(Kword));
	a->word = keep_strdup(&k->keep,w);
	a->next = k->head;
	k->head = a;
}

Kwordlist *kword_new(void)
{
	Kwordlist *kw = calloc(1,sizeof(Kwordlist));
	return kw;
}
Kwordlist *kword_reverse(Kwordlist *list)
{
	Kwordlist *kw = kword_new();
	Kword *k;
	for (k=list->head;k!=NULL;k=k->next) {
		kword_add(kw,k->word);
	}
	kword_free(list);
	return kw;
}
void kword_free(Kwordlist *list)
{
	if (list==NULL) return;
	keep_freeall(&list->keep);
	free(list);
}
