#ifndef SUPPORT_UTF8_H
#define SUPPORT_UTF8_H

#ifdef UNIX
#include "reader/strnlen_def.h"
#endif //UNIX
#ifdef __cplusplus
extern "C" {
#endif
/*
 * в rsaenh/ нет winnls.h
 * CSP_WinDef.h не включается из-за включения windef.h
 * --> нет прототипов MultiByteToWideChar() и WideCharToMultiByte()
 *     и нет CP_ACP, CP_UTF8
 */
#if defined IN_RSA && !defined CP_ACP
#define CP_ACP                      0           // default to ANSI code page
#define CP_UTF8                     65001

int
MultiByteToWideChar(
  UINT CodePage,         // code page
  DWORD dwFlags,         // character-type options
  LPCSTR lpMultiByteStr, // string to map
  int cbMultiByte,       // number of bytes in string
  wchar_t* lpWideCharStr,  // wide-character buffer
  int cchWideChar        // size of buffer
);

int
WideCharToMultiByte(
  UINT CodePage,            // code page
  DWORD dwFlags,            // performance and mapping flags
  const wchar_t* lpWideCharStr,    // wide-character string
  int cchWideChar,          // number of chars in string
  LPSTR lpMultiByteStr,     // buffer for new string
  int cbMultiByte,          // size of buffer
  LPCSTR lpDefaultChar,     // default for unmappable chars
  LPBOOL lpUsedDefaultChar  // set when default char used
);
#endif	/* IN_RSA */

#ifdef UNICODE
#   define safe_2unilen( src ) (wcslen (src) + 1)
#   define safe_uni2len( src ) ((wcslen (src) + 1) * sizeof(wchar_t))

#   define safe_2unincpy( dest, src, destlen, srclen ) wcsncpy( dest, src, destlen )
#   define safe_2unicpy( dest, src ) wcscpy( dest, src )
#   define safe_uni2ncpy( dest, src, destlen, srclen ) wcsncpy( dest, src, srclen )
#   define safe_uni2cpy( dest, src ) wcscpy( dest, src )
#else /* UNICODE */
#   define safe_2unilen( src ) MultiByteToWideChar( CP_ACP, 0, (src), (int)(-1), NULL, (int)(0))
#   define safe_uni2len( src ) WideCharToMultiByte( CP_ACP, 0, (src), (int)(-1), NULL, (int)(0), NULL, NULL )

#   define safe_2unincpy( dest, src, destlen, srclen ) MultiByteToWideChar( CP_ACP, 0, (src), (int)(srclen), (dest), (int)(destlen) )
#   define safe_2unicpy( dest, src ) safe_2unincpy(dest, src, ((strlen(src)+1)*sizeof(wchar_t)), (strlen(src)+1))
#   define safe_uni2ncpy( dest, src, destlen, srclen ) WideCharToMultiByte( CP_ACP, 0, (src), (int)(srclen), (dest), (int)(destlen), NULL, NULL )
#   define safe_uni2cpy( dest, src ) safe_uni2ncpy( dest, src, (wcslen(src)+1), ((wcslen(src)+1)*sizeof(wchar_t)))
#endif /* UNICODE */
  
/* src - не равный NULL указатель. Возвращает длину строки в wchar_t *, с учетом терминального символа */
#define safe_utf8len_inwc( src ) (MultiByteToWideChar( CP_UTF8, 0, (src), (int)(-1), NULL, (int)(0) ))

/* src - не равный NULL указатель. srclen - максимальная длина считываемой строки .
Возвращает длину строки в wchar_t *, с учетом терминального символа */
#define safe_utf8len_inwc_n( src, srclen ) (MultiByteToWideChar( CP_UTF8, 0, (src), (int)(srclen), NULL, (int)(0) ))

/*  преобразует UTF8 строку в wchar_t *
dest - указатель на буфер типа wchar_t * ,
src - указатель на BYTE *,
destlen - длина буфера dest в wchar_t,
srclen - длина буфера src  в байтах
*/
#define safe_utf8stowcs( dest, src, destlen, srclen) MultiByteToWideChar( CP_UTF8, 0, (src), (int)(srclen), (dest), (int)(destlen) )

/* src - указатель на wchar_t строку. возвращает длину буфера для записи UTF8 строки. */
#define safe_wcslen_inutf8( src ) (WideCharToMultiByte( CP_UTF8, 0, (src), (int)(-1), NULL, (int)(0), NULL, NULL ))

/* src - указатель на wchar_t строку. srclen - длина src в wchar_t.
возвращает длину буфера для записи UTF8 строки. */
#define safe_wcslen_inutf8_n( src, srclen ) (WideCharToMultiByte( CP_UTF8, 0, (src), (int)(srclen), NULL, (int)(0), NULL, NULL ))

/*  преобразует wchar_t строку в UTF8
dest - указатель на буфер типа BYTE *,
src - указатель на wchar_t * ,
destlen - длина буфера dest в байтах ,
srclen - длина буфера src в wchar_t
*/
#define safe_wcstoutf8s( dest, src, destlen, srclen ) WideCharToMultiByte( CP_UTF8, 0, (src), (int)(srclen), (dest), (int)(destlen), NULL, NULL )


typedef void* (*Alloc_func)(void * alloc_param, size_t size);
typedef void(*Free_func)(void * alloc_param, void * ptr);


static void* cpro_std_malloc(void * alloc_param, size_t size)
{
    UNUSED(alloc_param);
    return malloc(size);
}

static void cpro_std_free(void * alloc_param, void * ptr)
{
    UNUSED(alloc_param);
    free(ptr);
}


#ifdef _MSC_VER
#pragma warning (push)
#pragma warning (disable: 4996)
#endif


#ifdef UNIX
#include "locale.h"
const char* get_current_encoding(void);
#else //UNIX 
static SUP_INLINE const char* get_current_encoding()
{
    return "ru_RU.cp1251";
}
#endif //UNIX
static SUP_INLINE BOOL is_utf8_encoding()
{
    const char* csCurLang = get_current_encoding();
    if (!csCurLang) //-V547
	return FALSE;
    if (strstr(csCurLang, "utf8") != NULL)
        return TRUE;
    if (strstr(csCurLang, "UTF-8") != NULL)
        return TRUE;
    return FALSE;
}

/*
Функция копирования строки UTF8 в строку TCHAR с терминальным символом.
dest - буфер для результата. Должен быть размера, достаточного, чтобы поместилась строка TCHAR символов, заканчивающаяся терминальным символом.
src - буфер исходной UTF8 строки, заканчивающейся терминальным символом.

Функция копирует символы, включая терминальный. Если копирование успешно, результатом будет указатель на dest. Иначе результат равен NULL.
*/
static SUP_INLINE TCHAR * _utf82cpy(Alloc_func alloc_func, Free_func free_func, void * alloc_param, TCHAR * dest, const char * src, BOOL use_current_locale)
{
    wchar_t * tmpdest = NULL;
    size_t destlen;

    if (src == NULL || dest == NULL)
	return NULL;

    if (use_current_locale && is_utf8_encoding()) {
        strcpy ((char*)dest, src);
        return dest;
    }

    destlen = safe_utf8len_inwc(src);
    tmpdest = (wchar_t *)alloc_func(alloc_param, destlen*sizeof(wchar_t));
    if (!tmpdest)
	return NULL;

    safe_utf8stowcs(tmpdest, src, destlen, strlen(src) + 1);
    _uni2cpy(dest, tmpdest);
    free_func(alloc_param, tmpdest);
    return dest;
}

/*
Функция копирования строки UTF8 в строку TCHAR с терминальным символом.
dest - буфер для результата. Должен быть размера, достаточного, чтобы поместилась строка TCHAR символов, заканчивающаяся терминальным символом.
src - буфер исходной UTF8 строки.
destlen - максимальное количество символов (TCHAR-ов), которое примет результирующий буфер. С учетом \0.
srclen - максимальное число символов для копирования из src.

Функция копирует символы из строки src в строку dest, включая терминальный, но не больше, чем srclen. Строка dest завершается терминальным символом, даже если терминальный символ из src скопирован не был. 
*/
static SUP_INLINE TCHAR * _utf82lcpy(Alloc_func alloc_func, Free_func free_func, void * alloc_param, TCHAR * dest, const char * src, size_t destlen, size_t srclen, BOOL use_current_locale)
{
    wchar_t *tmpdest = NULL;
    size_t tmpdestlen;

    if (src == NULL || dest == NULL)
	return NULL;

    if (use_current_locale && is_utf8_encoding()) {
	if (srclen > destlen)
	    return NULL;
	memcpy(dest, src, srclen);
	dest[srclen] = '\0';
        return dest;
    }

    tmpdestlen = safe_utf8len_inwc_n(src, srclen);
    tmpdest = (wchar_t *)alloc_func(alloc_param, (tmpdestlen + 1)*sizeof(wchar_t));
    if (!tmpdest)
	return NULL;

    safe_utf8stowcs(tmpdest, src, tmpdestlen, srclen);
    if (wcsnlen(tmpdest, tmpdestlen) == tmpdestlen)
    {
	tmpdest[tmpdestlen] = L'\0';
	tmpdestlen++;
    }
    else
	tmpdestlen = wcsnlen(tmpdest, tmpdestlen) + 1;
    if (tmpdestlen > destlen)
    {
	free_func(alloc_param, tmpdest);
	return NULL;
    }
    _uni2cpy(dest, tmpdest);
    free_func(alloc_param, tmpdest);
    return dest;
}

/*функция определяет число символов TCHAR, необходимых для записи UTF8-строки, переданной в src. Не считая нулевой символ.*/
static SUP_INLINE size_t _utf82len(const char * src, BOOL use_current_locale)
{
    size_t len;
    if (!src) return 0;
    if (src[0] == '\0')	return 0;

    if (use_current_locale && is_utf8_encoding()) {
        return strlen(src);
    }
    len = (size_t)safe_utf8len_inwc(src);
    return len ? (len - 1) : 0;
}

/*функция определяет число символов TCHAR, необходимых для записи UTF8-строки, переданной в src. Не считая нулевой символ.*/
static SUP_INLINE size_t _utf82llen(const char * src, size_t len, BOOL use_current_locale)
{
    if (!src || !len) 
	return 0;
    if (src[0] == '\0')	
	return 0;

    if (use_current_locale && is_utf8_encoding()) {
	return len;
    }
    return (size_t)safe_utf8len_inwc_n(src, len);
}

/*перевод строки из TCHAR в UTF8. 
  Копирует строку из src в формате TCHAR, в UTF8 строку, заканчивающуюся терминальным символом, в dest.
  Возвращает NULL при неудаче и dest при успехе.*/
static SUP_INLINE char * _2utf8cpy(Alloc_func alloc_func, Free_func free_func, void * alloc_param, char* dest, const TCHAR* src, BOOL use_current_locale)
{
    size_t tmplen;
    wchar_t * tmpbuf;

    if (!src || !dest) return NULL;
    tmplen = _tcslen(src);

    if (use_current_locale && is_utf8_encoding()) {
	strcpy(dest, (char*)src);
	return dest;
    }

    tmpbuf = (wchar_t *)alloc_func(alloc_param, (tmplen + 1) * sizeof(wchar_t));
    if (!tmpbuf) return NULL;

    safe_2unicpy(tmpbuf, src);
    safe_wcstoutf8s(dest, tmpbuf, safe_wcslen_inutf8(tmpbuf), tmplen + 1);
    free_func(alloc_param, tmpbuf);
    return dest;
}

/*перевод строки из TCHAR, пришедшего из консоли, в TCHAR для отображения.
Копирует строку из src в формате TCHAR, в UTF8 строку, заканчивающуюся терминальным символом, в dest.
Возвращает NULL при неудаче и dest при успехе.*/
static SUP_INLINE TCHAR * console_2utf8cpy(Alloc_func alloc_func, Free_func free_func, void * alloc_param, TCHAR* dest, const TCHAR* src)
{
    if (is_utf8_encoding()) {
	size_t tmplen;
	wchar_t * tmpbuf;

	if (!src || !dest) return NULL;
	tmplen = _tcslen(src);

	tmpbuf = (wchar_t *)alloc_func(alloc_param, (tmplen + 1) * sizeof(wchar_t));
	if (!tmpbuf) return NULL;

	safe_2unicpy(tmpbuf, src);
	safe_wcstoutf8s((char*)dest, tmpbuf, safe_wcslen_inutf8(tmpbuf), tmplen + 1);
	free_func(alloc_param, tmpbuf);
    }
    else {
	_tcscpy(dest, src);
    }
    return dest;
}

/* функция копирования строки из TCHAR в UTF8 с контролем длины буфера 
   dest - буфер для записи строки в кодировке UTF8.
   src - буфер, содержащий строку TCHAR.
   destlen - размер буфера dest в байтах.
   srclen - максимальный размер копируемой строки src в символах TCHAR.
   Функция копирует не более чем srclen символов, включая терминальный, из src в dest, кодируя результат в UTF8. Результат завершается терминальным символом, 
   даже если терминальный символ из src скопирован не был.
   Возвращает NULL при неудаче и dest при успехе.
   */
static SUP_INLINE char * _2utf8lcpy(Alloc_func alloc_func, Free_func free_func, void * alloc_param, char* dest, const TCHAR* src, size_t destlen, size_t srclen, BOOL use_current_locale)
{
    size_t tmplen;
    wchar_t * tmpbuf;

    if (!src || !dest) return NULL;

    tmplen = _tcsnlen(src, srclen);

    if (use_current_locale && is_utf8_encoding()) {
	if (tmplen > destlen)
	    return NULL;
        strcpy (dest, (char*)src);
        return dest;
    }

    tmpbuf = (wchar_t *)alloc_func(alloc_param, (tmplen + 1)*sizeof(wchar_t));
    if (!tmpbuf) return NULL;
    safe_2unincpy(tmpbuf, src, tmplen + 1, tmplen);
    tmpbuf[tmplen] = L'\0';

    if ((size_t)safe_wcslen_inutf8(tmpbuf) > destlen)
    {
	free_func(alloc_param, tmpbuf);
	return NULL;
    }

    safe_wcstoutf8s(dest, tmpbuf, safe_wcslen_inutf8(tmpbuf), tmplen + 1);
    free_func(alloc_param, tmpbuf);
    return dest;
}

/*
Функция возвращает длину строки src в кодировке UTF8, без терминального символа.
*/
static SUP_INLINE size_t _2utf8len(Alloc_func alloc_func, Free_func free_func, void * alloc_param, const TCHAR * src, BOOL use_current_locale)
{
    size_t tmplen;
    wchar_t * tmpbuf;

    if (!src) return 0;
    if (src[0] == _TEXT('\0'))
	return 0;
    tmplen = _tcslen(src);

    if (use_current_locale && is_utf8_encoding()) {
        return tmplen;
    }

    tmpbuf = (wchar_t *)alloc_func(alloc_param, (tmplen + 1)*sizeof(wchar_t));
    if (!tmpbuf) return 0;

    safe_2unicpy(tmpbuf, src);
    tmplen = safe_wcslen_inutf8(tmpbuf) - 1;
    free_func(alloc_param, tmpbuf);
    return tmplen;
}

/* Функция копирования строки UTF8 в строку TCHAR с терминальным символом.
dest - буфер для результата. Должен быть размера, достаточного, чтобы поместилась строка TCHAR символов, заканчивающаяся терминальным символом.
src - буфер исходной UTF8 строки, заканчивающейся терминальным символом.

Функция копирует символы, включая терминальный. Если копирование успешно, результатом будет указатель на dest. Иначе результат равен NULL.

Работает через malloc и free.
*/
static SUP_INLINE TCHAR * std_utf82cpy(TCHAR * dest, const char * src)
{
    return _utf82cpy(cpro_std_malloc, cpro_std_free, NULL, dest, src, TRUE);
}

/*
Функция копирования строки UTF8 в строку TCHAR с терминальным символом.
dest - буфер для результата. Должен быть размера, достаточного, чтобы поместилась строка TCHAR символов, заканчивающаяся терминальным символом.
src - буфер исходной UTF8 строки.
destlen - максимальное количество символов (TCHAR-ов), которое примет результирующий буфер. С учетом \0.
srclen - максимальное число байт для копирования из src.

Функция копирует символы из строки src в строку dest, включая терминальный, но не больше, чем srclen. Строка dest завершается терминальным символом, даже если терминальный символ из src скопирован не был.

Работает через malloc и free.
*/
static SUP_INLINE TCHAR * std_utf82lcpy(TCHAR * dest, const char * src, size_t destlen, size_t srclen)
{
    return _utf82lcpy(cpro_std_malloc, cpro_std_free, NULL, dest, src, destlen, srclen, TRUE);
}

/*функция определяет число символов TCHAR, необходимых для записи UTF8-строки, переданной в src. Не считая нулевой символ.

Работает через malloc и free.
*/
static SUP_INLINE size_t std_utf82len(const char * src)
{
    return _utf82len(src, TRUE);
}

/*функция определяет число символов TCHAR, необходимых для записи UTF8-строки, переданной в src. 

Работает через malloc и free.
*/
static SUP_INLINE size_t std_utf82llen(const char * src, size_t src_len)
{
    return _utf82llen(src, src_len, TRUE);
}

/* функция копирования строки из TCHAR в UTF8 с контролем длины буфера
dest - буфер для записи строки в кодировке UTF8.
src - буфер, содержащий строку TCHAR.
destlen - размер буфера dest в байтах.
srclen - максимальный размер копируемой строки src в символах TCHAR.
Функция копирует не более чем srclen символов, включая терминальный, из src в dest, кодируя результат в UTF8. Результат завершается терминальным символом,
даже если терминальный символ из src скопирован не был.
Возвращает NULL при неудаче и dest при успехе.

Работает через malloc и free.
*/
static SUP_INLINE char * std_2utf8lcpy(char* dest, const TCHAR* src, size_t destlen, size_t srclen)
{
    return _2utf8lcpy(cpro_std_malloc, cpro_std_free, NULL, dest, src, destlen, srclen, TRUE);
}

/*
Функция возвращает длину строки src в кодировке UTF8, без терминального символа.
Работает через malloc и free.
*/
static SUP_INLINE size_t std_2utf8len(const TCHAR * src)
{
    return _2utf8len(cpro_std_malloc, cpro_std_free, NULL, src, TRUE);
}

// Функции конвертации CP_ACP <-> UTF-8, которые не смотрят на текущую локаль
// Полные аналоги функций без суфикса _cpacp

static SUP_INLINE TCHAR* std_utf82lcpy_cpacp(TCHAR* dest, const char* src, size_t destlen, size_t srclen)
{
    return _utf82lcpy(cpro_std_malloc, cpro_std_free, NULL, dest, src, destlen, srclen, FALSE);
}
static SUP_INLINE size_t std_utf82llen_cpacp(const char* src, size_t src_len)
{
    return _utf82llen(src, src_len, FALSE);
}
static SUP_INLINE char* std_2utf8lcpy_cpacp(char* dest, const TCHAR* src, size_t destlen, size_t srclen)
{
    return _2utf8lcpy(cpro_std_malloc, cpro_std_free, NULL, dest, src, destlen, srclen, FALSE);
}
static SUP_INLINE size_t std_2utf8len_cpacp(const TCHAR* src)
{
    return _2utf8len(cpro_std_malloc, cpro_std_free, NULL, src, FALSE);
}

#ifdef _MSC_VER
#pragma warning (pop)
#endif

#ifdef __cplusplus
}
#endif

#endif //SUPPORT_UTF8_H
