#include "acl_stdafx.hpp"

#if !defined(HAVE_H_ICONV) && !defined(ANDROID)
# define HAVE_H_ICONV
#endif

#ifdef	HAVE_H_ICONV
# ifndef USE_WIN_ICONV
#  include <iconv.h>
# endif
#endif

#ifndef ACL_PREPARE_COMPILE
#include <string.h>
#include "acl_cpp/stdlib/string.hpp"
#include "acl_cpp/stdlib/util.hpp"
#include "acl_cpp/stdlib/log.hpp"
#include "acl_cpp/stdlib/charset_conv.hpp"
#endif

#define SCOPY ACL_SAFE_STRNCPY

#ifdef HAVE_H_ICONV
static const char UTF8_HEADER[] = { (char) 0xEF, (char) 0xBB, (char) 0xBF, (char) 0x00 };
#endif

#ifdef HAVE_H_ICONV
# ifdef USE_WIN_ICONV
#  include "internal/win_iconv.hpp"

#  define __iconv_open    iconv_open
#  define __iconv_close   iconv_close
#  define __iconv         iconv
# elif defined(ACL_WINDOWS)

typedef iconv_t (*iconv_open_fn)(const char*, const char*);
typedef int     (*iconv_close_fn)(iconv_t);
typedef size_t  (*iconv_fn)(iconv_t, const char**, size_t*, char**, size_t*, int*);
typedef int     (*iconvctl_fn)(iconv_t, int, void*);

static iconv_open_fn __iconv_open = NULL;
static iconv_close_fn __iconv_close = NULL;
static iconv_fn __iconv = NULL;
static iconvctl_fn __iconvctl = NULL;

static acl_pthread_once_t __iconv_once = ACL_PTHREAD_ONCE_INIT;
static ACL_DLL_HANDLE __iconv_dll = NULL;

// 程序退出时释放动态加载的 iconv.dll 库
#ifndef HAVE_NO_ATEXIT
static void __iconv_dll_unload(void)
{
	if (__iconv_dll != NULL) {
		acl_dlclose(__iconv_dll);
		__iconv_dll = NULL;
		logger("iconv.dll unload ok");
	}
}
#endif

// 动态加载 iconv.dll 库
static void __iconv_dll_load(void)
{
	const char* path = "iconv.dll";

	if (__iconv_dll != NULL) {
		logger_fatal("__iconv_dll not null");
	}

	__iconv_dll = acl_dlopen(path);
	if (__iconv_dll == NULL) {
		logger_fatal("load %s error: %s", path, last_serror());
	}

	__iconv_open = (iconv_open_fn) acl_dlsym(__iconv_dll, "libiconv_open");
	if (__iconv_open == NULL) {
		logger_fatal("load iconv_open from %s error: %s", path, last_serror());
	}

	__iconv_close = (iconv_close_fn) acl_dlsym(__iconv_dll, "libiconv_close");
	if (__iconv_close == NULL) {
		logger_fatal("load iconv_close from %s error: %s", path, last_serror());
	}

	__iconv = (iconv_fn) acl_dlsym(__iconv_dll, "libiconv");
	if (__iconv == NULL) {
		logger_fatal("load iconv from %s error: %s", path, last_serror());
	}

	__iconvctl = (iconvctl_fn) acl_dlsym(__iconv_dll, "libiconvctl");
	if (__iconvctl == NULL) {
		logger_fatal("load iconvctl from %s error: %s", path, last_serror());
	}

	logger("iconv.dll loaded");
#ifndef HAVE_NO_ATEXIT
	atexit(__iconv_dll_unload);
#endif
}

# else
#  define __iconv_open    iconv_open
#  define __iconv_close   iconv_close
#  define __iconv         iconv
#  define __iconvctl      iconvctl
# endif
#endif

namespace acl {

#define EQ(x, y) !strcasecmp((x), (y))

charset_conv::charset_conv()
: m_addInvalid(true)
, m_errmsg("ok")
, m_pBuf(NULL)
{
	m_fromCharset[0] = 0;
	m_toCharset[0] = 0;
#ifdef  HAVE_H_ICONV
	m_iconv = (iconv_t) -1;
	m_pInBuf = NULL;
	m_pOutBuf = NULL;
# ifdef ACL_WINDOWS 
#  ifndef USE_WIN_ICONV
	acl_pthread_once(&__iconv_once, __iconv_dll_load);
#  endif
# endif
#else
	(void) m_addInvalid;
	(void) m_errmsg;
	(void) m_pBuf;
	(void) m_iconv;
	(void) m_pInBuf;
	(void) m_pOutBuf;
	(void) m_pUtf8Pre;
#endif
}

charset_conv::~charset_conv()
{
#ifdef  HAVE_H_ICONV
	if (m_iconv != (iconv_t) -1) {
		__iconv_close(m_iconv);
	} if (m_pInBuf) {
		acl_vstring_free(m_pInBuf);
	}
	if (m_pOutBuf) {
		acl_vstring_free(m_pOutBuf);
	}
#endif
	delete m_pBuf;
}

void charset_conv::set_add_invalid(bool onoff)
{
	m_addInvalid = onoff;
}

const char* charset_conv::serror() const
{
	return (m_errmsg.c_str());
}

void charset_conv::reset()
{
	m_errmsg = "ok";

#ifdef  HAVE_H_ICONV
	if (m_iconv != (iconv_t) -1) {
		__iconv_close(m_iconv);
		m_iconv = (iconv_t) - 1;
	}
	m_fromCharset[0] = 0;
	m_toCharset[0]   = 0;
#endif
}

bool charset_conv::convert(const char* fromCharset, const char* toCharset,
	const char* in, size_t n, acl::string* out)
{
	if (fromCharset == NULL || toCharset == NULL
		|| in == NULL || n == 0 || out == NULL) {

		m_errmsg = "params invalid";
		return false;
	}

	if (!update_begin(fromCharset, toCharset)) {
		reset();
		return false;
	}
	if (!update(in, n, out)) {
		reset();
		return false;
	}
	update_finish(out);
	return true;
}

#define	STR	acl_vstring_str
#define	LEN	ACL_VSTRING_LEN
#define	SIZE	ACL_VSTRING_SIZE
#define EQ2(x, y) (((x) == NULL && (y) == NULL)  \
		|| ((x) != NULL && (y) != NULL && !strcasecmp((x), (y))))

bool charset_conv::update_begin(const char* fromCharset,
	const char* toCharset)
{
#ifdef  HAVE_H_ICONV
	if (EQ2(fromCharset, toCharset)) {
		return true;
	}

	if (fromCharset == NULL || toCharset == NULL) {
		if (m_iconv != (iconv_t) -1) {
			return true;
		}

		logger_error("input invalid, from: %s, to: %s, m_conv: %s",
			fromCharset ? fromCharset : "null",
			toCharset ? toCharset : "null",
			m_iconv == (iconv_t) -1 ? "invalid" : "valud");
		m_errmsg = "input invalid";
		return false;
	}

	// 如果源是 UTF-8 编码，则 m_pTuf8Pre 从 UTF8_HEADER 头部第
	// 一个字节开始进行匹配，否则从最后一个字节 '\0' 开始匹配，
	// 即跳过 UTF-8 头部匹配过程
	if (EQ(fromCharset, "utf-8") || EQ(fromCharset, "utf8")) {
		m_pUtf8Pre = UTF8_HEADER;
	} else {
		m_pUtf8Pre = &UTF8_HEADER[3];
	}

	if (m_iconv != (iconv_t) -1
		&& EQ(m_fromCharset, fromCharset)
		&& EQ(m_toCharset, toCharset)) {

		return true;
	}

	SCOPY(m_fromCharset, fromCharset, sizeof(m_fromCharset));
	SCOPY(m_toCharset, toCharset, sizeof(m_toCharset));

	if (m_iconv != (iconv_t) -1) {
		__iconv_close(m_iconv);
	}
	m_iconv = __iconv_open(toCharset, fromCharset);
	if (m_iconv == (iconv_t) -1) {
		logger_error("iconv_open(%s, %s) error(%s)",
			toCharset, fromCharset, last_serror());
		m_errmsg.format("iconv_open(%s, %s) error(%s)",
			toCharset, fromCharset, last_serror());
		return false;
	} else {
#ifdef ACL_WINDOWS
# ifndef USE_WIN_ICONV
		int  n = 1;
		__iconvctl(m_iconv, ICONV_TRIVIALP, &n);

		n = 1;
		__iconvctl(m_iconv, ICONV_SET_DISCARD_ILSEQ, &n);

		n = 1;
		__iconvctl(m_iconv, ICONV_SET_TRANSLITERATE, &n);
# endif // USE_WIN_ICONV
#endif

		char  *pi = NULL, *po = NULL;
		size_t zi = 0, zo = 0;
#ifdef	ACL_WINDOWS
# ifdef USE_WIN_ICONV
		__iconv(m_iconv, (const char**) &pi, &zi, &po, &zo);
# else
		__iconv(m_iconv, (const char**) &pi, &zi, &po, &zo, NULL);
# endif // USE_WIN_ICONV
#elif defined(ACL_SUNOS5)
		__iconv(m_iconv, (const char**) &pi, &zi, &po, &zo);
#elif defined(ACL_FREEBSD)
		__iconv(m_iconv, (const char**) &pi, &zi, &po, &zo);
#else
		__iconv(m_iconv, &pi, &zi, &po, &zo);
#endif
		return true;
	}
#else
	logger_error("no iconv lib");
	m_errmsg = "no iconv lib";
	return false;
#endif
}

bool charset_conv::update(const char* in, size_t len, acl::string* out)
{
#ifdef  HAVE_H_ICONV
	if (in == NULL) {
		logger_fatal("in null");
	}
	if (out == NULL) {
		logger_fatal("out null");
	}

	if (EQ(m_fromCharset, m_toCharset)) {
		out->append(in, len);
		return true;
	}

	if (m_iconv == (iconv_t) -1) {
		logger_error("m_iconv invalid");
		m_errmsg = "m_iconv invalid";
		return false;
	}

	// 去掉有些 UTF-8 文档中开始的 UTF-8 引导符
	if (*m_pUtf8Pre) {
		while (len > 0) {
			if (*m_pUtf8Pre == 0x00) {
				break;
			} else if (*m_pUtf8Pre != *in) {
				// 必须使 UTF-8 前缀失效
				m_pUtf8Pre = &UTF8_HEADER[3];
				break;
			}
			m_pUtf8Pre++;
			in++;
			len--;
		}
	}

	if (len == 0) {
		return true;
	}

	if (m_pInBuf == NULL) {
		m_pInBuf = acl_vstring_alloc(len);
	}

	if (m_pOutBuf == NULL) {
		m_pOutBuf = acl_vstring_alloc(len);
	} else {
		ACL_VSTRING_SPACE(m_pOutBuf, (int) len);
	}

	// 先将输入数据进行缓冲
	if (*m_pUtf8Pre && m_pUtf8Pre - UTF8_HEADER > 0) {
		acl_vstring_memcpy(m_pInBuf, UTF8_HEADER,
			m_pUtf8Pre - UTF8_HEADER);
	}
	acl_vstring_memcat(m_pInBuf, in, len);
	ACL_VSTRING_TERMINATE(m_pInBuf);

	char  *pIn, *pOut;
	size_t ret, nIn, nOut;

	while (true) {
		nIn = LEN(m_pInBuf);
		if (nIn == 0) {
			break;
		}
		pIn  = STR(m_pInBuf);
		pOut = STR(m_pOutBuf);
		nOut = SIZE(m_pOutBuf);

#ifdef	ACL_WINDOWS
# ifdef USE_WIN_ICONV
		ret = __iconv(m_iconv, (const char**) &pIn, &nIn,
				&pOut, &nOut);
# else
		int   err;
		ret = __iconv(m_iconv, (const char**) &pIn, &nIn,
				&pOut, &nOut, &err);
		errno = err;
# endif // USE_WIN_ICONV
#elif defined(ACL_SUNOS5)
		ret = __iconv(m_iconv, (const char**) &pIn, &nIn, &pOut, &nOut);
#elif defined(ACL_FREEBSD)
		ret = __iconv(m_iconv, (const char**) &pIn, &nIn, &pOut, &nOut);
#else
		ret = __iconv(m_iconv, &pIn, &nIn, &pOut, &nOut);
#endif

		if (ret != (size_t) -1) {
			if ((ret = SIZE(m_pOutBuf) - nOut) > 0) {
				out->append(STR(m_pOutBuf), ret);
			} else { // xxx
				out->append(in, len);
			}
			ACL_VSTRING_RESET(m_pInBuf);
			break;
		} else if (errno == E2BIG) {
			if ((ret = SIZE(m_pOutBuf) - nOut) > 0) {
				out->append(STR(m_pOutBuf), ret);
			}
			if (pIn > STR(m_pInBuf) && nIn < LEN(m_pInBuf)) {
				acl_vstring_memmove(m_pInBuf, pIn, nIn);
			}
			// 扩大内存空间
			ACL_VSTRING_SPACE(m_pOutBuf, SIZE(m_pOutBuf) * 2);
			continue;
		} else if (errno == EILSEQ) {
			char  *pi = NULL, *po = NULL;
			size_t zi = 0, zo = 0;

			// 重置状态, 似乎也没啥用处
#ifdef	ACL_WINDOWS
# ifdef USE_WIN_ICONV
			__iconv(m_iconv, (const char**) &pi, &zi, &po, &zo);
# else
			__iconv(m_iconv, (const char**) &pi, &zi, &po, &zo, NULL);
# endif
#elif defined(ACL_SUNOS5)
			__iconv(m_iconv, (const char**) &pi, &zi, &po, &zo);
#elif defined(ACL_FREEBSD)
			__iconv(m_iconv, (const char**) &pi, &zi, &po, &zo);
#else
			__iconv(m_iconv, &pi, &zi, &po, &zo);
#endif

			// 遇到无效的多字节序列，pIn 指向第一个无效的位置

			// 先拷贝已经转换的数据
			if ((ret = SIZE(m_pOutBuf) - nOut) > 0) {
				out->append(STR(m_pOutBuf), ret);
			}

			if (nIn == 0) {
				ACL_VSTRING_RESET(m_pInBuf);
				break;
			}

			acl_assert(pIn >= STR(m_pInBuf));

			// 是否跳过无效字节?
			if (m_addInvalid) {
				(*out) += (char)(*pIn); // 直接拷贝无效字节
			}
			nIn--;
			pIn++;
			if (nIn > 0) {
				acl_vstring_memmove(m_pInBuf, pIn, nIn);
			} else {
				ACL_VSTRING_RESET(m_pInBuf);
			}
		} else if (errno == EINVAL) {
			char  *pi = NULL, *po = NULL;
			size_t zi = 0, zo = 0;

			// 重置状态, 似乎也没啥用处
#ifdef	ACL_WINDOWS
# ifdef USE_WIN_ICONV
			__iconv(m_iconv, (const char**) &pi, &zi, &po, &zo);
# else
			__iconv(m_iconv, (const char**) &pi, &zi, &po, &zo, NULL);
# endif // USE_WIN_ICONV
#elif defined(ACL_SUNOS5)
			__iconv(m_iconv, (const char**) &pi, &zi, &po, &zo);
#elif defined(ACL_FREEBSD)
			__iconv(m_iconv, (const char**) &pi, &zi, &po, &zo);
#else
			__iconv(m_iconv, &pi, &zi, &po, &zo);
#endif

			// 输入的多字节序列不完整，pIn 指向该不完整的位置

			// 先拷贝已经转换的数据
			if ((ret = SIZE(m_pOutBuf) - nOut) > 0) {
				out->append(STR(m_pOutBuf), ret);
			}

			// 移动数据，将未转换的数据移至缓冲区起始位置
			if (nIn > 0) {
				acl_vstring_memmove(m_pInBuf, pIn, nIn);
			} else {
				ACL_VSTRING_RESET(m_pInBuf);
			}
			break;
		} else if (LEN(m_pInBuf) > 0) {
			// 如果遇到了无效的字符集，根据设置的标志位
			// 决定是否直接拷贝
			if (m_addInvalid) {
				out->append(STR(m_pInBuf), LEN(m_pInBuf));
				ACL_VSTRING_RESET(m_pInBuf);
			}
			break;
		} else {
			break;
		}
	}

	return true;
#else
	(void) in;
	(void) len;
	(void) out;
	logger_error("no iconv lib");
	m_errmsg = "no iconv lib";
	return false;
#endif
}

void charset_conv::update_finish(acl::string* out)
{
#ifdef  HAVE_H_ICONV
	if (m_pInBuf && LEN(m_pInBuf) > 0 && m_addInvalid) {
		out->append(STR(m_pInBuf), LEN(m_pInBuf));
		ACL_VSTRING_RESET(m_pInBuf);
	}
#endif
}

int charset_conv::push_pop(const char* in, size_t len,
	acl::string* out, size_t max /* = 0 */)
{
	if (m_pBuf == NULL) {
		m_pBuf = NEW acl::string(1024);
	}

	if (in && len > 0 && !update(in, len, m_pBuf)) {
		return -1;
	}

	len = m_pBuf->length();
	if (len == 0) {
		return 0;
	}

	size_t n;
	if (max > 0) {
		n = max > len ? len : max;
	} else {
		n = len;
	}

	out->append(m_pBuf->c_str(), n);

	if (len > n) {
		m_pBuf->memmove(m_pBuf->c_str() + n, len - n);
	} else {
		m_pBuf->clear();
	}

	return (int) n;
}

int charset_conv::pop_end(acl::string* out, size_t max /* = 0 */)
{
	if (m_pBuf == NULL) {
		logger_error("call push_pop first");
		return -1;
	}

	update_finish(m_pBuf);

	if (out == NULL) {
		m_pBuf->clear();
		return 0;
	}

	size_t n = m_pBuf->length();
	if (n == 0) {
		return 0;
	}
	if (max > 0 && n > max) {
		n = max;
	}
	out->append(m_pBuf->c_str(), m_pBuf->length());
	m_pBuf->clear();
	return (int) n;
}

void charset_conv::clear(void)
{
	if (m_pBuf) {
		m_pBuf->clear();
	}
}

// 获得字符集转换器
charset_conv* charset_conv::create(const char* fromCharset, const char* toCharset)
{
	if (fromCharset == NULL || toCharset == NULL) {
		return NULL;
	}
	if (strcasecmp(fromCharset, toCharset) == 0) {
		return NULL;
	}

	charset_conv* conv = NEW charset_conv();
	if (!conv->update_begin(fromCharset, toCharset)) {
		delete conv;
		return NULL;
	}
	return conv;
}

string strconv(const char* in, size_t len,
	const char* from_charset, const char* to_charset)
{
	charset_conv conv;
	string res;
	if (conv.convert(from_charset, to_charset, in, len, &res)) {
		return res;
	} else {
		logger_error("convert error, from=%s, to=%s",
			from_charset, to_charset);
		res.clear();
		return res;
	}
}

} // namespace acl
