/////////////////////////////////////////////////////////////////////////////////////////////
// Copyright 2024 Garmin International, Inc.
// Licensed under the Flexible and Interoperable Data Transfer (FIT) Protocol License; you
// may not use this file except in compliance with the Flexible and Interoperable Data
// Transfer (FIT) Protocol License.
/////////////////////////////////////////////////////////////////////////////////////////////
// ****WARNING****  This file is auto-generated!  Do NOT edit this file.
// Profile Version = 21.158.0Release
// Tag = production/release/21.158.0-0-gc9428aa
/////////////////////////////////////////////////////////////////////////////////////////////


#include "fit_config.hpp"
#include "fit_runtime_exception.hpp"
#include "fit_unicode.hpp"

///////////////////////////////////////////////////////////////////////
// Private Definitions
///////////////////////////////////////////////////////////////////////

// Encoding types.
typedef FIT_ENUM FIT_UNICODE_ENCODING;
#define FIT_UNICODE_ENCODING_UTF8            ((FIT_UNICODE_ENCODING)0)
#define FIT_UNICODE_ENCODING_UTF16           ((FIT_UNICODE_ENCODING)1)
#define FIT_UNICODE_ENCODING_UTF32           ((FIT_UNICODE_ENCODING)2)
#define FIT_UNICODE_ENCODING_INVALID         FIT_ENUM_INVALID
#define FIT_UNICODE_ENCODING_COUNT           3

#if (FIT_WIDE_CHAR_SIZE == 1)
#define INTERNAL_UNICODE_ENCODING         FIT_UNICODE_ENCODING_UTF8
#elif (FIT_WIDE_CHAR_SIZE == 2)
#define INTERNAL_UNICODE_ENCODING         FIT_UNICODE_ENCODING_UTF16
#elif (FIT_WIDE_CHAR_SIZE == 4)
#define INTERNAL_UNICODE_ENCODING         FIT_UNICODE_ENCODING_UTF32
#else
   #error "Wide character size not specified as 1, 2 or 4 bytes."
#endif

// Conversion error codes.
typedef FIT_ENUM FIT_STRING_CONVERSION_ERROR;
#define FIT_STRING_CONVERSION_ERROR_UNICODE_INVALID_POINT                     ((FIT_STRING_CONVERSION_ERROR)0)
#define FIT_STRING_CONVERSION_ERROR_UNICODE_UNDEFINED_SOURCE_ENCODING         ((FIT_STRING_CONVERSION_ERROR)1)
#define FIT_STRING_CONVERSION_ERROR_UNICODE_UNDEFINED_DESTINATION_ENCODING    ((FIT_STRING_CONVERSION_ERROR)2)
#define FIT_STRING_CONVERSION_ERROR_UTF8_INVALID_UNIT                         ((FIT_STRING_CONVERSION_ERROR)3)
#define FIT_STRING_CONVERSION_ERROR_UTF8_INVALID_LEAD_UNIT                    ((FIT_STRING_CONVERSION_ERROR)4)
#define FIT_STRING_CONVERSION_ERROR_UTF8_INVALID_CONTINUATION_UNIT            ((FIT_STRING_CONVERSION_ERROR)5)
#define FIT_STRING_CONVERSION_ERROR_UTF8_NON_CONTINUATION_UNIT                ((FIT_STRING_CONVERSION_ERROR)6)
#define FIT_STRING_CONVERSION_ERROR_UTF8_OVERENCODED_POINT_1                  ((FIT_STRING_CONVERSION_ERROR)7)
#define FIT_STRING_CONVERSION_ERROR_UTF8_OVERENCODED_POINT_2                  ((FIT_STRING_CONVERSION_ERROR)8)
#define FIT_STRING_CONVERSION_ERROR_UTF8_OVERENCODED_POINT_3                  ((FIT_STRING_CONVERSION_ERROR)9)
#define FIT_STRING_CONVERSION_ERROR_UTF8_OVERENCODED_POINT_4                  ((FIT_STRING_CONVERSION_ERROR)10)
#define FIT_STRING_CONVERSION_ERROR_UTF8_INVALID_NUM_UNITS                    ((FIT_STRING_CONVERSION_ERROR)11)
#define FIT_STRING_CONVERSION_ERROR_UTF8_END_OF_STRING                        ((FIT_STRING_CONVERSION_ERROR)12)
#define FIT_STRING_CONVERSION_ERROR_UTF16_INVALID_UNIT                        ((FIT_STRING_CONVERSION_ERROR)13)
#define FIT_STRING_CONVERSION_ERROR_UTF16_INVALID_LEAD_UNIT                   ((FIT_STRING_CONVERSION_ERROR)14)
#define FIT_STRING_CONVERSION_ERROR_UTF16_INVALID_TAIL_UNIT                   ((FIT_STRING_CONVERSION_ERROR)15)
#define FIT_STRING_CONVERSION_ERROR_UTF16_NON_TAIL_UNIT                       ((FIT_STRING_CONVERSION_ERROR)16)
#define FIT_STRING_CONVERSION_ERROR_UTF16_OVERENCODED_POINT_1                 ((FIT_STRING_CONVERSION_ERROR)17)
#define FIT_STRING_CONVERSION_ERROR_UTF16_OVERENCODED_POINT_2                 ((FIT_STRING_CONVERSION_ERROR)18)
#define FIT_STRING_CONVERSION_ERROR_UTF16_INVALID_NUM_UNITS                   ((FIT_STRING_CONVERSION_ERROR)19)
#define FIT_STRING_CONVERSION_ERROR_UTF16_END_OF_STRING                       ((FIT_STRING_CONVERSION_ERROR)20)
#define FIT_STRING_CONVERSION_ERROR_UTF32_INVALID_UNIT                        ((FIT_STRING_CONVERSION_ERROR)21)
#define FIT_STRING_CONVERSION_ERROR_INVALID                                   FIT_ENUM_INVALID
#define FIT_STRING_CONVERSION_ERROR_COUNT                                     22

// General Unicode.
typedef unsigned long int FIT_LARGE_TYPE;
#define UNICODE_IS_POINT_VALID(POINT)        ((((((FIT_LARGE_TYPE)(POINT) & (FIT_LARGE_TYPE)0x1FFFFF) <= (FIT_LARGE_TYPE)0x00D7FF) || (((FIT_LARGE_TYPE)(POINT) & (FIT_LARGE_TYPE)0x1FFFFF) >= (FIT_LARGE_TYPE)0x00E000)) && (((FIT_LARGE_TYPE)(POINT) & (FIT_LARGE_TYPE)0x1FFFFF) <= (FIT_LARGE_TYPE)0x10FFFF)) ? (FIT_TRUE) : (FIT_FALSE))

// UTF-8 encoding.
#define UTF8_IS_UNIT_VALID(UNIT)             (((((FIT_LARGE_TYPE)(UNIT) & (FIT_LARGE_TYPE)0xFF) != (FIT_LARGE_TYPE)0xC0) && (((FIT_LARGE_TYPE)(UNIT) & (FIT_LARGE_TYPE)0xFF) != (FIT_LARGE_TYPE)0xC1) && (((FIT_LARGE_TYPE)(UNIT) & (FIT_LARGE_TYPE)0xFF) <= (FIT_LARGE_TYPE)0xF4)) ? (FIT_TRUE) : (FIT_FALSE))
#define UTF8_IS_UNIT_1UNIT(UNIT)             ((((FIT_LARGE_TYPE)(UNIT) & (FIT_LARGE_TYPE)0x80) == (FIT_LARGE_TYPE)0x00) ? (FIT_TRUE) : (FIT_FALSE))
#define UTF8_IS_UNIT_2UNIT_LEAD(UNIT)        ((((FIT_LARGE_TYPE)(UNIT) & (FIT_LARGE_TYPE)0xE0) == (FIT_LARGE_TYPE)0xC0) ? (FIT_TRUE) : (FIT_FALSE))
#define UTF8_IS_UNIT_3UNIT_LEAD(UNIT)        ((((FIT_LARGE_TYPE)(UNIT) & (FIT_LARGE_TYPE)0xF0) == (FIT_LARGE_TYPE)0xE0) ? (FIT_TRUE) : (FIT_FALSE))
#define UTF8_IS_UNIT_4UNIT_LEAD(UNIT)        ((((FIT_LARGE_TYPE)(UNIT) & (FIT_LARGE_TYPE)0xF8) == (FIT_LARGE_TYPE)0xF0) ? (FIT_TRUE) : (FIT_FALSE))
#define UTF8_IS_UNIT_MULTI_CONT(UNIT)        ((((FIT_LARGE_TYPE)(UNIT) & (FIT_LARGE_TYPE)0xC0) == (FIT_LARGE_TYPE)0x80) ? (FIT_TRUE) : (FIT_FALSE))
#define UTF8_IS_POINT_1UNIT(POINT)           (((((FIT_LARGE_TYPE)(POINT) & (FIT_LARGE_TYPE)0x1FFFFF) <= (FIT_LARGE_TYPE)0x00007F)) ? (FIT_TRUE) : (FIT_FALSE))
#define UTF8_IS_POINT_2UNIT(POINT)           (((((FIT_LARGE_TYPE)(POINT) & (FIT_LARGE_TYPE)0x1FFFFF) >= (FIT_LARGE_TYPE)0x000080) && (((FIT_LARGE_TYPE)(POINT) & (FIT_LARGE_TYPE)0x1FFFFF) <= (FIT_LARGE_TYPE)0x0007FF)) ? (FIT_TRUE) : (FIT_FALSE))
#define UTF8_IS_POINT_3UNIT(POINT)           (((((FIT_LARGE_TYPE)(POINT) & (FIT_LARGE_TYPE)0x1FFFFF) >= (FIT_LARGE_TYPE)0x000800) && (((FIT_LARGE_TYPE)(POINT) & (FIT_LARGE_TYPE)0x1FFFFF) <= (FIT_LARGE_TYPE)0x00FFFF)) ? (FIT_TRUE) : (FIT_FALSE))
#define UTF8_IS_POINT_4UNIT(POINT)           (((((FIT_LARGE_TYPE)(POINT) & (FIT_LARGE_TYPE)0x1FFFFF) >= (FIT_LARGE_TYPE)0x010000) && (((FIT_LARGE_TYPE)(POINT) & (FIT_LARGE_TYPE)0x1FFFFF) <= (FIT_LARGE_TYPE)0x10FFFF)) ? (FIT_TRUE) : (FIT_FALSE))
#define UTF8_1UNIT_TO_POINT(UNIT)            ((((FIT_LARGE_TYPE)(UNIT) & (FIT_LARGE_TYPE)0x7F) <<  0))
#define UTF8_2UNIT_TO_POINT(LEAD,C1)         ((((FIT_LARGE_TYPE)(LEAD) & (FIT_LARGE_TYPE)0x1F) <<  6) | (((FIT_LARGE_TYPE)(C1) & (FIT_LARGE_TYPE)0x3F) <<  0))
#define UTF8_3UNIT_TO_POINT(LEAD,C1,C2)      ((((FIT_LARGE_TYPE)(LEAD) & (FIT_LARGE_TYPE)0x0F) << 12) | (((FIT_LARGE_TYPE)(C1) & (FIT_LARGE_TYPE)0x3F) <<  6) | (((FIT_LARGE_TYPE)(C2) & (FIT_LARGE_TYPE)0x3F) <<  0))
#define UTF8_4UNIT_TO_POINT(LEAD,C1,C2,C3)   ((((FIT_LARGE_TYPE)(LEAD) & (FIT_LARGE_TYPE)0x07) << 18) | (((FIT_LARGE_TYPE)(C1) & (FIT_LARGE_TYPE)0x3F) << 12) | (((FIT_LARGE_TYPE)(C2) & (FIT_LARGE_TYPE)0x3F) <<  6) | (((FIT_LARGE_TYPE)(C3) & (FIT_LARGE_TYPE)0x3F) <<  0))
#define UTF8_POINT_TO_1UNIT(POINT)           ((((FIT_LARGE_TYPE)(POINT) >>  0) & (FIT_LARGE_TYPE)0x7F) | (FIT_LARGE_TYPE)0x00)
#define UTF8_POINT_TO_2UNIT_LEAD(POINT)      ((((FIT_LARGE_TYPE)(POINT) >>  6) & (FIT_LARGE_TYPE)0x1F) | (FIT_LARGE_TYPE)0xC0)
#define UTF8_POINT_TO_2UNIT_CONT1(POINT)     ((((FIT_LARGE_TYPE)(POINT) >>  0) & (FIT_LARGE_TYPE)0x3F) | (FIT_LARGE_TYPE)0x80)
#define UTF8_POINT_TO_3UNIT_LEAD(POINT)      ((((FIT_LARGE_TYPE)(POINT) >> 12) & (FIT_LARGE_TYPE)0x0F) | (FIT_LARGE_TYPE)0xE0)
#define UTF8_POINT_TO_3UNIT_CONT1(POINT)     ((((FIT_LARGE_TYPE)(POINT) >>  6) & (FIT_LARGE_TYPE)0x3F) | (FIT_LARGE_TYPE)0x80)
#define UTF8_POINT_TO_3UNIT_CONT2(POINT)     ((((FIT_LARGE_TYPE)(POINT) >>  0) & (FIT_LARGE_TYPE)0x3F) | (FIT_LARGE_TYPE)0x80)
#define UTF8_POINT_TO_4UNIT_LEAD(POINT)      ((((FIT_LARGE_TYPE)(POINT) >> 18) & (FIT_LARGE_TYPE)0x07) | (FIT_LARGE_TYPE)0xF0)
#define UTF8_POINT_TO_4UNIT_CONT1(POINT)     ((((FIT_LARGE_TYPE)(POINT) >> 12) & (FIT_LARGE_TYPE)0x3F) | (FIT_LARGE_TYPE)0x80)
#define UTF8_POINT_TO_4UNIT_CONT2(POINT)     ((((FIT_LARGE_TYPE)(POINT) >>  6) & (FIT_LARGE_TYPE)0x3F) | (FIT_LARGE_TYPE)0x80)
#define UTF8_POINT_TO_4UNIT_CONT3(POINT)     ((((FIT_LARGE_TYPE)(POINT) >>  0) & (FIT_LARGE_TYPE)0x3F) | (FIT_LARGE_TYPE)0x80)

// UTF-16 encoding.
// Using unicode version 8.0 D91, http://www.unicode.org/versions/Unicode8.0.0/ch03.pdf
// Isolated code units in the range D800..DFFF are ill formed.
// Any UTF 32 code unit > 10FFFF is ill formed.
// This check is already performed by UNICODE_IS_POINT_VALID macro
#define UTF16_IS_UNIT_VALID(UNIT)            (UNICODE_IS_POINT_VALID(UNIT))
#define UTF16_IS_UNIT_1UNIT(UNIT)            (((((FIT_LARGE_TYPE)(UNIT) & (FIT_LARGE_TYPE)0xFFFF)<= (FIT_LARGE_TYPE)0xD7FF) || (((FIT_LARGE_TYPE)(UNIT) & (FIT_LARGE_TYPE)0xFFFF)>= (FIT_LARGE_TYPE)0xE000)) ? (FIT_TRUE) : (FIT_FALSE))
#define UTF16_IS_UNIT_2UNIT_LEAD(UNIT)       (((((FIT_LARGE_TYPE)(UNIT) & (FIT_LARGE_TYPE)0xFFFF)>= (FIT_LARGE_TYPE)0xD800) && (((FIT_LARGE_TYPE)(UNIT) & (FIT_LARGE_TYPE)0xFFFF)<= (FIT_LARGE_TYPE)0xDBFF)) ? (FIT_TRUE) : (FIT_FALSE))
#define UTF16_IS_UNIT_2UNIT_TAIL(UNIT)       (((((FIT_LARGE_TYPE)(UNIT) & (FIT_LARGE_TYPE)0xFFFF)>= (FIT_LARGE_TYPE)0xDC00) && (((FIT_LARGE_TYPE)(UNIT) & (FIT_LARGE_TYPE)0xFFFF)<= (FIT_LARGE_TYPE)0xDFFF)) ? (FIT_TRUE) : (FIT_FALSE))
#define UTF16_IS_POINT_1UNIT(POINT)          (((((FIT_LARGE_TYPE)(POINT) & (FIT_LARGE_TYPE)0x1FFFFF) <= (FIT_LARGE_TYPE)0x00FFFF)) ? (FIT_TRUE) : (FIT_FALSE))
#define UTF16_IS_POINT_2UNIT(POINT)          (((((FIT_LARGE_TYPE)(POINT) & (FIT_LARGE_TYPE)0x1FFFFF) >= (FIT_LARGE_TYPE)0x010000) && (((FIT_LARGE_TYPE)(POINT) & (FIT_LARGE_TYPE)0x1FFFFF) <= (FIT_LARGE_TYPE)0x10FFFF)) ? (FIT_TRUE) : (FIT_FALSE))
#define UTF16_1UNIT_TO_POINT(UNIT)           ((FIT_LARGE_TYPE)(UNIT) & (FIT_LARGE_TYPE)0xFFFF)
#define UTF16_2UNIT_TO_POINT(LEAD,TAIL)      (((((FIT_LARGE_TYPE)(LEAD) & (FIT_LARGE_TYPE)0xFFFF) - (FIT_LARGE_TYPE)0xD800) << 10) + ((((FIT_LARGE_TYPE)(TAIL) & (FIT_LARGE_TYPE)0xFFFF) - (FIT_LARGE_TYPE)0xDC00) <<  0) + (FIT_LARGE_TYPE)0x10000)
#define UTF16_POINT_TO_1UNIT(POINT)          ((FIT_LARGE_TYPE)(POINT) & (FIT_LARGE_TYPE)0xFFFF)
#define UTF16_POINT_TO_2UNIT_LEAD(POINT)     ((((((FIT_LARGE_TYPE)(POINT) & (FIT_LARGE_TYPE)0x1FFFFF) - (FIT_LARGE_TYPE)0x10000) >> 10) & (FIT_LARGE_TYPE)0x3FF) + (FIT_LARGE_TYPE)0xD800)
#define UTF16_POINT_TO_2UNIT_TAIL(POINT)     ((((((FIT_LARGE_TYPE)(POINT) & (FIT_LARGE_TYPE)0x1FFFFF) - (FIT_LARGE_TYPE)0x10000) >>  0) & (FIT_LARGE_TYPE)0x3FF) + (FIT_LARGE_TYPE)0xDC00)

// UTF 32 encoding
// Using unicode version 8.0 D90, http://www.unicode.org/versions/Unicode8.0.0/ch03.pdf
// UTF 32 code units in the range 0000D800..0000DFFF are ill formed.
// Any UTF 32 code unit > 0010FFFF is ill formed.
// This check is already performed by UNICODE_IS_POINT_VALID macro
#define UTF32_IS_UNIT_VALID(UNIT)            (UNICODE_IS_POINT_VALID(UNIT))
#define UTF32_UNIT_TO_POINT(UNIT)            ((FIT_LARGE_TYPE)(UNIT) & (FIT_LARGE_TYPE)0x0010FFFF)
#define UTF32_POINT_TO_UNIT(POINT)           ((FIT_LARGE_TYPE)(POINT) & (FIT_LARGE_TYPE)0x0010FFFF)

namespace fit
{

///////////////////////////////////////////////////////////////////////
// Private Classes
///////////////////////////////////////////////////////////////////////

class StringConversionException : public RuntimeException
{
public:
    StringConversionException(const FIT_STRING_CONVERSION_ERROR newErrorCode = FIT_STRING_CONVERSION_ERROR_INVALID)
        : RuntimeException(std::string("")),
          errorCode(newErrorCode)
    {
    }

private:
    FIT_STRING_CONVERSION_ERROR errorCode;
};

///////////////////////////////////////////////////////////////////////
// Private Functions
///////////////////////////////////////////////////////////////////////

template <typename STRING_TYPE_DST, typename CHAR_TYPE_DST, typename STRING_TYPE_SRC, typename CHAR_TYPE_SRC>
FIT_BOOL ConvertStringEncoding(STRING_TYPE_DST& strDst, const FIT_UNICODE_ENCODING dstEncoding, const STRING_TYPE_SRC& strSrc, const FIT_UNICODE_ENCODING srcEncoding)
{
   STRING_TYPE_DST convertedStr;

   for (unsigned long int i = 0; i < strSrc.size(); ++i)
   {
      try
      {
         FIT_LARGE_TYPE codePoint = 0;

         // Decode the source.
         switch (srcEncoding)
         {
            case FIT_UNICODE_ENCODING_UTF8:
            {
               if (!UTF8_IS_UNIT_VALID(strSrc[i]))
                  throw StringConversionException(FIT_STRING_CONVERSION_ERROR_UTF8_INVALID_UNIT);

               unsigned long int numUnits = 0;

               if (UTF8_IS_UNIT_1UNIT(strSrc[i]))
                  numUnits = 1;
               else if (UTF8_IS_UNIT_2UNIT_LEAD(strSrc[i]))
                  numUnits = 2;
               else if (UTF8_IS_UNIT_3UNIT_LEAD(strSrc[i]))
                  numUnits = 3;
               else if (UTF8_IS_UNIT_4UNIT_LEAD(strSrc[i]))
                  numUnits = 4;
               else
                  throw StringConversionException(FIT_STRING_CONVERSION_ERROR_UTF8_INVALID_LEAD_UNIT);

               unsigned long int numContUnits = numUnits - 1;

               while (numContUnits--)
               {
                  if ((++i) >= strSrc.size())
                     throw StringConversionException(FIT_STRING_CONVERSION_ERROR_UTF8_END_OF_STRING);

                  if (!UTF8_IS_UNIT_MULTI_CONT(strSrc[i]))
                     throw StringConversionException(FIT_STRING_CONVERSION_ERROR_UTF8_NON_CONTINUATION_UNIT);

                  if (!UTF8_IS_UNIT_VALID(strSrc[i]))
                     throw StringConversionException(FIT_STRING_CONVERSION_ERROR_UTF8_INVALID_CONTINUATION_UNIT);
               }

               switch (numUnits)
               {
                  case 1:
                     codePoint = UTF8_1UNIT_TO_POINT(strSrc[i]);
                       if (!UTF8_IS_POINT_1UNIT(codePoint))
                          throw StringConversionException(FIT_STRING_CONVERSION_ERROR_UTF8_OVERENCODED_POINT_1);
                       break;
                  case 2:
                     codePoint = UTF8_2UNIT_TO_POINT(strSrc[i-1], strSrc[i]);
                       if (!UTF8_IS_POINT_2UNIT(codePoint))
                          throw StringConversionException(FIT_STRING_CONVERSION_ERROR_UTF8_OVERENCODED_POINT_2);
                       break;
                  case 3:
                     codePoint = UTF8_3UNIT_TO_POINT(strSrc[i-2], strSrc[i-1], strSrc[i]);
                       if (!UTF8_IS_POINT_3UNIT(codePoint))
                          throw StringConversionException(FIT_STRING_CONVERSION_ERROR_UTF8_OVERENCODED_POINT_3);
                       break;
                  case 4:
                     codePoint = UTF8_4UNIT_TO_POINT(strSrc[i-3], strSrc[i-2], strSrc[i-1], strSrc[i]);
                       if (!UTF8_IS_POINT_4UNIT(codePoint))
                          throw StringConversionException(FIT_STRING_CONVERSION_ERROR_UTF8_OVERENCODED_POINT_4);
                       break;
               }
            }
                 break;

            case FIT_UNICODE_ENCODING_UTF16:
            {
               if (!UTF16_IS_UNIT_VALID(strSrc[i]))
                  throw StringConversionException(FIT_STRING_CONVERSION_ERROR_UTF16_INVALID_UNIT);

               if (UTF16_IS_UNIT_1UNIT(strSrc[i]))
               {
                  codePoint = static_cast<FIT_LARGE_TYPE>(UTF16_1UNIT_TO_POINT(strSrc[i]));

                  if (!UTF16_IS_POINT_1UNIT(codePoint))
                     throw StringConversionException(FIT_STRING_CONVERSION_ERROR_UTF16_OVERENCODED_POINT_1);
               }
               else if (UTF16_IS_UNIT_2UNIT_LEAD(strSrc[i]))
               {
                  if ((++i) >= strSrc.size())
                     throw StringConversionException(FIT_STRING_CONVERSION_ERROR_UTF16_END_OF_STRING);

                  if (!UTF16_IS_UNIT_2UNIT_TAIL(strSrc[i]))
                     throw StringConversionException(FIT_STRING_CONVERSION_ERROR_UTF16_NON_TAIL_UNIT);

                  if (!UTF16_IS_UNIT_VALID(strSrc[i]))
                     throw StringConversionException(FIT_STRING_CONVERSION_ERROR_UTF16_INVALID_TAIL_UNIT);

                  codePoint = static_cast<FIT_LARGE_TYPE>(UTF16_2UNIT_TO_POINT(strSrc[i-1], strSrc[i]));

                  if (!UTF16_IS_POINT_2UNIT(codePoint))
                     throw StringConversionException(FIT_STRING_CONVERSION_ERROR_UTF16_OVERENCODED_POINT_2);
               }
               else
               {
                  throw StringConversionException(FIT_STRING_CONVERSION_ERROR_UTF16_INVALID_LEAD_UNIT);
               }
            }
                 break;

            case FIT_UNICODE_ENCODING_UTF32:
            {
               if (!UTF32_IS_UNIT_VALID(strSrc[i]))
                  throw StringConversionException(FIT_STRING_CONVERSION_ERROR_UTF32_INVALID_UNIT);

               codePoint = static_cast<FIT_LARGE_TYPE>(UTF32_UNIT_TO_POINT(strSrc[i]));
            }
                 break;

            default:
               throw StringConversionException(FIT_STRING_CONVERSION_ERROR_UNICODE_UNDEFINED_SOURCE_ENCODING);
         }

         // Check that the code point is valid.
         if (!UNICODE_IS_POINT_VALID(codePoint))
            throw StringConversionException(FIT_STRING_CONVERSION_ERROR_UNICODE_INVALID_POINT);

         // Encode the destination.
         switch (dstEncoding)
         {
            case FIT_UNICODE_ENCODING_UTF8:
               if (UTF8_IS_POINT_1UNIT(codePoint))
               {
                  convertedStr.push_back((CHAR_TYPE_DST) UTF8_POINT_TO_1UNIT(codePoint));
               }
               else if (UTF8_IS_POINT_2UNIT(codePoint))
               {
                  convertedStr.push_back((CHAR_TYPE_DST) UTF8_POINT_TO_2UNIT_LEAD(codePoint));
                  convertedStr.push_back((CHAR_TYPE_DST) UTF8_POINT_TO_2UNIT_CONT1(codePoint));
               }
               else if (UTF8_IS_POINT_3UNIT(codePoint))
               {
                  convertedStr.push_back((CHAR_TYPE_DST) UTF8_POINT_TO_3UNIT_LEAD(codePoint));
                  convertedStr.push_back((CHAR_TYPE_DST) UTF8_POINT_TO_3UNIT_CONT1(codePoint));
                  convertedStr.push_back((CHAR_TYPE_DST) UTF8_POINT_TO_3UNIT_CONT2(codePoint));
               }
               else if (UTF8_IS_POINT_4UNIT(codePoint))
               {
                  convertedStr.push_back((CHAR_TYPE_DST) UTF8_POINT_TO_4UNIT_LEAD(codePoint));
                  convertedStr.push_back((CHAR_TYPE_DST) UTF8_POINT_TO_4UNIT_CONT1(codePoint));
                  convertedStr.push_back((CHAR_TYPE_DST) UTF8_POINT_TO_4UNIT_CONT2(codePoint));
                  convertedStr.push_back((CHAR_TYPE_DST) UTF8_POINT_TO_4UNIT_CONT3(codePoint));
               }
               else
               {
                  throw StringConversionException(FIT_STRING_CONVERSION_ERROR_UTF8_INVALID_NUM_UNITS);
               }
                 break;

            case FIT_UNICODE_ENCODING_UTF16:
               if (UTF16_IS_POINT_1UNIT(codePoint))
               {
                  convertedStr.push_back((CHAR_TYPE_DST) UTF16_POINT_TO_1UNIT(codePoint));
               }
               else if (UTF16_IS_POINT_2UNIT(codePoint))
               {
                  convertedStr.push_back((CHAR_TYPE_DST) UTF16_POINT_TO_2UNIT_LEAD(codePoint));
                  convertedStr.push_back((CHAR_TYPE_DST) UTF16_POINT_TO_2UNIT_TAIL(codePoint));
               }
               else
               {
                  throw StringConversionException(FIT_STRING_CONVERSION_ERROR_UTF16_INVALID_NUM_UNITS);
               }
                 break;

            case FIT_UNICODE_ENCODING_UTF32:
               convertedStr.push_back((CHAR_TYPE_DST) UTF32_POINT_TO_UNIT(codePoint));
                 break;

            default:
               throw StringConversionException(FIT_STRING_CONVERSION_ERROR_UNICODE_UNDEFINED_DESTINATION_ENCODING);
         }

      }  // end try
      catch (const StringConversionException&)
      {
         continue;   // Go to the next character.
      }
   }

   strDst = convertedStr;
   return FIT_TRUE;
}


///////////////////////////////////////////////////////////////////////
// Public Functions
///////////////////////////////////////////////////////////////////////

Unicode::UTF8_STRING Unicode::Encode_BaseToUTF8(const FIT_WSTRING& strSrc)
{
   Unicode::UTF8_STRING strDst;
   if (ConvertStringEncoding<Unicode::UTF8_STRING, Unicode::UTF8_STRING::value_type, FIT_WSTRING, FIT_WSTRING::value_type>(strDst, FIT_UNICODE_ENCODING_UTF8, strSrc, INTERNAL_UNICODE_ENCODING))
      return strDst;
   else
      return Unicode::UTF8_STRING();
}

FIT_WSTRING Unicode::Encode_UTF8ToBase(const Unicode::UTF8_STRING& strSrc)
{
   FIT_WSTRING strDst;
   if (ConvertStringEncoding<FIT_WSTRING, FIT_WSTRING::value_type, Unicode::UTF8_STRING, Unicode::UTF8_STRING::value_type>(strDst, INTERNAL_UNICODE_ENCODING, strSrc, FIT_UNICODE_ENCODING_UTF8))
      return strDst;
   else
      return FIT_WSTRING_INVALID;
}

Unicode::UTF8_STRING Unicode::Copy_StdToUTF8(const std::string& strSrc)
{
#if 0
   Unicode::UTF8_STRING strDst(static_cast<Unicode::UTF8_STRING::size_type>(strSrc.size()), 0);

   for (Unicode::UTF8_STRING::size_type i = 0; i < strDst.size(); i++)
      strDst[i] = static_cast<Unicode::UTF8_STRING::value_type>(strSrc[i]);

   return strDst;
#endif

   return strSrc; // Input type is typedef'd to be the same as output - this shortcut is valid.
}

std::string Unicode::Copy_UTF8ToStd(const Unicode::UTF8_STRING& strSrc)
{
#if 0
   string strDst(static_cast<string::size_type>(strSrc.size()), 0);

   for (std::string::size_type i = 0; i < strDst.size(); i++)
      strDst[i] = static_cast<string::value_type>(strSrc[i]);

   return strDst;
#endif

   return strSrc; // Input type is typedef'd to be the same as output - this shortcut is valid.
}

} // namespace fit
