The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#include "tstring.h"

MODULE = Audio::TagLib		PACKAGE = Audio::TagLib::String
PROTOTYPES: ENABLE


################################################################
# 
# PUBLIC MEMBER FUNCTIONS
# 
################################################################

TagLib::String * 
TagLib::String::new(...)
PROTOTYPE: ;$$
PREINIT:
        // Patch Festus-02 rt.cpan.org #79474
	// char *encode, *fromcode; 
	const char *encode, *fromcode;
	enum TagLib::String::Type t;
	bool is_copy_from_string = TRUE;
	iconv_t codec;
	char *inbuf, *outbuf;
	size_t inlen, outlen, utf8len;
	char *utf8;
	char *errmsg;
CODE:
	/*!
	 * String()
	 * String(const String &s)
	 * String(const std::string &s,Type t=Latin1)
	 * String(const wstring &s,Type t=UTF16BE)
	 * String(const wchar_t *s,Type t=UTF16BE)
	 * String(char c,Type t=Latin1)
	 * String(wchar_t c,Type t=Latin1)
	 * String(const char *s,Type t=Latin1)
	 * String(const ByteVector &v,Type t=Latin1)
	 * 
	 * not all the constructors to be used
	 * from the use point of view
	 * just five types are concerned
	 * a)new empty instance
	 * b)copy from a String instance
	 * c)copy from a ByteVector instance
	 * d)construct from a 8bit-based encode char or string
	 * e)construct from a non 8bit-based encode char or string
	 * 
	 * 8bit-based     => Latin1 or UTF8
	 * non 8bit-based => UTF16BE or UTF16LE or UTF16
	 */
	switch(items) {
	case 3:
		/*!
		 * encode is specified by user 
		 * which means
		 * the string param should be of the specified encode format
		 */
		if(sv_isobject(ST(1)) && 
			sv_derived_from(ST(1), "Audio::TagLib::ByteVector")) {
			/* encode must be 8bit-based */
			encode = SvPV_nolen(ST(2));
			if(strncasecmp(encode, "Latin1", 6) == 0)
				t = TagLib::String::Latin1;
			else if(strncasecmp(encode, "UTF8", 4) == 0) 
				t = TagLib::String::UTF8;
			else 
				croak("encode must be Latin1 or UTF8 for ByteVector");
			RETVAL = new TagLib::String(*INT2PTR(
				TagLib::ByteVector *,SvIV(SvRV(ST(1)))),t);
			is_copy_from_string = FALSE;
		} else
			encode = SvPV_nolen(ST(2));
		break;
	case 2:
		/*!
		 * choose specific encode according to string format
		 * ASCII or UTF8 accoring to UTF8 flag
		 * for any other code formats, user MUST specify encode
		 */
		if(sv_isobject(ST(1))) {
			if(sv_derived_from(ST(1), "Audio::TagLib::String")) {
				RETVAL = new TagLib::String(*INT2PTR(
					TagLib::String *,SvIV(SvRV(ST(1)))));
				is_copy_from_string = FALSE;
			} else if(sv_derived_from(ST(1), 
				"Audio::TagLib::ByteVector")) {
				RETVAL = new TagLib::String(*INT2PTR(
					TagLib::ByteVector *,SvIV(SvRV(ST(1)))));
				is_copy_from_string = FALSE;
			}
		} else if(SvUTF8(ST(1))) {
				encode = "UTF8";
		} else { 
				/* default encode */
				encode = "Latin1";
		}
		break;
	default:
		/* items == 1 */
		RETVAL = new TagLib::String();
		is_copy_from_string = FALSE;
	}
	if(is_copy_from_string) {
		if(strncasecmp(encode, "Latin1", 6) == 0) {
			t = TagLib::String::Latin1;
			fromcode = "Latin1";
		} else if(strncasecmp(encode, "UTF8", 4) == 0) {
			t = TagLib::String::UTF8;
			fromcode = "UTF8";
		} else if(strncasecmp(encode, "UTF16BE", 7) == 0) {
			t = TagLib::String::UTF16BE;
			fromcode = "UTF16BE";
		} else if(strncasecmp(encode, "UTF16LE", 7) == 0) {
			t = TagLib::String::UTF16LE;
			fromcode = "UTF16LE";
		} else if(strncasecmp(encode, "UTF16", 5) == 0) {
			t = TagLib::String::UTF16;
			fromcode = "UTF16";
		} else 
			croak("invalid encode in TagLib::String::new()");
		/* process data */
		switch(t) {
		case TagLib::String::Latin1:
			//printf("Latin1: %s\n", SvPV_nolen(ST(1)));
			RETVAL = sv_len(ST(1)) == 1 ? 
				new TagLib::String(*SvPV_nolen(ST(1))) : 
				new TagLib::String(SvPV_nolen(ST(1)));
			break;
		case TagLib::String::UTF8:
			RETVAL = new TagLib::String(SvPVX(ST(1)), t);
			break;
		default:
			/* any other encodings converted to utf8 */
			//sv_dump(ST(1));
			//printf("fromcode = %s\n", fromcode);
                        // Patch Festus-02 tstring.xs:
			// if(!(codec = iconv_open("UTF8", fromcode))) 
			const char *from_code;
            if(strncasecmp(fromcode, "UTF8", 4) == 0) {
                from_code = "UTF-8";
            } else if(strncasecmp(fromcode, "UTF16BE", 7) == 0) {
                from_code = "UTF-16BE";
            } else if(strncasecmp(fromcode, "UTF16LE", 7) == 0) {
                from_code = "UTF-16LE";
            } else if(strncasecmp(fromcode, "UTF16", 5) == 0) {
                from_code = "UTF-16";
            } else
                from_code = fromcode;
			if(!(codec = iconv_open("UTF-8", from_code)))
                        // end of Festus-02
				croak("iconv_open failed, check your encode");
			/* inlen MUST be the extract byte length of string */
			/* the terminal '\0' should NOT be included in length */
			inlen  = SvCUR(ST(1));
			utf8len = outlen = (inlen/1024+1)*1024;
			utf8 = new char[outlen];
			if(!utf8)
				croak("can't allocate memory for string");
			inbuf  = SvPVX(ST(1));
			outbuf = utf8;
			iconv(codec, NULL, NULL, NULL, NULL);
			if(iconv_wrap(codec, &inbuf, &inlen, &outbuf, &outlen) == -1) {
				sprintf(errmsg, "error converting from %s to UTF8", 
					fromcode);
				delete [] utf8;
				iconv_close(codec);
				croak(errmsg);
 			}
			//printf("inlen = %d, outlen = %d\n", inlen, outlen);
			/* add terminating '\0' to the end of output string */
			utf8[utf8len - outlen] = '\0';
			//for(int i = 0; i < strlen(utf8)+1; i++) {
			//	printf("%d: %#d\n", i, utf8[i]);
			//}
			iconv_close(codec);
			RETVAL = new TagLib::String(utf8, TagLib::String::UTF8);
			delete [] utf8;
		}
	}
OUTPUT:
	RETVAL

void
TagLib::String::DESTROY()
CODE:
	/* skip TagLib::String::null */
	if(THIS != &(TagLib::String::null) && !SvREADONLY(SvRV(ST(0))))
		delete THIS;

################################################################
# 
# std::string to8Bit(bool unicode=false) const
# 
# return a PV instead
# set UTF8 flag accordingly
# 
################################################################
SV * 
TagLib::String::to8Bit(unicode = false)
	bool unicode
INIT:
	std::string string = THIS->to8Bit(unicode);
CODE:
	RETVAL = newSVpv(string.c_str(), 0);
	if(sv_len_utf8(RETVAL) != sv_len(RETVAL))
		SvUTF8_on(RETVAL);
OUTPUT:
	RETVAL

SV * 
TagLib::String::toCString(unicode = false) 
	bool unicode
INIT:
	const char *c_str = THIS->toCString(unicode);
CODE:
	RETVAL = newSVpv(c_str, 0);
	if(sv_len_utf8(RETVAL) != sv_len(RETVAL))
		SvUTF8_on(RETVAL);
OUTPUT:
	RETVAL

TagLib::String::Iterator * 
TagLib::String::begin()
CODE:
	RETVAL = new TagLib::String::Iterator(THIS->begin());
OUTPUT:
	RETVAL

################################################################
# 
# ConstIterator begin() const
# not exported
# 
################################################################

TagLib::String::Iterator * 
TagLib::String::end()
CODE:
	RETVAL = new TagLib::String::Iterator(THIS->end());
OUTPUT:
	RETVAL

################################################################
# 
# ConstIterator end() const
# not exported
# 
################################################################

int 
TagLib::String::find(s, offset = 0)
	TagLib::String * s
	int offset
CODE:
	RETVAL = THIS->find(*s, offset);
OUTPUT:
	RETVAL

TagLib::String * 
TagLib::String::substr(position, n = 0xffffffff)
	unsigned int position
	unsigned int n
CODE:
	RETVAL = new TagLib::String(THIS->substr(position, n));
OUTPUT:
	RETVAL

void 
TagLib::String::append(s)
	TagLib::String * s
CODE:
	(void)THIS->append(*s);
	XSRETURN(1);

TagLib::String * 
TagLib::String::upper()
CODE:
	RETVAL = new TagLib::String(THIS->upper());
OUTPUT:
	RETVAL

unsigned int 
TagLib::String::size()
CODE:
	RETVAL = THIS->size();
OUTPUT:
	RETVAL

bool 
TagLib::String::isEmpty()
CODE:
	RETVAL = THIS->isEmpty();
OUTPUT:
	RETVAL

bool 
TagLib::String::isNull()
CODE:
	RETVAL = THIS->isNull();
OUTPUT:
	RETVAL

TagLib::ByteVector * 
TagLib::String::data(t)
	TagLib::String::Type t
PREINIT:
	TagLib::ByteVector tmp;
CODE:
	tmp = THIS->data(t);
	//char *s = tmp.data();
	//for(int i = 0; i < 4; i++) {
	//	printf("%d: %c\n", i, s[i]);
	//}
	RETVAL = new TagLib::ByteVector(tmp);
OUTPUT:
	RETVAL

int 
TagLib::String::toInt()
CODE:
	RETVAL = THIS->toInt();
OUTPUT:
	RETVAL

TagLib::String * 
TagLib::String::stripWhiteSpace()
CODE:
	RETVAL = new TagLib::String(THIS->stripWhiteSpace());
OUTPUT:
	RETVAL

################################################################
# 
# implement wchar & operator[](int i)
# 
################################################################
SV * 
TagLib::String::getChar(i)
	int i
PREINIT:
	iconv_t codec;
	char mb[8];
	char *inbuf, *outbuf;
	size_t inlen, outlen;
INIT:
	outlen = 8;
	wchar_t & wc = THIS->operator[](i);
CODE:
	inbuf = (char *)&wc;
	outbuf = mb;
	inlen = sizeof(wchar_t)/sizeof(char);
    /* Festus Hagen 1.62.fh8 - [rt.cpan.org #82529] # */
    /* WCHAR_T is system dependent. */
    /* Taglib uses UTF-16BE internally */
    /* UTF-16LE is what Perl uses internally? */
    /*codec = iconv_open("UTF-8", "WCHAR_T");*/
    codec = iconv_open("UTF-8", "UTF-16LE");
	if(!codec)
		croak("iconv_open failed in String::_toArray");
	iconv(codec, NULL, NULL, NULL, NULL);
	//printf("inlen = %d, outlen = %d\n", inlen, outlen);
	if(iconv_wrap(codec, &inbuf, &inlen, &outbuf, &outlen) == -1)
		croak("iconv failed in String::_toArray");
	iconv_close(codec);
	mb[8-outlen] = '\0';
	//printf("here: %s\n", mb);
	RETVAL = newSVpv(mb, 0);
	/* set UTF8 flag accordingly */
	sv_utf8_decode(RETVAL);
OUTPUT:
	RETVAL

################################################################
# 
# const wchar & operator[](int i) const
# not exported
# 
################################################################

bool 
TagLib::String::_equal(s, swap = NULL)
	TagLib::String * s
	char * swap
CODE:
	RETVAL = THIS->operator==(*s);
OUTPUT:
	RETVAL

################################################################
# 
# implement 
# String & operator+=(const String &s)
# 
# String & operator+=(const char *s)
# String & operator+=(char c)
# String & operator+=(const wchar_t *s)
# String & operator+=(wchar_t c)
# 
################################################################
TagLib::String * 
TagLib::String::_append(...)
PROTOTYPE: $
CODE:
	if(sv_isobject(ST(1)) && sv_derived_from(ST(1), "Audio::TagLib::String"))
		RETVAL = new TagLib::String(THIS->operator+=(
			*INT2PTR(TagLib::String *, SvIV(SvRV(ST(1))))));
	else if(SvPOK(ST(1))) {
		/*!
		 * in perl the string might be encoded as either Latin1
		 * or UTF-8
		 * on the other side, operator+=(const char *s) doesn't 
		 * specify the encoding of param s
		 * furture more, the returned String object is allcated 
		 * on stack
		 * 
		 * so in this case, the appended string is initially stored 
		 * in a string object, which formats the string to the same 
		 * internal encode of String class
		 * then copy the data to a new object which is allocated on 
		 * heap
		 */
		RETVAL = new TagLib::String(THIS->append(TagLib::String(
			SvPVutf8_nolen(ST(1)), TagLib::String::UTF8)));
	} else
		croak("ST(1) is not of type TagLib::String or SV");
OUTPUT:
	RETVAL

################################################################
# implement
# String & operator=(const String &s)
# String & operator=(const ByteVector &v)
# 
# String & operator=(const std::string &s)
# String & operator=(const wstring &s)
# String & operator=(const wchar_t *s)
# String & operator=(char c)
# String & operator=(wchar_t c)
# String & operator=(const char *s)
# 
################################################################
void 
TagLib::String::copy(...)
PROTOTYPE: $
PREINIT:
	TagLib::String * s;
	TagLib::ByteVector * v;
	char * c;
PPCODE:
	if(sv_isobject(ST(1))) {
		if(sv_derived_from(ST(1), "Audio::TagLib::String")) {
			s = INT2PTR(TagLib::String *, SvIV(SvRV(ST(1))));
			(void)THIS->operator=(*s);
		} else if(sv_derived_from(ST(1), "Audio::TagLib::ByteVector")) {
			v = INT2PTR(TagLib::ByteVector *, SvIV(SvRV(ST(1))));
			(void)THIS->operator=(*v);
		}
	} else if(SvPOK(ST(1))) {
		if(SvUTF8(ST(1))) {
			c = SvPVX(ST(1));
			if(SvCUR(ST(1)) == 1) {
				TagLib::String tmp1(*c, TagLib::String::UTF8);
				(void)THIS->operator=(tmp1);
			} else {
				TagLib::String tmp2(c, TagLib::String::UTF8);
				(void)THIS->operator=(tmp2);
			}
		} else {
			c = SvPVX(ST(1));
			if(SvCUR(ST(1)) == 1) {
				TagLib::String tmp3(*c, TagLib::String::Latin1);
				(void)THIS->operator=(tmp3);
			} else {
				TagLib::String tmp4(c, TagLib::String::Latin1);
				(void)THIS->operator=(tmp4);
			}
		}
	} else
		croak("ST(1) is not of type String/ByteVector or a valid string");
	XSRETURN(1);

bool 
TagLib::String::_lessThan(s, swap = NULL)
	TagLib::String * s
	char * swap
CODE:
	RETVAL = THIS->operator<(*s);
OUTPUT:
	RETVAL

################################################################
# 
# STATIC PUBLIC MEMBER FUNCTIONS
# 
################################################################
static TagLib::String * 
TagLib::String::number(n)
	int n
CODE:
	RETVAL = new TagLib::String(TagLib::String::number(n));
OUTPUT:
	RETVAL

################################################################
# 
# STATIC PUBLIC ATTRIBUTES
# 
################################################################
static TagLib::String * 
TagLib::String::null()
CODE:
	RETVAL = &(TagLib::String::null);
OUTPUT:
	RETVAL

################################################################
# 
# PROTECTED MEMBER FUNCTIONS
# 
# void detach()
# not exported
# 
################################################################

################################################################
# 
# SPECIAL MEMBER FUNCTIONS
# 
# for special use in Perl
# 
################################################################

################################################################
# 
# return the memory address of instance 
# 
################################################################
void 
TagLib::String::_memoAddress()
PREINIT:
	char strAddress[512];
PPCODE:
	sprintf(strAddress, "%#u", THIS);
	ST(0) = newSVpv(strAddress, 0);
	sv_2mortal(ST(0));
	XSRETURN(1);