#include <swilib.h>
#include "string_util.h"

/////////////////////////////////////////// Разный стафф для замены спецсимволов

// Структура, описывающая, что на что менять
typedef struct
{
  char mask[7];
  char replace;
}REPL_ARRAY;

// Сами замены и их количество
const int Repl_chars_count = 5;
REPL_ARRAY Repl_chars[] = {"&apos;\0",0x27,
                           "&quot;\0",'"',
                           "&lt;\0\0\0", '<',
                           "&gt;\0\0\0", '>',
                           "&amp;\0\0", '&',
};

/*
 * UTF8 -> UTF16
 */
int utf8_2unicode(const char **res, const char *str)
{
  int zz=0;
  int c=*str++;
  char *s=(char *)&zz;
  *res=NULL;
  if ((c >> 7) == 0x0)
  {
    zz=c;
  }
  else if ((c >> 5) == 0x6)
  {
    s[1] = ((c & 0x1f) >> 2);
    s[0] = c << 6;
    c = *str++;
    s[0] |= (c & 0x3f);
  }
  else if ((c >> 4) == 0xe)
  {
    s[1] = c << 4;
    c = *str++;
    s[1] |= ((c >> 2) & 0xf);
    s[0] = c << 6;
    c = *str++;
    s[0] |= (c & 0x3f);
  }
  *res=str;
  return zz;
}

/*
    Получить спецсимвол по его маске
IN: mask_begin - строка символов
    out_ofs - число, к которому прибавится длина обработанной последовательности
OUT: out_ofs - смещение в строке, откуда начинаются необработанные данные
    <return> - спецсимвол
*/
char GetSpecialSym(char *mask_begin, uint32_t *out_ofs)
{
  int i=0;
  int replen;
  char rep_ex[10];
  if(*mask_begin!='&')return *(mask_begin);
  for(i=0;i<Repl_chars_count;i++)
  {
    replen = strlen(Repl_chars[i].mask);  // Определяем длину очередной маски
    zeromem(rep_ex,10);
    strncpy(rep_ex,mask_begin,replen);    // Копируем строку такой длины с текущей позиции
    if(!strcmp(rep_ex,Repl_chars[i].mask))// Если совпало с очередной маской
    {
      *out_ofs+=replen-1;                   // Увеличиваем обработанную длину на длину маски
      return Repl_chars[i].replace;       // Возвращаем символ для замены
    }
  }
  return *(mask_begin);       //  Масок не нашлось, возвращаем как есть
}

int GetSpecialSymMaskN(char sym)
{
  for(int i=0; i<Repl_chars_count; i++)
    if(Repl_chars[i].replace==sym)
      return i;
  return -1;
}

char * Replace_Special_Syms(char * unrep_str)
{
  unsigned int unrep_len=strlen(unrep_str);
  char *rep_buffer = malloc(unrep_len+1);
  zeromem(rep_buffer, unrep_len+1);
  char tmp=0;
  unsigned int rpl_c=0;
  for(uint32_t j=0;j<unrep_len;j++)
  {
    tmp = *(unrep_str+j);
    tmp = GetSpecialSym(unrep_str+j, &j);
    //ShowMSG(1,(int)"fnd");
    rep_buffer[rpl_c++]=tmp;
  }
  rep_buffer = realloc(rep_buffer,rpl_c+1);
  return rep_buffer;
}

char * Mask_Special_Syms(const char * unrep_str)
{
  unsigned int unrep_len = strlen(unrep_str);
  unsigned int rep_buffer_size = unrep_len*2+16;
  char *rep_buffer = malloc(rep_buffer_size);
  unsigned int c_pos = 0;
  for(uint32_t i=0; i<unrep_len; i++)
  {
    int n=GetSpecialSymMaskN(unrep_str[i]);
    if(n!=-1)
    {
      strcpy(rep_buffer+c_pos, Repl_chars[n].mask);
      c_pos += strlen(Repl_chars[n].mask);
    }
    else
    {
      rep_buffer[c_pos++] = unrep_str[i];
    }
    if (c_pos+10>rep_buffer_size)
    {
      rep_buffer_size *= 2;
      rep_buffer = realloc(rep_buffer, rep_buffer_size);
    }
  }
  rep_buffer[c_pos] = '\0';
  rep_buffer = realloc(rep_buffer, c_pos+1);
  return rep_buffer;
}

/* Вернуть значение параметра по имени параметра из строки вида:
 nonce="2444323444",qop="auth",charset=utf-8,algorithm=md5-sess

IN: ch - строка
    req - имя требуемого параметра
    cut_quotes - обрезать ли кавычки, если параметр в кавычках
OUT: Искомое значение; нужно освободить память!
*/
char *Get_Param_Value(char *ch, char *req, char cut_quotes)
{
//  char ch[]="nonce=\"2444323444\",qop=\"auth\",charset=utf-8,algorithm=md5-sess";
//  char req[]="qop";
  char *n_displace = strstr(ch, req);     // начало строки с именем параметра
  char *eq=n_displace + strlen(req);
  if(!(eq[0]=='='))return NULL;
  eq+=1;
  char *zpt= strchr(n_displace,',');
  if(!zpt)zpt=ch+strlen(ch);
  int len;
  char *val;
  if(cut_quotes)
  {
    len=zpt-eq-2;
    val=malloc(len+1);
    for(int i=0;i<len;i++) val[i]=*(eq+i+1);
  }
  else
  {
    len=zpt-eq;
    val=malloc(len+1);
    for(int i=0;i<len;i++) val[i]=*(eq+i);
  }
  val[len]=0x0;
  return val;
}


/*
  Преобразование буфера данных из кодировки UTF-8 в ANSI
IN:
  - tmp_out: куда положить результат. Буфер уже должен существовать
             и в нем должно быть достаточно места
  - UTF8_str: откуда брать данные для преобразования
  - size: сколько длина буфера для преобразования (UTF8_str)
  - fact - куда положить итоговый размер данных в буфере

OUT:  результирующий буфер.
*/
void* convUTF8_to_ANSI(char* tmp_out, char *UTF8_str, unsigned int size, int* fact)
{
  // Рассматривая строку UTF8 как обычную, определяем её длину
  if(!UTF8_str)return NULL;
  int st_len = size;

  // Выделяем память - на всякий случай столько же. Это предельный случай,
  // когда весь поступивший буфер - на русском языке. Реально будет, скорее всего,
  // занято меньше, посему в конце сделаем realloc
  int lastchar = 0;
  int dummy;
  char chr, chr2, chr3;
  for(int i=0;i<st_len;i++)
  {
  chr = (*(UTF8_str+i));

	if (chr<0x80)
        {
          *(tmp_out+lastchar)=chr;
          lastchar++;
          goto L_END_CYCLE;
        }
	if (chr<0xc0)
        {
          ShowMSG(1,(int)"Bad UTF-8 Encoding encountered (chr<0xC0)");
          mfree(tmp_out);
          return NULL;
        }

        chr2 = *(UTF8_str+i+1);

        if (chr2<0x80)
        {
          ShowMSG(1,(int)"Bad UTF-8 Encoding encountered (chr2<0x80)");
          mfree(tmp_out);
          return NULL;
        }

	if (chr<0xe0) {
	    // cx, dx
	    char test1 = (chr & 0x1f)<<6;
            char test2 = chr2 & 0x3f;
            *(tmp_out+lastchar)= test1 | test2 + 127 + 0x31;
            i++;
            lastchar++;
            goto L_END_CYCLE;
	}
	if (chr<0xf0) {
	    // cx, dx
	    chr3= *(UTF8_str+i+2);

	    if (chr3<0x80)
            {
              ShowMSG(1,(int)"Bad UTF-8 Encoding encountered");
              mfree(tmp_out);
              return NULL;
            }
	    else
            {
              *(tmp_out+lastchar) =  ((chr & 0x0f)<<12) | ((chr2 &0x3f) <<6) | (chr3 &0x3f);
              i=i+2;
            }
	}

  L_END_CYCLE:
    dummy++;
  }
  tmp_out = realloc(tmp_out,lastchar);
  *fact = lastchar;
  return tmp_out;
}

// Преобразование в нижний регистр
char* str2lower(char *st)
{
  unsigned int len = strlen(st);
  for(uint32_t i=0;i<len;i++)
  {
    char sym = *(st+i);
    if(sym<0x80)
    {
      if(st[i]>='A' && st[i]<='Z') st[i] += ('a' - 'A');
      continue;
    }
    if(sym<0xe0)
    {
      i++;
      if(st[i] >= 0x90 && st[i]<=0xAF) st[i]+= (0xB0 - 0x90);
    }
  }
  return st;
}

////////////////////////////////////////////////////////////////////////////////
#pragma inline
int tolower(int C)
{
//  if ((C>='A' && C<='Z')) C-='A'-'a';
    if(C<0x80)
    {
      if(C>='A' && C<='Z') C += ('a' - 'A');
      return C;
    }
      if(C >= 0x90 && C<=0xAF) C+= (0xB0 - 0x90);
  return(C);
}


// Аналог strcmp, но без чувствительности к регистру
int stricmp(const char *s, const char *d)
{
  int cs;
  int ds;
  do
  {
    cs=tolower(*s++);
    ds=tolower(*d++);
    cs-=ds;
    if (cs) break;
  }
  while(ds);
  return(cs);
}

int strnicmp(const char *s1, const char *s2, size_t len)
{
	/* Yes, Virginia, it had better be unsigned */
	unsigned int c1, c2;

	c1 = 0;	c2 = 0;
	if (len) {
		do {
			c1 = *s1; c2 = *s2;
			s1++; s2++;
			if (!c1)
				break;
			if (!c2)
				break;
			if (c1 == c2)
				continue;
			c1 = tolower(c1);
			c2 = tolower(c2);
			if (c1 != c2)
				break;
		} while (--len);
	}
	return c1 - c2;
}

// Аналог strstr, но без чувствительности к регистру
/*
 * Find the first occurrence of find in s.
 */
char *stristr(const char *s, const char *find)
{
    char c, sc;
    size_t len;

    if ((c = tolower((unsigned char)(*find++))) != 0) {
        len = strlen(find);
        do {
            do {
                if ((sc = tolower((unsigned char)(*s++))) == 0)
                    return (NULL);
            } while (sc != c);
        } while (strnicmp(s, find, len) != 0);
        s--;
    }
    return ((char *)s);
}
////////////////////////////////////////////////////////////////////////////////

/*
// Аналог strstr, но без чувствительности к регистру
char *stristr(char *haystack, char *needle)
{
  if(!haystack || !needle)return NULL;
  char *i_haystack = malloc(strlen(haystack)+1);
  char *i_needle = malloc(strlen(needle)+1);
  strcpy(i_haystack, haystack);
  strcpy(i_needle, needle);
  str2lower(i_haystack);
  str2lower(i_needle);
  // Сравниваем уже строки в нижнем регистре и считаем смещение
  char *q = strstr(i_haystack, i_needle);
  mfree(i_haystack);
  mfree(i_needle);
  if(!q)  // Значит, нет подстроки
  {
    return NULL;
  }
  int delta = q - i_haystack;
  return haystack + delta;  // Есть подстрока, очевидно, по тому же смещению
}

// Аналог strcmp, но без чувствительности к регистру
int stricmp(char *str1, char *str2)
{
  if(!str1 || !str2)return NULL;
  char *i_str1 = malloc(strlen(str1)+1);
  char *i_str2 = malloc(strlen(str2)+1);
  strcpy(i_str1, str1);
  strcpy(i_str2, str2);
  str2lower(i_str1);
  str2lower(i_str2);
  // Сравниваем уже строки в нижнем регистре
  int res= strcmp(i_str1, i_str2);
  mfree(i_str1);
  mfree(i_str2);
  return res;
}
*/
char* str2lower_ANSI(char *st)
{
  unsigned int len = strlen(st);
  for(uint32_t i=0;i<len;i++)
  {
    char sym = *(st+i);
    char *cc = st+i;
    *cc= sym>='A' && sym<='Z' ? sym + ('a' - 'A') : sym >= 'А' && sym <= 'Я'? sym + ('а' - 'А') : sym;
  }
  return st;
}


// Строковый вариант
char* convUTF8_to_ANSI_STR(char *UTF8_str)
{
  // Рассматривая строку UTF8 как обычную, определяем её длину
  if(!UTF8_str)return NULL;
  int st_len = strlen(UTF8_str);

  // Выделяем память - на всякий случай дохера
  int lastchar = 0;
  int dummy;
  char* tmp_out = malloc(st_len+1);
  zeromem(tmp_out,st_len+1);
  char chr, chr2, chr3;
  for(int i=0;i<st_len;i++)
  {
  chr = (*(UTF8_str+i));

	if (chr<0x80)
        {
          *(tmp_out+lastchar)=chr;
          lastchar++;
          goto L_END_CYCLE;
        }
	if (chr<0xc0)
        {
          ShowMSG(1,(int)"Bad UTF-8 Encoding encountered (chr<0xC0)");
          mfree(tmp_out);
          return NULL;
        }

        chr2 = *(UTF8_str+i+1);

        if (chr2<0x80)
        {
          ShowMSG(1,(int)"Bad UTF-8 Encoding encountered (chr2<0x80)");
          mfree(tmp_out);
          return NULL;
        }

	if (chr<0xe0) {
	    // cx, dx
                  if ((chr == 0xD0)&&(chr2 == 0x81)){*(tmp_out+lastchar) = 0xA8;}//Ё
             else if ((chr == 0xD0)&&(chr2 == 0x86)){*(tmp_out+lastchar) = 0xB2;}//І
             else if ((chr == 0xD0)&&(chr2 == 0x87)){*(tmp_out+lastchar) = 0xAF;}//Ї
             else if ((chr == 0xD0)&&(chr2 == 0x84)){*(tmp_out+lastchar) = 0xAA;}//Є
             else if ((chr == 0xD1)&&(chr2 == 0x91)){*(tmp_out+lastchar) = 0xB8;}//ё
             else if ((chr == 0xD1)&&(chr2 == 0x96)){*(tmp_out+lastchar) = 0xB3;}//і
             else if ((chr == 0xD1)&&(chr2 == 0x97)){*(tmp_out+lastchar) = 0xBF;}//ї
             else if ((chr == 0xD1)&&(chr2 == 0x94)){*(tmp_out+lastchar) = 0xBA;}//є
             else if ((chr == 0xD2)&&(chr2 == 0x91)){*(tmp_out+lastchar) = 0xE3;}//ґ->г
             else if ((chr == 0xD2)&&(chr2 == 0x90)){*(tmp_out+lastchar) = 0xC3;}//Ґ->Г
             else
          {
	    char test1 = (chr & 0x1f)<<6;
            char test2 = chr2 & 0x3f;
            *(tmp_out+lastchar)= test1 | test2 + 127 + 0x31;
          }
            i++;
            lastchar++;
            goto L_END_CYCLE;
	}
	if (chr<0xf0) {
	    // cx, dx
	    chr3= *(UTF8_str+i+2);

	    if (chr3<0x80)
            {
              ShowMSG(1,(int)"Bad UTF-8 Encoding encountered");
              mfree(tmp_out);
              return NULL;
            }
	    else
            {
              *(tmp_out+lastchar) =  ((chr & 0x0f)<<12) | ((chr2 &0x3f) <<6) | (chr3 &0x3f);
              i=i+2;
            }
	}

  L_END_CYCLE:
    dummy++;
  }
  st_len = strlen(tmp_out);
  tmp_out = realloc(tmp_out,st_len+1);
  return tmp_out;
}



// Готовимся к отказу от ANSI вообще. Пока не используется, скоро, наверное, будет...

/* UTF-8 to UTF-16 conversion.  Surrogates are handeled properly, e.g.
 * a single 4-byte UTF-8 character is encoded into a surrogate pair.
 * On the other hand, if the UTF-8 string contains surrogate values, this
 * is considered an error and returned as such.
 *
 * The destination array must be able to hold as many Unicode-16 characters
 * as there are ASCII characters in the UTF-8 string.  This in case all UTF-8
 * characters are ASCII characters.  No more will be needed.
 *
 * Copyright (c) 2000 Morten Rolland, Screen Media
 */

int utf8_to_utf16(char *utf8, int cc, char *unicode16)
{
	int count = 0;
	unsigned char c0, c1;
	unsigned long scalar;

	while(--cc >= 0) {
		c0 = *utf8++;
		/*DPRINTF("Trying: %02x\n",c0);*/

		if ( c0 < 0x80 ) {
			/* Plain ASCII character, simple translation :-) */
			*unicode16++ = c0;
			count++;
			continue;
		}

		if ( (c0 & 0xc0) == 0x80 )
			/* Illegal; starts with 10xxxxxx */
			return -1;

		/* c0 must be 11xxxxxx if we get here => at least 2 bytes */
		scalar = c0;
		if(--cc < 0)
			return -1;
		c1 = *utf8++;
		/*DPRINTF("c1=%02x\n",c1);*/
		if ( (c1 & 0xc0) != 0x80 )
			/* Bad byte */
			return -1;
		scalar <<= 6;
		scalar |= (c1 & 0x3f);

		if ( !(c0 & 0x20) ) {
			/* Two bytes UTF-8 */
			if ( scalar < 0x80 )
				return -1;	/* Overlong encoding */
			*unicode16++ = scalar & 0x7ff;
			count++;
			continue;
		}

		/* c0 must be 111xxxxx if we get here => at least 3 bytes */
		if(--cc < 0)
			return -1;
		c1 = *utf8++;
		/*DPRINTF("c1=%02x\n",c1);*/
		if ( (c1 & 0xc0) != 0x80 )
			/* Bad byte */
			return -1;
		scalar <<= 6;
		scalar |= (c1 & 0x3f);

		if ( !(c0 & 0x10) ) {
			/*DPRINTF("####\n");*/
			/* Three bytes UTF-8 */
			if ( scalar < 0x800 )
				return -1;	/* Overlong encoding */
			if ( scalar >= 0xd800 && scalar < 0xe000 )
				return -1;	/* UTF-16 high/low halfs */
			*unicode16++ = scalar & 0xffff;
			count++;
			continue;
		}

		/* c0 must be 1111xxxx if we get here => at least 4 bytes */
		c1 = *utf8++;
		if(--cc < 0)
			return -1;
		/*DPRINTF("c1=%02x\n",c1);*/
		if ( (c1 & 0xc0) != 0x80 )
			/* Bad byte */
			return -1;
		scalar <<= 6;
		scalar |= (c1 & 0x3f);

		if ( !(c0 & 0x08) ) {
			/* Four bytes UTF-8, needs encoding as surrogates */
			if ( scalar < 0x10000 )
				return -1;	/* Overlong encoding */
			scalar -= 0x10000;
			*unicode16++ = ((scalar >> 10) & 0x3ff) + 0xd800;
			*unicode16++ = (scalar & 0x3ff) + 0xdc00;
			count += 2;
			continue;
		}

		return -1;	/* No support for more than four byte UTF-8 */
	}
	return count;
}




/*
Следующая функция взята из файла:
http://www.cs.umd.edu/projects/hpsl/chaos/ResearchAreas/ic/dist/InterComm-1.5.tar.gz/InterComm/src/ezxml/ezxml.c

В функции отключена проверка на UNICODE LE / BE, ибо в Сименсе используется только LE
*/
#define EZXML_BUFSIZE 1024
/* converts a UTF-16 string to UTF-8, returns a new string the must be freed or NULL if no conversion was needed*/
//char *ezxml_to_utf8(char **s, size_t *len) - оригинальный заголовок
char *utf16_to_utf8(char **s, size_t *len)
{
    char *u;
    size_t l = 0, sl, max = *len;
    long c, c2;
    int b, be = 0;//be = (**s == '\xFE') ? 1 : (**s == '\xFF') ? 0 : -1; - отключение проверки!!!

    if (be == -1) return NULL; /* not UTF-16*/

    u = malloc(max); zeromem(u, max);
    //    for (sl = 2; sl < *len - 1; sl += 2) {      // Второй фикс: у нас строка на 2 байта больше
        for (sl = 2; sl <= *len ; sl += 2) {
      c = (be) ? ((long)(*s)[sl] << 8) | (*s)[sl + 1] : /* big-endian*/
	((long)(*s)[sl + 1] << 8) | (*s)[sl];  /* little-endian*/
      if (c >= 0xD800 && c <= 0xDFFF && (sl += 2) < *len - 1) { /* high-half*/
	c2 = (be) ? ((long)(*s)[sl] << 8) | (*s)[sl + 1] : /* big-endian*/
	  ((long)(*s)[sl + 1] << 8) | (*s)[sl];  /* little-endian*/
            c = (((c & 0x3FF) << 10) | (c2 & 0x3FF)) + 0x10000;
        }

        while (l + 6 > max) u = realloc(u, max += EZXML_BUFSIZE);
        if (c < 0x80) u[l++] = c; /* US-ASCII subset*/
        else { /* multi-byte UTF-8 sequence*/
	  for (b = 0, c2 = c; c2; c2 /= 2) b++; /* bits in c*/
	  b = (b - 2) / 5; /* bytes in payload;*/
	  u[l++] = (0xFF << (7 - b)) | (c >> (6 * b)); /*head*/
	  while (b) u[l++] = 0x80 | ((c >> (6 * --b)) & 0x3F); /* payload*/
        }
    }

    //return *s = realloc(u, *len = l);
    *len = l;
    u = realloc(u, l+1);    // Не убиваем исходную WSHDR!
    return u;
}


/*
  Обеспечивает преобразование кривого UTF-8 Сименса в UTF-8 для Jabber
*/
char* Correct_UTF8_String(char* utf8_str)
{
  int l = strlen(utf8_str)*2;
  // ^ так нельзя делать цикл, строка на самом длиннее, чем strlen
  int j=0;
  int i=0;
  char character = *utf8_str;
  while(character!='\0')
  {
    if(character!=0x1F)
    {
      utf8_str[j]=character;
      j++;
    }
    i++;
    character = *(utf8_str+i);
  }
  utf8_str[j]='\0';
  utf8_str = realloc(utf8_str, j+1);
  return utf8_str;
}


char * ANSI2UTF8(const char * ansi_str, unsigned int maxlen)
{
  uint32_t utf8_len = 0;
  WSHDR * ws_str = AllocWS(maxlen);
  ascii2ws(ws_str, ansi_str);
  char * utf8_str = malloc(maxlen * 2 + 1);
  ws_2utf8(ws_str, utf8_str, (int*)&utf8_len, maxlen * 2 + 1);
  FreeWS(ws_str);
  utf8_str = realloc(utf8_str, utf8_len + 1);
  utf8_str[utf8_len] = '\0';
  return utf8_str;
}

// From NatICQ

typedef struct
{
  unsigned short u;
  char dos;
  char win;
  char koi8;
} TUNICODE2CHAR;

const TUNICODE2CHAR unicode2char[]=
{
  // CAPITAL Cyrillic letters (base)
  0x410,0x80,0xC0,0xE1, // А
  0x411,0x81,0xC1,0xE2, // Б
  0x412,0x82,0xC2,0xF7, // В
  0x413,0x83,0xC3,0xE7, // Г
  0x414,0x84,0xC4,0xE4, // Д
  0x415,0x85,0xC5,0xE5, // Е
  0x416,0x86,0xC6,0xF6, // Ж
  0x417,0x87,0xC7,0xFA, // З
  0x418,0x88,0xC8,0xE9, // И
  0x419,0x89,0xC9,0xEA, // Й
  0x41A,0x8A,0xCA,0xEB, // К
  0x41B,0x8B,0xCB,0xEC, // Л
  0x41C,0x8C,0xCC,0xED, // М
  0x41D,0x8D,0xCD,0xEE, // Н
  0x41E,0x8E,0xCE,0xEF, // О
  0x41F,0x8F,0xCF,0xF0, // П
  0x420,0x90,0xD0,0xF2, // Р
  0x421,0x91,0xD1,0xF3, // С
  0x422,0x92,0xD2,0xF4, // Т
  0x423,0x93,0xD3,0xF5, // У
  0x424,0x94,0xD4,0xE6, // Ф
  0x425,0x95,0xD5,0xE8, // Х
  0x426,0x96,0xD6,0xE3, // Ц
  0x427,0x97,0xD7,0xFE, // Ч
  0x428,0x98,0xD8,0xFB, // Ш
  0x429,0x99,0xD9,0xFD, // Щ
  0x42A,0x9A,0xDA,0xFF, // Ъ
  0x42B,0x9B,0xDB,0xF9, // Ы
  0x42C,0x9C,0xDC,0xF8, // Ь
  0x42D,0x9D,0xDD,0xFC, // Э
  0x42E,0x9E,0xDE,0xE0, // Ю
  0x42F,0x9F,0xDF,0xF1, // Я
  // CAPITAL Cyrillic letters (additional)
  0x402,'_',0x80,'_', // _ .*.*
  0x403,'_',0x81,'_', // _ .*.*
  0x409,'_',0x8A,'_', // _ .*.*
  0x40A,'_',0x8C,'_', // _ .*.*
  0x40C,'_',0x8D,'_', // _ .*.*
  0x40B,'_',0x8E,'_', // _ .*.*
  0x40F,'_',0x8F,'_', // _ .*.*
  0x40E,0xF6,0xA1,'_', // Ў ...*
  0x408,0x4A,0xA3,0x4A, // _ .*.*
  0x409,0x83,0xA5,0xBD, // _ .*..
  0x401,0xF0,0xA8,0xB3, // Ё
  0x404,0xF2,0xAA,0xB4, // Є
  0x407,0xF4,0xAF,0xB7, // Ї
  0x406,0x49,0xB2,0xB6, // _ .*..
  0x405,0x53,0xBD,0x53, // _ .*.*
  // SMALL Cyrillic letters (base)
  0x430,0xA0,0xE0,0xC1, // а
  0x431,0xA1,0xE1,0xC2, // б
  0x432,0xA2,0xE2,0xD7, // в
  0x433,0xA3,0xE3,0xC7, // г
  0x434,0xA4,0xE4,0xC4, // д
  0x435,0xA5,0xE5,0xC5, // е
  0x436,0xA6,0xE6,0xD6, // ж
  0x437,0xA7,0xE7,0xDA, // з
  0x438,0xA8,0xE8,0xC9, // и
  0x439,0xA9,0xE9,0xCA, // й
  0x43A,0xAA,0xEA,0xCB, // к
  0x43B,0xAB,0xEB,0xCC, // л
  0x43C,0xAC,0xEC,0xCD, // м
  0x43D,0xAD,0xED,0xCE, // н
  0x43E,0xAE,0xEE,0xCF, // о
  0x43F,0xAF,0xEF,0xD0, // п
  0x440,0xE0,0xF0,0xD2, // р
  0x441,0xE1,0xF1,0xD3, // с
  0x442,0xE2,0xF2,0xD4, // т
  0x443,0xE3,0xF3,0xD5, // у
  0x444,0xE4,0xF4,0xC6, // ф
  0x445,0xE5,0xF5,0xC8, // х
  0x446,0xE6,0xF6,0xC3, // ц
  0x447,0xE7,0xF7,0xDE, // ч
  0x448,0xE8,0xF8,0xDB, // ш
  0x449,0xE9,0xF9,0xDD, // щ
  0x44A,0xEA,0xFA,0xDF, // ъ
  0x44B,0xEB,0xFB,0xD9, // ы
  0x44C,0xEC,0xFC,0xD8, // ь
  0x44D,0xED,0xFD,0xDC, // э
  0x44E,0xEE,0xFE,0xC0, // ю
  0x44F,0xEF,0xFF,0xD1, // я
  // SMALL Cyrillic letters (additional)
  0x452,'_',0x90,'_', // _ .*.*
  0x453,'_',0x83,'_', // _ .*.*
  0x459,'_',0x9A,'_', // _ .*.*
  0x45A,'_',0x9C,'_', // _ .*.*
  0x45C,'_',0x9D,'_', // _ .*.*
  0x45B,'_',0x9E,'_', // _ .*.*
  0x45F,'_',0x9F,'_', // _ .*.*
  0x45E,0xF7,0xA2,'_', // ў ...*
  0x458,0x6A,0xBC,0x6A, // _ .*.*
  0x491,0xA3,0xB4,0xAD, // _ .*..
  0x451,0xF1,0xB8,0xA3, // ё
  0x454,0xF3,0xBA,0xA4, // є
  0x457,0xF5,0xBF,0xA7, // ї
  0x456,0x69,0xB3,0xA6, // _ .*..
  0x455,0x73,0xBE,0x73, // _ .*.*
  0x0A0,'_',0xA0,0x20, // space .*..
  0x0A4,'_',0xA4,0xFD, // ¤   .*..
  0x0A6,'_',0xA6,'_', // ¦   .*.*
  0x0B0,0xF8,0xB0,0x9C, // °
  0x0B7,0xFA,0xB7,0x9E, // ·
  // 0x2022,,0x95,0x95, //    .*..
  // 0x2116,0xFC,0xB9,0x23, // №   ...*
  // 0x2219,,0xF9,0x9E, //    .*..
  // 0x221A,0xFB,,0x96, // v   ..*.
  // 0x25A0,0xFE,,0x94, // ¦
  0x0000,0,0,0
};

const unsigned short win2unicode[128]=
{
  0x0402,0x0403,0x201A,0x0453,0x201E,0x2026,0x2020,0x2021,
  0x20AC,0x2030,0x0409,0x2039,0x040A,0x040C,0x040B,0x040F,
  0x0452,0x2018,0x2019,0x201C,0x201D,0x2022,0x2013,0x2014,
  0x0020,0x2122,0x0459,0x203A,0x045A,0x045C,0x045B,0x045F,
  0x00A0,0x040E,0x045E,0x0408,0x00A4,0x0490,0x00A6,0x00A7,
  0x0401,0x00A9,0x0404,0x00AB,0x00AC,0x00AD,0x00AE,0x0407,
  0x00B0,0x00B1,0x0406,0x0456,0x0491,0x00B5,0x00B6,0x00B7,
  0x0451,0x2116,0x0454,0x00BB,0x0458,0x0405,0x0455,0x0457,
  0x0410,0x0411,0x0412,0x0413,0x0414,0x0415,0x0416,0x0417,
  0x0418,0x0419,0x041A,0x041B,0x041C,0x041D,0x041E,0x041F,
  0x0420,0x0421,0x0422,0x0423,0x0424,0x0425,0x0426,0x0427,
  0x0428,0x0429,0x042A,0x042B,0x042C,0x042D,0x042E,0x042F,
  0x0430,0x0431,0x0432,0x0433,0x0434,0x0435,0x0436,0x0437,
  0x0438,0x0439,0x043A,0x043B,0x043C,0x043D,0x043E,0x043F,
  0x0440,0x0441,0x0442,0x0443,0x0444,0x0445,0x0446,0x0447,
  0x0448,0x0449,0x044A,0x044B,0x044C,0x044D,0x044E,0x044F
};

unsigned int char8to16(int c)
{
  if (c>=128)
  {
    return(win2unicode[c-128]);
  }
  return(c);
}

unsigned int char16to8(unsigned int c)
{
  const TUNICODE2CHAR *p=unicode2char;
  unsigned int i;
  if (c<128) return(c);
  while((i=p->u))
  {
    if (c==i)
    {
      return(p->win);
    }
    p++;
  }
  c&=0xFF;
  if (c<32) return(' ');
  return(c);
}

void ascii2ws(WSHDR *ws, const char *s)
{
  char c;
  CutWSTR(ws,0);
  while((c=*s++))
  {
    wsAppendChar(ws,char8to16(c));
  }
}

void utf82win(char*d,const char *s)
{
  for (; *s; s+=2)
  {
    unsigned char ub = *s, lb = *(s+1);
    if (ub == 0xD0)
    {
                     if(lb == 0x81){*d = 0xA8;}//Ё
		else if(lb == 0x86){*d = 0xB2;}//І
		else if(lb == 0x87){*d = 0xAF;}//Ї
		else if(lb == 0x84){*d = 0xAA;}//Є
	  else {*d = lb + 48;}
    } else
    if (ub == 0xD1)
    {
                     if(lb == 0x91){*d = 0xB8;}//ё
		else if(lb == 0x96){*d = 0xB3;}//і
		else if(lb == 0x97){*d = 0xBF;}//ї
		else if(lb == 0x94){*d = 0xBA;}//є
	        else {*d = lb + 112;}
    } else
    if (ub == 0xD2)
    {
//        if(lb == 0x91){*d = 0xB4;}//ґ
//        if(lb == 0x90){*d = 0xA5;}//Ґ
        if(lb == 0x91){*d = 0xE3;}//ґ->г
        if(lb == 0x90){*d = 0xC3;}//Ґ->Г
    } else
    {
      *d = ub;
      s--;
    }
      d++;
  }
  *d = 0;
}

long GetIDLETime(TTime intime, TDate indate)
{
 TTime endt;
 TDate endd;
// TDate endd, resd;
 signed int res=0, rmin=0;
 long resul=0;
 GetDateTime(&endd, &endt);
 res = endt.sec - intime.sec;
 if (res < 0)
 {
   res = 60 + res;
   rmin = 1;
 }
 resul = res;
 res = endt.min - intime.min - rmin;
 rmin = 0;
 if (res <0 )
 {
   res = 60 + res;
   rmin = 1;
 }
 resul = resul + res*60;
 res = endt.hour - intime.hour - rmin;
 rmin = 0;
 if (res < 0)
 {
   res = 24 + res;
   rmin = 1;
 }
 resul = resul + res*3600;
 res = endd.day - indate.day - rmin;
 rmin = 0;
 if (res < 0)
 {
   res = 31 + res;
//   rmin=1;
 }
 resul = resul + res*86400;
/*
 res = end.mounth - indate.mounth - rmin;
 rmin = 0;
 if (res < 0 )
 {
   res = 12 + res;
 }
 res = resul + res*2678400; mnogovato trowki
   */
 return(resul);
}

char *utf82filename(char *str)
{
  int len = strlen(str)+16;
  WSHDR *ws = AllocWS(len);
  char *res = malloc(len);
  utf8_2ws(ws, str, len);
  ws_2str(ws, res, len);
  FreeWS(ws);
  return res;
}

void CutWSStringWidth(WSHDR *ws, int width, int font)
{
  unsigned short *wsbody=ws->wsbody;
  int wi = 0;
  int index=0;
  while((wi<width)&&(index<=wsbody[0]))
  {
    index++;
    wi += GetSymbolWidth(wsbody[index],font);
  }
  CutWSTR(ws, index-1);
}

const char badchars[] = {'?', '*', '"', ':', '<', '>', '/', '\\', '|', '\n', '\r', ','};

void remove_bad_chars(char *s)
{
  int c;
  while((c=*s))
  {
    for (uint32_t i=0; i<(sizeof(badchars)/sizeof(char)); i++)
    {
      if (c==badchars[i])
      {
        *s='_';
        break;
      }
    }
    s++;
  }
}
//EOL,EOF

