/***************************************************************************
                           ciconv.cpp  -  DCLib C++ iconv wrapper
                             -------------------
    begin                : Thu Sep 20 2007
    copyright            : (C) 2007 by Edward Sheldrake
    email                : ejs1920@yahoo.co.uk
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "ciconv.h"

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#ifndef ICONV_CONST
#define ICONV_CONST
#endif

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

/** calls iconv_open */
CIconv::CIconv( const CString & from, const CString & to )
{
	if ( from == to )
	{
		disabled = true;
	}
	else
	{
		cd = iconv_open( to.Data(), from.Data() );
		if ( cd == (iconv_t)(-1) )
		{
			perror( "CIconv iconv_open" );
			printf( "CIconv: encoding disabled - iconv_open failed\n" );
			disabled = true;
		}
		else
		{
			disabled = false;
		}
	}
}

/** calls iconv_close */
CIconv::~CIconv()
{
	if ( disabled == false )
	{
		iconv_close( cd );
	}
}

/** calls iconv */
CString CIconv::encode( const char * orig, const size_t origlen )
{
	if ( orig == 0 || origlen == 0 )
	{
		return CString();
	}
	
	CString result;
	size_t retval;
	
	size_t inleft = origlen;
	
	size_t outleft;
	if ( inleft < 1024 )
	{
		outleft = inleft * 4;
	}
	else
	{
		outleft = 4096;
	}
	
	const char * inbuf = orig;
	
	// allocate an extra byte to ensure the data is \0 terminated
	char * outbuf = (char*) calloc( 1, outleft + 1 );
	
	char * outstart = outbuf;
	
	while ( inleft > 0 )
	{
		retval = iconv( cd, (ICONV_CONST char **)&inbuf, &inleft, &outbuf, &outleft );
	
		if ( retval == (size_t) -1 )
		{
			// handle invalid multibyte sequence
			if ( errno == EILSEQ )
			{
				outstart[outbuf - outstart] = '_';
				
				inbuf++;
				inleft--;
				
				outbuf++;
				outleft--;
			}
			else if ( errno == E2BIG ) // not enough room in output buffer for next char
			{
				// CString += uses strlen to compute the length
				// which will be correct since we zero filled the memory
				// with calloc, so we don't need to compute any sizes here
				result += outstart;
				free(outstart);
				outleft = 4096;
				outbuf = (char*) calloc( 1, outleft + 1 );
				outstart = outbuf;
			}
			else
			{
				perror("CIconv::encode");
				
				free(outstart);
				return orig;
			}
		}
	}
		
	result += outstart;
	free(outstart);
	
	//printf( "orig=\"%s\" len=%d\nresult=\"%s\" len=%d\n", orig.Data(), orig.Length(), result.Data(), result.Length() );
	
	return result;
	
}
