#ifdef __XCODE__
#include "xcode_quiet.h"
#endif


#include "miconv.h"
#define DMID 19
#include "debug.h"
#include "defines.h"
#include "lib.h"
#include "rstring.h"
#include "mime.h"
#include <stdlib.h>
#include <string.h>

/**/
struct miconv {
	iconv_t h;
	char *to;
	char *bf;
	int bfsz;
};
#ifndef TRUE
#define FALSE 0
#define TRUE (!FALSE)
#endif
/**/
char *miconv_buf(Miconv *i, int bytes, int *bfsz) {
	if (bytes < BFSZ/2) bytes = BFSZ/2;
	if (i->bfsz < bytes*2) {
		i->bf = realloc(i->bf,bytes*2);
		i->bfsz = bytes*2;
	}
	if (bfsz) *bfsz = i->bfsz-1;
	return i->bf;
}
/**/
Miconv *miconv_new_real(char *to, char *file, int line)
{
#ifdef NOICONV
	return NULL;
#else
	Miconv *i = d_callocid(1,1,sizeof(Miconv),file,line);
	i->h = (iconv_t)-1;
	i->to = strdup(to);
	return i;
#endif
}
void miconv_free_bf(Miconv **i)
{
	if ((*i)->bf) { ffree((*i)->bf); (*i)->bfsz = 0; }
}
int miconv_close(Miconv *i)
{
	if (!i) return FALSE;
#ifndef NOICONV
	if (i->h != (iconv_t)-1) { iconv_close(i->h); i->h = (iconv_t)-1; }
#endif
	miconv_free_bf(&i);
	return i->h == (iconv_t)-1;
}



void miconv_free(Miconv **i)
{
	if (!i || !*i) return ;
#ifndef NOICONV
	if ((*i)->h != (iconv_t)-1) { iconv_close((*i)->h); (*i)->h = (iconv_t)-1; }
#endif
	miconv_free_bf(i);
	ffree((*i)->to);
	ffree(*i);
}

int miconv_set(Miconv *i, char *from)
{
	if (!i) return FALSE;
#ifndef NOICONV
	if (i->h != (iconv_t)-1) iconv_close(i->h);
	i->h = iconv_open(i->to,from);
#endif
	return i->h != (iconv_t)-1;
}
int miconv_set2(Miconv *i, char *from, int iconv_ascii2iso, int iconv_translit, int iconv_ilseq)
{
	int val=-1;
	
	if (i->h != (iconv_t)-1) iconv_close(i->h);
	if (strcmpnc(from,"UTF-8")==0){
		i->h = (iconv_t)-1;
	} else {	
		if (iconv_ascii2iso && strcmpnc(from,"us-ascii")==0) 
			from="ISO-8859-1";	// Treat us-ascii as ISO-8859-1 to improve display of some emails 
		i->h = iconv_open("UTF-8",from);
		
		// Solaris X86 does not seem to have these predefined. Fix properly at some stage
#ifndef ICONV_NOTRANS
		if (iconv_translit)
			iconvctl(i->h,ICONV_SET_TRANSLITERATE,&iconv_translit);
		if (iconv_ilseq)
			iconvctl(i->h,ICONV_SET_DISCARD_ILSEQ,&iconv_ilseq);
#endif
		
	}
	return i->h != (iconv_t)-1;	
}





int miconv_isset(Miconv *i)
{
	if (!i) return FALSE;
	return i->h != (iconv_t)-1;
}

int miconv_direct(Miconv *i, char *src, size_t bytes, char **dst, size_t length)
{
	int total = (int) length;

	if (!i) return 0;
	if (i->h == (iconv_t)-1) return 0;
#ifdef NOICONV
	
	return 0;
#else
	// Warning : having "int bytes" and passing (size_t*)&bytes into iconv DOES NOT WORK on LINUX64
	if (iconv(i->h,(char**)&src,&bytes,dst,&length) == (size_t)-1) return 0;
	total -= (int)length;
	//*dst = 0;
	// removed by chrisp, this sets the destination to null, instead of adding a 'null' as intended.
	**dst = 0;
	return total;
#endif	
}

int miconv(Miconv *i, char *src, size_t bytes)
{
	char *s = src;
	char *d;
	size_t x;
	int xbytes;

	if (!i) return 0;
	if (i->h == (iconv_t)-1) return 0;
	d = miconv_buf(i,(int)bytes,&xbytes);
	x = (size_t) xbytes; 
#ifdef NOICONV
	memcpy(d,src,bytes);
	d += bytes; 
#else
	// Warning : having "int bytes" and passing (size_t*)&bytes into iconv DOES NOT WORK on LINUX64
	if (iconv(i->h,(char**)&s,&bytes,&d,&x) == (size_t)-1) return 0;
#endif
	*d = 0;
	return (int)(d - i->bf);
}

char *miconv_res(Miconv *i)
{
	if (!i) return NULL;
	return i->bf;
}

iconv_t miconv_handle(Miconv *i)
{
	if (!i) return (iconv_t)-1;
	return i->h;
}

extern const unsigned char charmap[];
char *miconv_header_decode(char *header, char *res, int bfsz)
{
	Miconv *i = NULL;
	char *p,*e,*s,*r = res;
	char tmp[BFSZ];
	char bf[BFSZ];
	int n,left;

	if (!header) goto err;

	left = bfsz;
	for(p = ncpy(tmp,header,BFSZ-1); p; ) {
		e = strstr(p,"=?");
		if (e) *e = 0;

		ncpy(r,p,left-1);
		n = lib_strlen(p);
		left -= n;
		r += n;

		if (!e) break;
		//=?iso-8859-1?q?...?=
		e += 2;

		//iso-8859-1?q?...?=
		s = strchr(e,'?');
		if (!s) goto err;
		*s++ = 0;

		i = miconv_new("UTF-8");
		if (!miconv_set(i,e)) {
			//evil hack?
			p = strstr(e,"-i");
			if (p) { *p = 0; miconv_set(i,e); }
			if (!miconv_isset(i)) goto err;
		}

		//q?...?=
		p = strchr(s,'?');
		if (!p) goto err;
		*p++ = 0;

		//...?=
		e = strstr(p,"?=");
		if (!e) goto err;
		*e = 0;

		if (charmap[*s] == charmap['B']) {
			n = mime_decode_base64(p,bf,BFSZ-1);
		}
		if (charmap[*s] == charmap['Q']) {
			n = mime_decode_quoted(p,bf,BFSZ-1);
			for(s = bf; s-bf < n; s++) {
				if (*s == '_') *s = ' ';
			}
		}
		bf[n] = 0;
		s = bf;

		if (!miconv(i,bf,n)) goto err;

		// Added by chrisp, so result is coppied to destination, doh!
//		i->bf[n] = 0;
		ncpy(r,i->bf,left-1);
		n = lib_strlen(r);
		left -= n;
		r += n;
		// end addition.
		miconv_free(&i);

		p = e + 2;
	}
	*r = 0;
	return res;
err:
	miconv_free(&i);
	if (header) ncpy(res,header,bfsz-1);
	else *res = 0;
	return res;
}
/**/
