// Simple library to pass XML into a tree structure.
// --------------------------------------------------
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <time.h>
#include <errno.h>
#include <stdarg.h>
#ifndef TRUE
#define FALSE 0
#define TRUE (!FALSE)
#endif
#include "defines.h"
#include "tpl.h"
#include "lib.h"
#include "lprintf.h"

#define DMID 15
#include "debug.h"
#include "xml.h"
#include "dstr.h"
void emsg(char * arg_list, ...);
void imsg(char * arg_list, ...);
void dmsg(char * arg_list, ...);



/*

<?xml version="1.0" encoding="ISO-8859-1"?>
<note date="12/11/2002">
<to>Tove</to>
<from>Jani</from>
</note>
</ note >


<note
date
=
"12/11/2002"
xmlns:f
=
"asdfasdff"
>

<to
>
Tove xyz asdfasfasfd
</to
>
</note
>



<
note 
date
=
"12/11/2002"
>
<
/
from
>


*/
int xml_apply_xlate(Xml *top, int depth, Pair *xlate_inherit);
Xml *xml_new(Xml *parent, char *file, int line)
{
	Xml *x = d_callocid(1,1,sizeof(Xml),file,line);
	x->parent = parent;
	return x;
}
Xml *xml_read_file_real(char *fname, char *file, int line)
{
	int sz = lib_file_size(fname);
	Xml *xml;
	FILE *f;
	char *bf = malloc(sz+1);
	f = fopen(fname,"rb");
	if (f) {
		fread(bf,1,sz,f);
		bf[sz] = 0;
		fclose(f);
	}
	xml = xml_read_buffer(bf,file,line);
	free(bf);
	return xml;
}
Xml *xml_read_buffer(char *in, char *file, int line)
{
	Xml *top;
	int end_found;
	char *s = strstr(in,"<?xml");
	if (s!=NULL) {
		// Find the end of the first line;
		s = strstr(s,">"); 
		if (s==NULL) {
			printf("Unable to find end of xml\n");
			return NULL;
		}
		s++;
	} else {
		s = in;
	}
	top = xml_new(NULL,file,line);
	xml_read_node(top,&s,&end_found,file,line);
	// Debuggin 
	xml_apply_xlate(top,0,NULL);
	//printf("---------------------- xml print after \n");
	//xml_print(top,0,NULL);
	return top;
}
int xml_read_text(Xml *cur, char **pin)
{
	int len=0,xlen=0,bfsz=0;
	char *updated=NULL;
	char *start = *pin;
	char *s = *pin;
	// For the moment lets just step past it.	
	for (;*s!=0;s++) {
		if (*s=='<') break;
	}
	len = (int)(s - *pin);
	*pin = s;
	if(cur->text) len+=lib_strlen(cur->text);
	bfsz=len+1;
	updated=calloc(1,bfsz);
	if(cur->text) ncpy(updated,cur->text,bfsz-1);
	ncat(updated,start,bfsz-1);
	ffree(cur->text);
	cur->text=updated;
	return TRUE;
}
int xml_read_token_real(char **pin, char *bf);
int xml_read_token(char **pin, char *bf)
{
	int x = xml_read_token_real(pin,bf);
//	printf("Read token %d (%s)\n",x,bf);
	return x;
}
int xml_read_token_real(char **pin, char *bf)
{
	char *in = *pin;
	char *out=bf;
	if (*in==0) return FALSE;
	if (*in=='"') {
		in++;
		// Search for ending quote.
		for (;*in!=0;in++) {
			if (out-bf>BFSZ-10) break;
			if (*in=='"') {
				in++;
				break;
			}
			*out++ = *in;
		}
	} else if (*in=='\'') {
		in++;
		// Search for ending quote.
		for (;*in!=0;in++) {
			if (out-bf>BFSZ-10) break;
			if (*in=='\'') {
				in++;
				break;
			}
			*out++ = *in;
		}
	} else {
	  for (;*in!=0;) {
		char c;
		int sisspace=FALSE;
		int isspec = FALSE;
	
		sisspace=FALSE;
		isspec = FALSE;
		if (out-bf>BFSZ-10) break;
		c = *out++ = *in++;
	
		if (c==' ') sisspace=TRUE;
		if (c=='\r') sisspace=TRUE;
		if (c=='\n') sisspace=TRUE;
		if (c=='\t') sisspace=TRUE;
		if (c=='<') isspec=TRUE;
		if (c=='=') isspec=TRUE;
		if (c=='>') isspec=TRUE;
		if (c=='/') isspec=TRUE;
		if (c=='"') isspec=TRUE;
		if (c=='\'') isspec=TRUE;
		if (sisspace) {
			out--;
			break;
		}
		if (isspec) {
			if (out==bf+1) { // One char, send the special
				break;
			} else {	// multi char, send what we got, special next time.
				out--; // not on end of current word
				in--;  // keep it for next time. 
				break;
			}
		}

	  }
	}
	*out = 0;
	*pin = in;
	return TRUE;
}
int xml_read_node(Xml *cur, char **pin, int *end_found, char *file, int line)
{
	char *s = *pin;
	char eq[BFSZ];
	char val[BFSZ];
	char bf[BFSZ];
	int ok;
	char name[BFSZ];

	*end_found = FALSE;
	// Find the start of the node. 
	for (;*s!=0;s++) if (*s=='<') break; 
	*pin = s;
	if (*s==0) {
		printf("could not find end of node\n");
		return FALSE;
	}

	s++;

	if (*s=='/')  {  // Found end of something.
		*pin = s+1;
		*end_found = TRUE;
		return TRUE;
	}

	// Read the name of the node.

	if (!xml_read_token(&s,bf)) {
		printf("xml: read token failed\n");
		return FALSE; 
	}
	cur->name = strdup(bf);

//	printf("Reading xml node (%s) \n",cur->name);
	

	// Read the attributes into the pair array...
	//<note date="12/11/2002">
	for (ok=xml_read_token(&s,bf);ok;ok=xml_read_token(&s,bf)) {
		if (strcmp(bf,"")==0) continue; // get the next token.
//printf("Read token loop (%s)\n",bf);
		if (strcmp(bf,"/")==0) { // Self terminating node.
			xml_read_token(&s,bf);
			*pin = s;
			//imsg("Found self termination\n");
			return TRUE;
		}
		if (strcmp(bf,">")==0) {
	//printf("found end bracket\n");
			break; // end of attributes
		}
		if (!xml_read_token(&s,eq)) {
			//printf("found no eq\n");
			break;
		}
		if (strcmp(eq,"=")==0) {
			ok = xml_read_token(&s,val);
			pair_add(&cur->attribs,bf,val);
	//printf("Attributes (%s) (%s)\n",bf,val);
		} else if (strcmp(bf,">")==0) { // end of attributes.
	//printf("end attrib2\n");
			break;
		} else {
			emsg("Error not expecting (%s)\n",eq);
			printf("Error not expecting (%s)\n",eq);
			break;
		}
	}
	
	// Now read any 'nodes' that we can find that are children of this node....
	for (ok=TRUE;ok;) {
		int endobj;
		Xml *n = xml_new(cur,file,line);
		
		xml_read_text(cur,&s); // Read any text and add a text object if we have to.
		ok = xml_read_node(n,&s,&endobj,file,line);
		if (!ok) {
			// chrisp added, dangling xml node we creatd, must be freed:
			xml_free(n); n = NULL;
			break;
		}
		if (endobj) {
			ok = xml_read_token(&s,name); // Must be a token next.			
			ok = xml_read_token(&s,bf); // Read the trailing bracket. 
			if (strcmp(cur->name,name)==0) {
				xml_free(n); n = NULL;
				break; // End of this object. 
			}
			else imsg("Invalid end of object (%s) (%s)\n",cur->name,name);
			// chrisp added, dangling xml node we creatd, must be freed:
			xml_free(n); n = NULL;
		} else {
			if (cur->child_last) cur->child_last->next = n;
			cur->child_last = n;
			if (cur->child==NULL) cur->child = n;

		}
	}
	*pin = s;
	
	return TRUE;
}
int notblank(char *s)
{
	if (s==NULL) return FALSE;
	for (;*s!=0;s++) {
		if (sisalnum(*s)) return TRUE;
	}
	return FALSE;
}
int xml_translate(Pair *xlate, char *name, char *out, char *nsout, char *nsp)
{
	char *rest;
	char *s;
	char res[BFSZ];
	char ns[BFSZ];
	strcpy(nsout,""); ncpy(out,name,BFSZ-1); 
	ncpy(ns,name,BFSZ-1);
	s = strchr(ns,':');
	if (s==NULL) {
		// Check if we have a default one...
		ncpy(res,pair_find(xlate,"$DEFAULT$"),BFSZ-1);
		if (lib_strlen(res)>0) {
			ncpy(nsout,res,BFSZ-1);
			return TRUE;
		}
		return FALSE;
	}
	*s = 0; rest = s+1;
	ncpy(out,rest,BFSZ-1);

	ncpy(res,pair_find(xlate,ns),BFSZ-1);
	if (lib_strlen(res)==0) {
		ncpy(nsout,ns,BFSZ-1);
		return FALSE;
	}
	lprintf(out,BFSZ-1,"%s",rest);
	ncpy(nsout,res,BFSZ-1);
	ncpy(nsp,ns,BFSZ-1);
	return TRUE;
}
Pair *xml_xlate_add(Pair *inherit, Xml *top)
{
	Pair *xnew=NULL;
	Pair *p;
	for (p=top->attribs;p!=NULL;p=p->next) {
	//printf("xml_xlate_add (%s)\n",p->var);
		if (strncmp(p->var,"xmlns:",6)==0) {
			if (xnew==NULL) pair_copy(&xnew,inherit);
			pair_add(&xnew,p->var+6,p->val);
			//printf("New Translation added (%s) (%s)\n",p->var+6,p->val);
		} else if (strcmp(p->var,"xmlns")==0) { // default one 
			if (xnew==NULL) pair_copy(&xnew,inherit);
			pair_add(&xnew,"$DEFAULT$",p->val);
			//printf("New Translation added (%s) (%s)\n","$DEFAULT$",p->val);
		}
	}
	if (xnew!=NULL) return xnew; 

	return inherit;
}
char *xml_top_name(Xml *top, char *tmp)
{
	if (top==NULL) return "";
	return top->name;
/*	int ret=FALSE;
	Pair *xlate = NULL;

	xlate = xml_xlate_add(xlate,top);
	xml_translate(xlate,top->name,tmp,nsp);
	pair_free(&xlate);
	return tmp;
	*/
}
int xml_print(Xml *top, int depth, Pair *xlate_inherit)
{
	Xml *x;
	int i;
	Pair *p;

	if (depth==0) printf("XML_PRINT====\n");
	if (top==NULL) {printf("XML->NULL\n"); return TRUE;}
	for (i=0; i<depth; i++) {
		printf(" ");
	}
	printf("<%s> ",top->name);
	for (p=top->attribs;p!=NULL;p=p->next) {
		printf("%s=%s ",p->var,p->val);
	}
	if (notblank(top->text)) printf("(%s)",top->text);
	printf("\n");
	for (x=top->child;x!=NULL;x=x->next) {
		xml_print(x,depth+1,NULL);
	}
	return TRUE;
}
int xml_apply_xlate(Xml *top, int depth, Pair *xlate_inherit)
{
	Xml *x;
	Pair *xlate = xlate_inherit;
	char tmp[BFSZ];
	char nstmp[BFSZ];
	char nsp[BFSZ];

	if (top==NULL) return TRUE;
	if (top->name==NULL) return TRUE;
	// If 'xlate' has change
	xlate = xml_xlate_add(xlate,top);

	xml_translate(xlate,top->name,tmp,nstmp,nsp);
	free(top->name); top->name = strdup(tmp);
	if (top->ns) free(top->ns); 
	top->ns = strdup(nstmp);
	if(lib_strlen(nsp)>0) top->ns_prefix=strdup(nsp);
//	printf("Translated name (%s) ns (%s)\n",top->name,nstmp);

	for (x=top->child;x!=NULL;x=x->next) {
		xml_apply_xlate(x,depth+1,xlate);
	}
	if (xlate!=xlate_inherit) pair_free(&xlate);
	return TRUE;
}
int xml_free(Xml *top)
{
	Xml *x,*next;

	if (top==NULL) return TRUE;
	for (x=top->child;x!=NULL;x=next) {
		next = x->next;
		xml_free(x);
	}
	ffree(top->text);
	ffree(top->ns);
	ffree(top->ns_prefix);
	pair_free(&top->attribs);
	ffree(top->name);
	ffree(top);
	return TRUE;
}
// xml_find ns=(DAV:) thing=(resourcetype) x->name=(DAV:displayname) ns(A)
Xml *xml_find(Xml *top, char *thing, char *ns) 
{
	Xml *x;
	for (x=top;x!=NULL;x=x->next) {
		if (x->ns) if (strcmp(ns,x->ns)==0) {
			if (strcmp(thing,x->name)==0) {
				return x;
			}
		}
	}
	return NULL;
}
char *xml_get(Xml *top, char *thing) 
{
	Xml *x;
	for (x=top;x!=NULL;x=x->next) {
			if (strcmp(thing,x->name)==0) {
				return x->text;
			}
	}
	return NULL;
}
Xml *xml_find_attrib(Xml *top, char *thing, char *param) 
{
	Xml *x;
	if (top==NULL) return NULL;
	for (x=top;x!=NULL;x=x->next) {
		if (strcmp(thing,x->name)==0) {
			if (pair_findnc(x->attribs,param)) return x;
		}		
	}
	return NULL;
}
Xml *xml_find_child(Xml *top, char *thing, char *ns) 
{
	Xml *x;
	if (top==NULL) return NULL;
	for (x=top->child;x!=NULL;x=x->next) {
		if (ns==NULL || lib_strlen(ns)==0) if (strcmp(thing,x->name)==0) return x;
		if (x->ns) {
			if (ns == NULL) lib_abort();
			if (strcmp(ns,x->ns)==0) {
				if (strcmp(thing,x->name)==0) {
					return x;
				}
			}
		}
	}
	return NULL;
}
int xml_exists(Xml *top, char *thing, char *ns) 
{
	Xml *x = xml_find(top,thing,ns);
	if (x==NULL) return FALSE;
	return TRUE;
}

int xml_tobf(Xml *top, char *bf, int bfsz)
{
	Dstr *out=NULL;
	strcpy(bf,"");
	if (top==NULL) return TRUE;
	out = xml_to_text(top,0,NULL);
	ncpy(bf,dstr_ss(out),bfsz-1);
	dstr_free(&out);
	return TRUE;
}

Dstr *xml_to_text(Xml *top, int depth, Pair *xlate_inherit)
{
	Xml *x;
	Pair *p;
	Dstr *dstr=NULL;
	Dstr *d2=NULL;
	int ns=FALSE;

	dstr_new(&dstr);
	dstr_nodisk(dstr,TRUE);
	if(top->ns && lib_strlen(top->ns)>0) 
		ns=TRUE;

	dstr_printf(dstr,"<");
	if(ns) dstr_printf(dstr,"%s:",top->ns_prefix);
	dstr_printf(dstr,"%s",top->name);
	for (p=top->attribs;p!=NULL;p=p->next) {
		dstr_printf(dstr," %s=%s",p->var,p->val);
	}
	if(ns) dstr_printf(dstr," xmlns:%s=\"%s\"",top->ns_prefix,top->ns);

//	dstr_printf(dstr,">\n");
	dstr_printf(dstr,">");
	if (notblank(top->text)) dstr_printf(dstr,"%s",top->text);
//	dstr_printf(dstr,"\n");
	for (x=top->child;x!=NULL;x=x->next) {
		d2 = xml_to_text(x,depth+1,NULL);
		dstr_printf(dstr,"%s",dstr_ss(d2));
		dstr_free(&d2);
	}
	if (ns) dstr_printf(dstr,"</%s:%s>",top->ns_prefix,top->name);
	else dstr_printf(dstr,"</%s>",top->name);
	return dstr;
}
Dstr *xml_to_text_naked(Xml *top, int depth, Pair *xlate_inherit) // don't add the tag of itself...
{
	Xml *x;
	Dstr *dstr=NULL;
	Dstr *d2=NULL;
	char *s;

	dstr_new(&dstr);
	dstr_nodisk(dstr,TRUE);

	if (notblank(top->text)) dstr_printf(dstr,"%s",top->text);
	if (top->child) {
//		dstr_printf(dstr,"\n");
		for (x=top->child;x!=NULL;x=x->next) {
			d2 = xml_to_text(x,depth+1,NULL);
			s=dstr_ss(d2);
			if(x!=top->child && lib_strlen(s)>0)
				dstr_printf(dstr,"\n");
			dstr_printf(dstr,"%s",s);
			dstr_free(&d2);
		}
	}
	return dstr;
}
	
void xml_text_free(Dstr **s)
{
	dstr_free(s);
}