// Scintilla source code edit control
/** @file UniConversion.cpp
 ** Functions to handle UFT-8 and UCS-2 strings.
 **/
/* Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
 * Copyright 2005-2006 Tom N Harris <telliamed@whoopdedo.org>
 * 
 * All Rights Reserved 
 * 
 * Permission to use, copy, modify, and distribute this software and its 
 * documentation for any purpose and without fee is hereby granted, 
 * provided that the above copyright notice appear in all copies and that 
 * both that copyright notice and this permission notice appear in 
 * supporting documentation. 
 * 
 * NEIL HODGSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 
 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 
 * AND FITNESS, IN NO EVENT SHALL NEIL HODGSON BE LIABLE FOR ANY 
 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE 
 * OR PERFORMANCE OF THIS SOFTWARE. 
 */

#include <cstdlib>

#include "UniConversion.h"

unsigned int UTF8Length(const wchar_t *uptr, unsigned int tlen) {
	unsigned int len = 0;
	for (unsigned int i = 0; i < tlen && uptr[i]; i++) {
		unsigned int uch = uptr[i];
		if (uch < 0x80)
			len++;
		else if (uch < 0x800)
			len+=2;
		else
			len +=3;
	}
	return len;
}

void UTF8FromUCS2(const wchar_t *uptr, unsigned int tlen, char *putf, unsigned int len) {
	int k = 0;
	for (unsigned int i = 0; i < tlen && uptr[i]; i++) {
		unsigned int uch = uptr[i];
		if (uch < 0x80) {
			putf[k++] = static_cast<char>(uch);
		} else if (uch < 0x800) {
			putf[k++] = static_cast<char>(0xC0 | (uch >> 6));
			putf[k++] = static_cast<char>(0x80 | (uch & 0x3f));
		} else {
			putf[k++] = static_cast<char>(0xE0 | (uch >> 12));
			putf[k++] = static_cast<char>(0x80 | ((uch >> 6) & 0x3f));
			putf[k++] = static_cast<char>(0x80 | (uch & 0x3f));
		}
	}
	putf[len] = '\0';
}

int UTF8FromUCS2(wchar_t uch, char *putf, unsigned int len) {
	int k = 0;
	if (uch < 0x80) {
		putf[k++] = static_cast<char>(uch);
	} else if (uch < 0x800) {
		putf[k++] = static_cast<char>(0xC0 | (uch >> 6));
		putf[k++] = static_cast<char>(0x80 | (uch & 0x3f));
	} else {
		putf[k++] = static_cast<char>(0xE0 | (uch >> 12));
		putf[k++] = static_cast<char>(0x80 | ((uch >> 6) & 0x3f));
		putf[k++] = static_cast<char>(0x80 | (uch & 0x3f));
	}
	return k;
}

unsigned int UCS2Length(const char *s, unsigned int len) {
	unsigned int ulen = 0;
	for (unsigned int i=0;i<len;i++) {
		unsigned char ch = static_cast<unsigned char>(s[i]);
		if ((ch < 0x80) || (ch > (0x80 + 0x40)))
			ulen++;
	}
	return ulen;
}

unsigned int UCS2FromUTF8(const char *s, unsigned int len, wchar_t *tbuf, unsigned int tlen) {
	unsigned int ui=0;
	const unsigned char *us = reinterpret_cast<const unsigned char *>(s);
	unsigned int i=0;
	while ((i<len) && (ui<tlen)) {
		unsigned char ch = us[i++];
		if (ch < 0x80) {
			tbuf[ui] = ch;
		} else if (ch < 0x80 + 0x40 + 0x20) {
			tbuf[ui] = static_cast<wchar_t>((ch & 0x1F) << 6);
			ch = us[i++];
			tbuf[ui] = static_cast<wchar_t>(tbuf[ui] + (ch & 0x7F));
		} else {
			tbuf[ui] = static_cast<wchar_t>((ch & 0xF) << 12);
			ch = us[i++];
			tbuf[ui] = static_cast<wchar_t>(tbuf[ui] + ((ch & 0x7F) << 6));
			ch = us[i++];
			tbuf[ui] = static_cast<wchar_t>(tbuf[ui] + (ch & 0x7F));
		}
		ui++;
	}
	return ui;
}

wchar_t UCS2FromUTF8(const char *s) {
	wchar_t uch = 0;
	const unsigned char *us = reinterpret_cast<const unsigned char *>(s);
	unsigned int i=0;
	unsigned char ch = us[i++];
	if (ch < 0x80) {
		uch = static_cast<wchar_t>(ch);
	} else if (ch < 0x80 + 0x40 + 0x20) {
		uch = static_cast<wchar_t>((ch & 0x1F) << 6);
		ch = us[i++];
		uch = static_cast<wchar_t>(uch + (ch & 0x7F));
	} else {
		uch = static_cast<wchar_t>((ch & 0xF) << 12);
		ch = us[i++];
		uch = static_cast<wchar_t>(uch + ((ch & 0x7F) << 6));
		ch = us[i++];
		uch = static_cast<wchar_t>(uch + (ch & 0x7F));
	}
	return uch;
}

char const* UTF8NextChar(char const* s) {
	unsigned char const *us = reinterpret_cast<unsigned char const *>(s);
	while ((*us >= 0x80) && (*us < (0x80 + 0x40)))
		++us;
	return reinterpret_cast<char const*>(us);
}

