// This file is part of the Coral project.
// Please read the copyright notice in the LICENSE file.

#include "unicodestring.h"
#include <iostream>
#include <coral/lib/assert.h>
#include <Python.h>
#include <vector>
namespace coral {

	static iconv_t native_to_uni;
	static iconv_t uni_to_native;

// must be "big enough"
#define MAX_BYTES_PER_CHAR 6

// obviously
#define BYTES_PER_UCS4 4

	std::string unicodestring::helper(iconv_t conv)
		const throw (std::exception) {
		size_t in = u.size();
		size_t out = in + 4;
		// cygwin needs const
#ifdef HAVE_ICONV_CONST_CHAR
		const char* pfrom = u.data();
#else
		char* pfrom = const_cast<char*>(u.data());
#endif
		size_t err;
		std::vector<char> to(out);
		char* pto = &to[0];
		err = iconv(conv, &pfrom, &in, &pto, &out);
		if (err == (size_t) -1) {
			throw std::exception();
		}
		std::string s(&to[0], pto);
		return s;
	}

	unicodestring::ucs4 unicodestring::h(iconv_t conv, const char* c)
		throw (std::exception) {
		coral_assert(c);
	        // cygwin needs const 
#ifdef HAVE_ICONV_CONST_CHAR
                const char* cc = c;
#else
		char *cc = const_cast<char*>(c);
#endif
		int s = strlen(c);
		size_t in = s;
		size_t out = s * MAX_BYTES_PER_CHAR;
		std::vector<char> buf(out + MAX_BYTES_PER_CHAR);
		char* b = &buf[0];
		size_t err = iconv(conv, &cc, &in, &b, &out);
		if (err == (size_t) -1) {
			throw std::exception();
		}
		for (int i = 0; i < MAX_BYTES_PER_CHAR; ++i) {
			*b++ = '\0';
		}
		ucs4 u(&buf[0], s * MAX_BYTES_PER_CHAR - out);
		return u;
	}

	unicodestring::unicodestring(const char* c, bool is_utf8)
		throw (std::exception)
		: u(is_utf8 ? (c ? c : "") : h(native_to_uni, c) ) {
		;
	}

	unicodestring::unicodestring(const std::string& s, bool is_utf8)
		throw (std::exception)
		: u(is_utf8 ? s: h(native_to_uni, s.c_str()) ) {
		;
	}

	unicodestring::unicodestring(PyObject* p) throw (std::exception) {
		if (PyString_Check(p)) {
			u = h(native_to_uni, PyString_AsString(p));
			return;
		}
		if (!PyUnicode_Check(p))
			throw std::exception();
		PyObject* str = PyUnicode_AsUTF8String(p);
		if (!str)
			throw std::exception();
		if (str != Py_None) {
			assert(PyString_Check(str));
			u = PyString_AsString(str);
		}
		Py_DECREF(str);
		return;
	}

	std::string unicodestring::toUTF8() const throw (std::exception) {
		return u;
	}

	std::string unicodestring::toNative() const throw (std::exception) {
		return helper(uni_to_native);
	}

	PyObject* unicodestring::toPython() const throw (std::exception) {
		return PyUnicode_DecodeUTF8(u.data(),
					    u.size(),
					    NULL);
	}

	unicodestring::ucs4::size_type unicodestring::size() const throw () {
	  ucs4::size_type cnt = 0;
	  ucs4::size_type add_for_multibyte = 1;
	  for (std::string::const_iterator i = u.begin();
	       i != u.end(); ++i) {
	    if (*i & 0x80) {
	      cnt += add_for_multibyte;
	      add_for_multibyte = 0;
	      continue;
	    }
	    add_for_multibyte = 1;
	    ++cnt;
	  }
	  return cnt;
	}
	void unicodestring::initUnicodeCharacterConversions() {
		// BUG should be native, not hardcoded as ISO-8859-15
		native_to_uni = iconv_open("UTF-8", "ISO-8859-15");
		uni_to_native = iconv_open("ISO-8859-15", "UTF-8");
		coral_assert(native_to_uni != (iconv_t) -1);
		coral_assert(uni_to_native != (iconv_t) -1);
	}

}

