#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include "gpklib.h"
#include "assert.h"
#include "gpkNaN.h"
#include "gpkieee754.h"

#define D if(0)

property::property()
: attr(0), val(0), comment(0), next(0), refc(0)
{}


property::property(struct property *n, C char *a, C char *v, C char *c)
{
 make(n, a, v, c);
}


void property::make(struct property *n, C char *a, C char *v, C char *c)
{
 refc = 0;
 next = n;
 attr = strdup(a ? a : "");
 val = v ? strdup(v) : 0;
 comment = c ? strdup(c) : 0;
}


property::property(C property& x)
{
 assert(x.next == 0);
 make(x.next, x.attr, x.val, x.comment);
}


property& property::operator=(C property& x)
{
 if(this != &x)	{
	assert(x.next == 0);
	free();
	make(x.next, x.attr, x.val, x.comment);
	}

 return *this;
}


void property::free()
{
 if(attr)	{
	::free(attr);
	attr = 0;
	}
 if(val)	{
	::free(val);
	val = 0;
	}
 if(comment)	{
	::free(comment);
	comment = 0;
	}
}


property::~property() { assert(refc == 0); free(); }

proplist::proplist()
: first(0)
{
 D fprintf(stderr, "Creating empty proplist\n");
}


void property::incref()
{
 D fprintf(stderr, "\tIR%d->%d\n", refc, refc+1);
 refc++;
}



BOOL property::decref()
{
 assert(refc>0);
 D fprintf(stderr, "\tDR%d->%d\n", refc, refc-1);
 return --refc <= 0;
}


proplist::~proplist()
{
 D fprintf(stderr, "Deleting proplist\n");
 while(pop())
	;
}


BOOL proplist::pop()
{
 assert(this);
 if(isempty())
	return FALSE;

 struct property *next = first->next;
 if(first->decref())
	delete first;
 first = next;

 return !isempty();
}


proplist::proplist(const proplist& x)
: first(x.first)
{
 D fprintf(stderr, "Duplicating proplist\n");
 struct property *t;
 for(t=first; t; t=t->next)
	t->incref();
}



proplist& proplist::operator=(C proplist& x)
{
 D fprintf(stderr, "Assigning proplist\n");
 if(this != &x)	{
	while(pop())
		;

	first = x.first;
	struct property *t;
	for(t=first; t; t=t->next)
		t->incref();
	}

 return *this;
}



proplist& proplist::operator+=(C proplist& x)
{
 D fprintf(stderr, "Appending proplist\n");
 struct property *t;
 for(t=x.first; t ; t=t->next)
	add(t->attr, t->val, t->comment);

 return *this;
}


/* String match with possible '*' as a wild card at the end. */
/* An initial '%' means that matching should be case-insensitive. */
int proplist::matches(C char *a, C char *b)
{
 assert(a && b);
 int ci = 0;
 if(*a == '%')	{
	ci = 1;
	a++;
	}
 if(*b == '%')	{
	ci = 1;
	b++;
	}

 if(ci)
	while(*a && tolower(*a)==tolower(*b))
		a++,b++;
 else
	while(*a && *a==*b)
		a++,b++;

 if(*a=='\0' && *b=='\0')
	return 1;
 else if(*a=='*' && *(a+1)=='\0')
	return 1;
 else if(*b=='*' && *(b+1)=='\0')
	return 1;

 return 0;
}




void proplist::add(C char *a, C char *v, C char *c)
{
 /* This function can invalidate pointers previously returned
 by proplist::get and proplist::gets
 */
 assert(this);
 assert(a);

 D fprintf(stderr, "Adding property %s to proplist\n", a?a:"<null>");
 /* If the parameter already exists, and is not shared, replace it. */

 struct property *t;
	/* Search for existing entry.  Slow - n^2 process,
	but valuable if there is a lot of repetition. */
 for(t=first; t && t->refc==1; t=t->next)
	if(strcmp(t->attr, a) == 0) {
		/* Use strcmp, rather than matches, because attributes
			that match each other may themselves match
			different sets.   An example is "ab" and "a*".
			*/
		if(t->val)
			free(t->val);
		if(t->comment)
			free(t->comment);

		t->val = v ? strdup(v) : 0;
		t->comment = c ? strdup(c) : 0;
		return;
		}

 first = new property(first, a, v, c);
 first->incref();
}


void proplist::add(C char *a, double v, C char *c)
{
 char buf[100];
 sprintf(buf, "%.14g", v);
 add(a, buf, c);
}


void proplist::add(C char *a, long v, C char *c)
{
 char buf[100];
 sprintf(buf, "%ld", v);
 add(a, buf, c);
}



void proplist::add(C char *a, int v, C char *c)
{
 char buf[100];
 sprintf(buf, "%d", v);
 add(a, buf, c);
}



void proplist::add(C char *a, short v, C char *c)
{
 char buf[100];
 sprintf(buf, "%d", v);
 add(a, buf, c);
}



property *proplist::get(C char *a)
{
 assert(this);
 assert(a);
 struct property *t;
 for(t=first; t; t=t->next)
	if(matches(t->attr, a))
		return t->val ? t : 0;

 return 0;
}


C property *proplist::get(C char *a) C
{
 assert(this);
 assert(a);
 C property *t;
 for(t=first; t; t=t->next)
	if(matches(t->attr, a))
		return t->val ? t : 0;

 return 0;
}




const char *proplist::gets(C char *attr, C char *notthere) C
{
 assert(this);
 C property *tmp = get(attr);
 if(!tmp)
	return notthere;

 return tmp->val;
}





double property::getd() C
{
 return safe_atof(val);
}


double proplist::getd(C char *attr, double notthere) C
{
 assert(this);
 C property *tmp = get(attr);
 if(!tmp)
	return notthere;

 return tmp->getd();
}


double proplist::getd(C char *attr) C
{
	return getd(attr, quiet_nan(0));
}


long property::getl() C
{
 return safe_atol(val);
}


long proplist::getl(C char *attr, long notthere) C
{
 assert(this);
 C property *tmp = get(attr);
 if(!tmp)
	return notthere;

 return tmp->getl();
}


long proplist::getl(C char *attr) C
{
	return getl(attr, LONG_NAN);
}



int property::geti() C
{
 return safe_atoi(val);
}



int proplist::geti(const char *attr, int notthere) C
{
 assert(this);
 C property *tmp = get(attr);
 if(!tmp)
	return notthere;
 return tmp->geti();
}




short property::geth() C
{
 return safe_atoh(val);
}



short proplist::geth(C char *attr, short notthere) C
{
 assert(this);
 C property *tmp = get(attr);
 if(!tmp)
	return notthere;
 return tmp->geth();
}



