// CharEncode.cpp: implementation of the CCharEncode class.
//
//////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2000, 2001 Robert Cunnings. All rights reserved.
//
// You may copy, modify, distribute or publish this code free of charge.
//
// NO WARRANTY: This software is provided AS IS, without warranty of any kind.
//
//////////////////////////////////////////////////////////////////////
//
// Routines to convert from UTF-8 to Unicode and back.
// Adapted in part from the famous code by Ken Thompson of AT&T Bell Laboratories.
// 
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "CharEncode.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

typedef struct
{
	int nCharMask;
	int nCharValue;
	int nShift;
	long lLongMask;
	long lLongValue;
} UTF8ENTRY;

static UTF8ENTRY UTF8Table[] = 
{
	{0x80,  0x00,   0*6,    0x7F,           0},              // 1 byte sequence
	{0xE0,  0xC0,   1*6,    0x7FF,          0x80},           // 2 byte sequence
	{0xF0,  0xE0,   2*6,    0xFFFF,         0x800},          // 3 byte sequence
	{0xF8,  0xF0,   3*6,    0x1FFFFF,       0x10000},        // 4 byte sequence
	{0xFC,  0xF8,   4*6,    0x3FFFFFF,      0x200000},       // 5 byte sequence
	{0xFE,  0xFC,   5*6,    0x7FFFFFFF,     0x4000000},      // 6 byte sequence
};

const int nNumUTF8Entries = sizeof(UTF8Table) / sizeof(UTF8ENTRY);

int WideCharToUTF8(char *pszMulti, const wchar_t WideChar)
{
	long lWork;
	int nShift;
	int nNumChars = 0;

	if (pszMulti == NULL)
    {
		return 0;
    }

	lWork = WideChar;

	for (int i = 0; i < nNumUTF8Entries; i++)
	{
		nNumChars++;
		if (lWork <= UTF8Table[i].lLongMask)
		{
			nShift = UTF8Table[i].nShift;
			*pszMulti = (char)UTF8Table[i].nCharValue | (char)(lWork >> nShift);
			while (nShift > 0)
			{
				nShift -= 6;
				pszMulti++;
				*pszMulti = 0x80 | ((char)(lWork >> nShift) & 0x3F);
			}
			return nNumChars;
		}
	}
	return 0;
}

int WideStringToUTF8(const wstring& wstrSource, string& strDest)
{
	int nNumChars = 0;
	strDest.erase();

	int nStop = (int)wstrSource.length();
	if (nStop < 1)
    {
		return 0;
    }
	const wchar_t *pwszStop = wstrSource.c_str() + nStop;
	const wchar_t *pwszCursor = wstrSource.c_str();

	char szTemp[8];
	while (pwszCursor < pwszStop)
	{
		int nResult = WideCharToUTF8(szTemp, *pwszCursor);
		if (nResult < 1)
		{
			return 0;
		}
		szTemp[nResult] = 0;
		strDest += szTemp;
		nNumChars += nResult;
		pwszCursor++;
	}
	return nNumChars;
}

int WideStringToUTF8(const wchar_t *pwszSource, unsigned long ulSrcLength, string& strDest)
{
	int nNumChars = 0;
	strDest.erase();

	int nStop = ulSrcLength;
	if (nStop < 1)
    {
		return 0;
    }
	const wchar_t *pwszStop = pwszSource + nStop;
	const wchar_t *pwszCursor = pwszSource;

	char szTemp[8];
	while (pwszCursor < pwszStop)
	{
		int nResult = WideCharToUTF8(szTemp, *pwszCursor);
		if (nResult < 1)
		{
			return 0;
		}
		szTemp[nResult] = 0;
		strDest += szTemp;
		nNumChars += nResult;
		pwszCursor++;
	}
	return nNumChars;
}

int WideStringToUTF8(const wchar_t *pwszSource, unsigned long ulSrcLength, char **pszDest)
{
#define BLOCK_SIZE 1024
	int nNumChars = 0;
	int nNumBlocks = 1;

	int nStop = ulSrcLength;
	if (nStop < 1)
    {
		return 0;
    }
	const wchar_t *pwszStop = pwszSource + nStop;
	const wchar_t *pwszCursor = pwszSource;

	char szTemp[8];
	*pszDest = (char *)malloc(BLOCK_SIZE);
	if (*pszDest == NULL)
    {
		return 0;
    }
	while (pwszCursor < pwszStop)
	{
		int nResult = WideCharToUTF8(szTemp, *pwszCursor);
		if (nResult < 1)
		{
			return 0;
		}
		if ((nNumChars + nResult) > (nNumBlocks * BLOCK_SIZE))
		{
			++nNumBlocks;
			*pszDest = (char *)realloc(*pszDest, nNumBlocks * BLOCK_SIZE);
			if (*pszDest == NULL)
				return 0;
		}
		memcpy(*pszDest + nNumChars, szTemp, nResult);
		nNumChars += nResult;
		pwszCursor++;
	}
	return nNumChars;
}

int UTF8ToWideChar(wchar_t *pWideChar, const char *pszMulti)
{
	long lWork;
	int nFirstChar, nNextChar;
	int nNumChars = 0;

	if (pszMulti == NULL)
    {
		return 0;
    }

	nFirstChar = *pszMulti & 0xFF;
	lWork = nFirstChar;

	for (int i = 0; i < nNumUTF8Entries; i++)
	{
		nNumChars++;
		if ((nFirstChar & UTF8Table[i].nCharMask) == UTF8Table[i].nCharValue)
		{
			lWork &= UTF8Table[i].lLongMask;
			if (lWork < UTF8Table[i].lLongValue)
				return 0;
			*pWideChar = (wchar_t)lWork;
			return nNumChars;
		}
		pszMulti++;
		nNextChar = (*pszMulti ^ 0x80) & 0xFF;
		if (nNextChar & 0xC0)
        {
		    return 0;
        }
		lWork = (lWork << 6 ) | nNextChar;
	}
	return 0;
}

int UTF8ToWideString(const string& strSource, wstring& wstrDest)
{
	int nNumWideChars = 0;
	wstrDest.erase();
	int nStop = (int)strSource.length();
	if (nStop < 1)
    {
		return 0;
    }

	const char *pszStop = strSource.c_str() + nStop;
	const char *pszCursor = strSource.c_str();

	while (pszCursor < pszStop)
	{
		wchar_t wc;

		int nResult = UTF8ToWideChar(&wc, pszCursor);
		if (nResult < 1)
		{
			return 0;
		}
		wstrDest += wc;
		pszCursor += nResult;
		++nNumWideChars;
	}
	return nNumWideChars;
}

int UTF8ToWideString(const char *pszSource, unsigned long ulSrcLength, wstring& wstrDest)
{
	int nNumWideChars = 0;
	wstrDest.erase();
	int nStop = ulSrcLength;
	if (nStop < 1)
    {
		return 0;
    }

	const char *pszStop = pszSource + nStop;
	const char *pszCursor = pszSource;

	while (pszCursor < pszStop)
	{
		wchar_t wc;

		int nResult = UTF8ToWideChar(&wc, pszCursor);
		if (nResult < 1)
		{
			return 0;
		}
		wstrDest += wc;
		pszCursor += nResult;
		++nNumWideChars;
	}
	return nNumWideChars;
}

int UTF8ToWideString(const char *pszSource, unsigned long ulSrcLength, wchar_t **pwszDest)
{
#define BLOCK_SIZE 1024
	int nNumBlocks = 1;
	int nNumWideChars = 0;

	int nStop = ulSrcLength;
	if (nStop < 1)
    {
		return 0;
    }

	const char *pszStop = pszSource + nStop;
	const char *pszCursor = pszSource;

	*pwszDest = (wchar_t *)malloc(BLOCK_SIZE * sizeof(wchar_t));
	if (*pwszDest == NULL)
    {
		return 0;
    }
	while (pszCursor < pszStop)
	{
		wchar_t wc;

		int nResult = UTF8ToWideChar(&wc, pszCursor);
		if (nResult < 1)
		{
			return 0;
		}
		if ((nNumWideChars + 1) > (nNumBlocks * BLOCK_SIZE))
		{
			++nNumBlocks;
			*pwszDest = (wchar_t *)realloc(*pwszDest, (nNumBlocks * BLOCK_SIZE) * sizeof(wchar_t));
			if (*pwszDest == NULL)
            {
		        return 0;
            }
		}
		memcpy(*pwszDest + nNumWideChars, &wc, sizeof(wchar_t));
		pszCursor += nResult;
		++nNumWideChars;
	}
	return nNumWideChars;
}
