using Microsoft.Extensions.Primitives;
using System.Collections.Specialized;
using System.Data;
using System.Globalization;
using System.IO.Compression;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;

namespace Kit.Helpers
{
    using Microsoft.AspNetCore.Http;
    using System;
    using static System.Net.Mime.MediaTypeNames;

    public enum GenerateTokenAlphabetMode
    {
        None = 0,
        AnyCase = 1,
        LowerCase = 2,
        UpperCase = 3,
    }

    public enum GenerateTokenDigitMode
    {
        None = 0,
        Dec = 1,
        Hex = 2
    }

    public static partial class SystemStringExtensionMethods
    {
        private static readonly CultureInfo ci = new CultureInfo("en-US");

        #region преобразования строк
        /// <summary>
        /// Вставляем подстроку в сроку
        /// </summary>
        /// <param name="thisString">исходная строка</param>
        /// <param name="startIndex">куда вставлять</param>
        /// <param name="value">строка для вставки</param>
        /// <param name="count">количество повторений при вставке</param>
        /// <returns>преобразованная строка</returns>
        public static string Insert(this string thisString, int startIndex, string value, int count)
        {
            StringBuilder sb = new StringBuilder(thisString);
            sb.Insert(startIndex, value, count);
            return sb.ToString();
        }
        /// <summary>
        /// Применяем функцию String.Format к текущей строке
        /// </summary>
        /// <param name="formatString">строка с форматом</param>
        /// <param name="args">аргументы преобразования</param>
        /// <returns>преобразованная строка</returns>
        public static string ApplyFormat(this string formatString, params object[] args)
        {
            if (formatString.IsNullOrEmpty())
                return String.Empty;
            return String.Format(formatString, args);
        }
        /// <summary>
        /// Применяем функция String.Format к параметру, используя текущую строку как аргумент
        /// </summary>
        /// <param name="thisString">текущая строка - аргумент операции</param>
        /// <param name="formatString">строка форматирования</param>
        /// <returns>преобразованная строка</returns>
        public static string ApplyToFormat(this string thisString, string formatString)
        {
            return String.Format(formatString, thisString);
        }
        /// <summary>
        /// Проверка строки на null или пустое значение
        /// </summary>
        /// <param name="thisString">Строка для проверки</param>
        /// <returns>true - если строка null или пустая, иначе false</returns>
        public static bool IsNullOrEmpty(this string thisString)
        {
            return String.IsNullOrEmpty(thisString);
        }
        /// <summary>
        /// Замена исходной строки на другую, если она пустая или null
        /// </summary>
        /// <param name="thisString">Проверяемая строка</param>
        /// <param name="otherString">Строка для замены</param>
        /// <returns>строка для замены, если исходная строка равна null или пустая, иначе исходная строка</returns>
        public static string IfNullOrEmpty(this string thisString, string otherString)
        {
            return !String.IsNullOrEmpty(thisString) ? thisString : otherString;
        }
        /// <summary>
        /// Замена исходной строки на другую, если она пустая или null
        /// </summary>
        /// <param name="thisString">Проверяемая строка</param>
        /// <param name="otherString">Строка для замены</param>
        /// <returns>строка для замены, если исходная строка равна null или пустая, иначе исходная строка</returns>
        public static string IfNullOrWhiteSpace(this string thisString, string otherString)
        {
            return !String.IsNullOrWhiteSpace(thisString) ? thisString : otherString;
        }
        /// <summary>
        /// Удаление подстроки
        /// </summary>
        /// <param name="input">Строка в которой идет поиск</param>
        /// <param name="remove">Подстрока, которую необходимо удалить</param>
        /// <returns>Преобразованная строка</returns>
        public static string? Remove(this string? input, string remove)
        {
            if (input == null) return null;
            return input.Replace(remove, String.Empty);
        }

        /// <summary>
        /// Обрезка строки по символу.
        /// </summary>
        /// <param name="input">Строка которую обрезаем</param>
        /// <param name="lenght">Максимальная длинна строки</param>
        /// <param name="end">Максимальная длинна строки</param>
        /// <returns>Обрезанная строка</returns>
        public static string CutString(this string input, int lenght, char end)
        {
            string result = input;
            if (lenght < input.Length)
            {
                string tmpstr = input.Substring(0, lenght);
                string[] arrstr = tmpstr.Split(end);
                if (lenght > 1)
                    result = arrstr.Take(arrstr.Length - 1).ToArray().Join(end.ToString()) + end.ToString();
            }
            return result;
        }
        #endregion

        #region Методы для перечислений
        /// <summary>
        /// Разбираем строку в элемент перечисления
        /// </summary>
        /// <typeparam name="T">тип перечисления</typeparam>
        /// <param name="value">строка</param>
        /// <param name="ignoreCase">игнорировать регистр</param>
        /// <returns>типизированный элемент перечисления</returns>
        public static T ParseToEnum<T>(this string value, bool ignoreCase)
        {
            return (T)Enum.Parse(typeof(T), value.Trim(), ignoreCase);
        }
        /// <summary>
        /// Разбираем строку в элемент перечисления
        /// с учетом регистра
        /// </summary>
        /// <typeparam name="T">тип перечисления</typeparam>
        /// <param name="value">строка</param>
        /// <returns>типизированный элемент перечисления</returns>
        public static T ParseToEnum<T>(this string value)
        {
            return value.ParseToEnum<T>(true);
        }

        public static T ParseToEnum<T>(this string value, bool ignoreCase, T defaultValue)
        {
            try
            {
                return (T)Enum.Parse(typeof(T), value.Trim(), ignoreCase);
            }
            catch (Exception)
            {
                return defaultValue;
            }
        }

        #endregion

        #region Virtual Path Utility
        /*
        /// <summary>
        /// Заменяем пути относительные приложения на пути относительные сервера
        /// </summary>
        /// <param name="text">текст со ссылками</param>
        /// <returns>текст с измененными ссылками</returns>
        public static string TextUrlsToAbsolute(this string text)
        {
            string basePath = VirtualPathUtility.ToAbsolute("~/");
            return text.Replace("~/", basePath);
        }
        */

        /*
        /// <summary>
        /// Заменяем пути к текущей теме на реальные.
        /// Заменяем пути относительные приложения на пути относительные сервера
        /// </summary>
        /// <param name="text">текст со ссылками</param>
        /// <param name="theme">имя текущей темы</param>
        /// <returns>текст с измененными ссылками</returns>
        public static string TextThemeUrlsToAbsolute(this string text, string theme)
        {
            string themePath = "~/App_Themes/{0}/".ApplyFormat(theme);
            return text.Replace("~/CurrentTheme/", themePath).TextUrlsToAbsolute();
        }
        */
        #endregion

        #region StringBuilder

        public static StringBuilder AppendFormatLine(this StringBuilder sb, string formatString, params object[] args)
        {
            if (!formatString.IsNullOrEmpty()) sb.AppendLine(formatString.ApplyFormat(args));
            return sb;
        }

        #endregion


        public static int ParseIPToInt32(this string ipString)
        {
            int ipInt32 = 0;
            int q = 1;
            foreach (string part in ipString.Split('.').Reverse())
            {
                ipInt32 += Int32.Parse(part) * q;
                q *= 256;
            }
            return ipInt32;
        }

        public static string? CleanFromHtmlSymbols(this string? text)
        {
            if (text == null)
            {
                return null;
            }

            text = text.Replace("&amp;", "&");
            text = text.Replace("&nbsp;", " ");
            text = text.Replace("&laquo;", "\"");
            text = text.Replace("&raquo;", "\"");
            text = text.Replace("&ndash;", "-");
            text = text.Replace("&#8213;", "-");
            return text;
        }

        public static string CleanFromHtmlTags(this string text)
        {
            text.CleanFromHtmlSymbols();

            text = Regex.Replace(text, @"\x26\S*?\x3B", " ", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Compiled);//&...;
            text = Regex.Replace(text, @"\<\s*?p\s*?.*?\>", " ", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Compiled);//<p> </p>
            text = Regex.Replace(text, @"\<\s*?br\s*?.*?\>", "\n", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Compiled);// <br> <br/>            
            text = Regex.Replace(text, @"\<(.+?)\>", " ", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Compiled);// <...>
            text = Regex.Replace(text, @"\</(.+?)\>", " ", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Compiled);// </...>
            return text;
        }

        public static string ToFirstSymbolUppper(this string input)
        {
            string output = string.Empty;
            if (input != null && input.Length > 0)
            {
                output = input.Substring(0, 1).ToUpper();
                if (input.Length > 1) output += input.Substring(1, input.Length - 1);
            }
            return output;
        }

        public static string ToFirstSymbolUppperOnly(this string input)
        {
            string output = string.Empty;
            if (input != null && input.Length > 0)
            {
                output = input.Substring(0, 1).ToUpper();
            }
            return output;
        }


        #region преобразования типов

        public static Int32? TryParseToInt32Nulable(this string input)
        {
            Int32 intValue;
            if (!Int32.TryParse(input, out intValue)) return null;
            return intValue;
        }

        public static int TryParseToInt32(this string input)
        {
            int intValue = 0;
            int.TryParse(input, out intValue);
            return intValue;
        }

        public static int TryParseToInt32(this string? input, int defaultValue)
        {
            int result;
            if (int.TryParse(input, out result)) return result;
            return defaultValue;
        }

        public static Int32? TryParseToInt32Nulable(this StringValues input)
        {
            Int32 intValue;
            if (!Int32.TryParse(input, out intValue)) return null;
            return intValue;
        }

        public static Int32 TryParseToInt32(this StringValues input)
        {
            Int32 intValue = 0;
            Int32.TryParse(input, out intValue);
            return intValue;
        }

        public static Int32 TryParseToInt32(this StringValues input, Int32 defaultValue)
        {
            int result;
            if (Int32.TryParse(input, out result)) return result;
            return defaultValue;
        }

        public static UInt32 TryParseToUInt32(this string input)
        {
            UInt32 intValue = 0;
            UInt32.TryParse(input, out intValue);
            return intValue;
        }
        public static UInt32 TryParseToUInt32(this string input, UInt32 defaultValue)
        {
            uint result;
            if (UInt32.TryParse(input, out result)) return result;
            return defaultValue;
        }
        public static UInt64 TryParseToUInt64(this string input)
        {
            ulong intValue = 0;
            UInt64.TryParse(input, out intValue);
            return intValue;
        }
        public static UInt64 TryParseToUInt64(this string input, UInt64 defaultValue)
        {
            ulong result;
            if (UInt64.TryParse(input, out result)) return result;
            return defaultValue;
        }
        public static UInt64? TryParseToUInt64Nulable(this string input)
        {
            UInt64 intValue;
            if (!UInt64.TryParse(input, out intValue)) return null;
            return intValue;
        }
        public static Decimal TryParseToDecimal(this string input, Decimal defaultValue)
        {
            Decimal.TryParse(input, out defaultValue);
            return defaultValue;
        }

        public static Decimal TryParseToDecimal(this string input)
        {
            Decimal decimalValue = 0;
            Decimal.TryParse(input, out decimalValue);
            return decimalValue;
        }

        public static Decimal TryParseToDecimal(this string input, IFormatProvider fmtPrvdr, Decimal defaultValue)
        {
            Decimal.TryParse(input, NumberStyles.Number, fmtPrvdr, out defaultValue);
            return defaultValue;
        }

        public static Double TryParseToDouble(this string input, Double defaultValue)
        {
            Double.TryParse(input, out defaultValue);
            return defaultValue;
        }

        public static Double TryParseToDouble(this string input)
        {
            Double decimalValue = 0;
            Double.TryParse(input, out decimalValue);
            return decimalValue;
        }

        public static Double TryParseToDouble(this string input, IFormatProvider fmtPrvdr, Double defaultValue)
        {
            Double.TryParse(input, NumberStyles.Number, fmtPrvdr, out defaultValue);
            return defaultValue;
        }

        public static IEnumerable<Int32> ParseToInt32Collection(this string input)
        {
            return input.ParseToInt32Collection(",");
        }
        public static IEnumerable<Int32> ParseToInt32Collection(this string input, string separator)
        {
            if (input.IsNullOrEmpty()) return new List<Int32>();
            return input
                    .Split(separator.ToCharArray())
                    .Select(str => str.TryParseToInt32Nulable())
                    .Where(intValue => intValue != null)
                    .Select(intValue => intValue ?? 0)
                    .ToList();
        }

        public static IEnumerable<short> ParseToShortCollection(this string input)
        {
            return input.ParseToShortCollection(",");
        }
        public static IEnumerable<short> ParseToShortCollection(this string input, string separator)
        {
            if (input.IsNullOrEmpty()) return new List<short>();
            return input
                    .Split(separator.ToCharArray())
                    .Select(str => str.TryParseToShort())
                    .ToList();
        }

        public static short TryParseToShort(this string input)
        {
            short intValue = 0;
            short.TryParse(input, out intValue);
            return intValue;
        }

        public static Int64 TryParseToShort(this string input, short defaultValue)
        {
            short.TryParse(input, out defaultValue);
            return defaultValue;
        }

        public static Int64? TryParseToInt64Nulable(this string input)
        {
            Int64 intValue;
            if (!Int64.TryParse(input, out intValue)) return null;
            return intValue;
        }

        public static Int64 TryParseToInt64(this string input)
        {
            Int64 intValue = 0;
            Int64.TryParse(input, out intValue);
            return intValue;
        }

        public static long TryParseToInt64(this string? input, long defaultValue)
        {
            if (long.TryParse(input, out long value))
            {
                return value;
            }
            return defaultValue;
        }

        public static IEnumerable<Int64> ParseToInt64Collection(this string input)
        {
            return input.ParseToInt64Collection(",");
        }
        public static IEnumerable<Int64> ParseToInt64Collection(this string input, string separator)
        {
            if (input.IsNullOrEmpty()) return new List<Int64>();
            return input
                    .Split(separator.ToCharArray())
                    .Select(str => str.TryParseToInt64Nulable())
                    .Where(intValue => intValue != null)
                    .Select(intValue => intValue ?? 0);
        }

        public static NameValueCollection ParseToNameValueCollection(this string input, char parameterSeparator, char nameValueSeparator)
        {
            NameValueCollection collection = new NameValueCollection();
            if (!input.IsNullOrEmpty())
                foreach (string item in input.Split(parameterSeparator))
                {
                    string[] parameter = item.Split(nameValueSeparator);
                    if (parameter.Length == 2)
                    {
                        collection.Add(parameter[0], parameter[1]);
                    }
                }
            return collection;
        }

        public static NameValueCollection ParseToNameValueCollection(this string input)
        {
            return input.ParseToNameValueCollection(';', ':');
        }

        public static IEnumerable<string> ParseToSplitCollection(this string value, char parameterSeparator)
        {
            if (value.IsNullOrEmpty()) return new List<string>();
            return value.Split(parameterSeparator);
        }

        public static DateTime? TryParseToDateTimeNullable(this string value)
        {
            return value.TryParseToDateTimeNullable(null);
        }

        public static DateTime? TryParseToDateTimeNullable(this string value, DateTime? defaultValue)
        {
            DateTime td;
            if (DateTime.TryParse(value, out td)) return td;
            return defaultValue;
        }

        public static DateTime TryParseToDateTimeUTC(this string value, DateTime defaultValue)
        {
            DateTime td;
            var culture = CultureInfo.CreateSpecificCulture("ru-Ru");
            var styles = DateTimeStyles.AdjustToUniversal;

            if (DateTime.TryParse(value, culture, styles, out td)) return td;
            return defaultValue;
        }
        public static DateTime TryParseToDateDMYNullable(this string value, DateTime defaultValue)
        {
            if (value.IsNullOrEmpty()) return defaultValue;
            var dtvalues = value.Replace("-", "/").Replace(".", "/").ParseToInt32Collection("/").ToArray();
            DateTime dt = DateTime.MaxValue;
            if (dtvalues.Count() == 3)
                try
                {
                    defaultValue = new DateTime(dtvalues[2], dtvalues[1], dtvalues[0]);
                }
                catch (Exception) { }
            return defaultValue;
        }

        public static DateTime? TryParseToDateDMYNullable(this string value)
        {
            DateTime? defaultValue = null;
            if (value.IsNullOrEmpty()) return defaultValue;
            var dtvalues = value.Replace("-", "/").Replace(".", "/").ParseToInt32Collection("/").ToArray();
            DateTime dt = DateTime.MaxValue;
            if (dtvalues.Count() == 3)
                try
                {
                    defaultValue = new DateTime(dtvalues[2], dtvalues[1], dtvalues[0]);
                }
                catch (Exception) { }
            return defaultValue;
        }

        public static DateTime? TryParseToDateDMYHMSNullable(this string value)
        {
            DateTime? defaultValue = null;
            if (value.IsNullOrEmpty()) return defaultValue;
            var dtvalues = value.TrimStart().TrimEnd().Replace("-", "/").Replace(".", "/").Replace(" ", "/").Replace(":", "/").ParseToInt32Collection("/").ToArray();
            DateTime dt = DateTime.MaxValue;
            if (dtvalues.Count() == 6)
                try
                {
                    defaultValue = new DateTime(dtvalues[2], dtvalues[1], dtvalues[0], dtvalues[3], dtvalues[4], dtvalues[5]);
                }
                catch (Exception ex)
                {
                    var z = ex;
                }
            return defaultValue;
        }

        public static DateTime TryParseToDateDMYFromExcel(this string value, DateTime defaultValue)
        {

            return value.TryParseToDateDMYFromExcelNullable() ?? defaultValue;
        }
        public static DateTime? TryParseToDateDMYFromExcelNullable(this string value)
        {
            DateTime? defaultValue = null;
            var days = value.TryParseToInt32();
            if (days != 0)
            {
                return new DateTime(1899, 12, 30).AddDays(days);
            }

            if (value.IsNullOrEmpty()) return defaultValue;
            var dtvalues = value.Replace("-", "/").Replace(".", "/").ParseToInt32Collection("/").ToArray();
            DateTime dt = DateTime.MaxValue;
            if (dtvalues.Count() == 3)
                try
                {
                    defaultValue = new DateTime(dtvalues[2], dtvalues[1], dtvalues[0]);
                }
                catch (Exception) { }

            return defaultValue;
        }

        public static Encoding TryParseToEncoding(this string value, Encoding defaultValue)
        {
            Encoding retVal = defaultValue;
            try
            {
                retVal = Encoding.GetEncoding(value);
            }
            catch (Exception) { } // swallow any exceptions

            return retVal;
        }

        #endregion

        public static bool ToLowerEquals(this string? one, string? other, bool nullEquals)
        {
            if (one != null && other != null) return one.ToLower() == other.ToLower();
            if (one == null && other == null) return nullEquals;
            return false;
        }

        public static bool ToLowerEquals(this string? one, string? other)
        {
            return one.ToLowerEquals(other, false);
        }

        public static bool ToLowerContains(this string? one, string? other, bool nullEquals)
        {
            if (one != null && other != null) return one.ToLower().Contains(other.ToLower());
            if (one == null && other == null) return nullEquals;
            return false;
        }
        public static bool ToLowerContains(this string? one, string? other)
        {
            return one.ToLowerContains(other, false);
        }
        public static bool ToLowerStartsWith(this string? one, string? other, bool nullEquals)
        {
            if (one != null && other != null) return one.ToLower().StartsWith(other.ToLower());
            if (one == null && other == null) return nullEquals;
            return false;
        }
        public static bool ToLowerStartsWith(this string? one, string? other)
        {
            return one.ToLowerStartsWith(other, false);
        }
        public static bool TryParseToBool(this string? input, bool defaultValue)
        {
            bool result;
            if (!bool.TryParse(input, out result))
                return defaultValue;
            return result;
        }
        public static bool TryParseToBool(this string? input, string trueValue, bool defaultValue)
        {
            bool result;
            if (!bool.TryParse(input.ToLowerContains(trueValue) ? "true" : "false", out result))
                return defaultValue;
            return result;
        }
        public static bool TryParseToBool(this StringValues input, bool defaultValue)
        {
            bool result;
            if (!bool.TryParse(input, out result))
                return defaultValue;
            return result;
        }
        public static bool TryParseToBool(this StringValues input, string trueValue, bool defaultValue)
        {
            bool result;
            if (!bool.TryParse(input.ToString().ToLowerContains(trueValue) ? "true" : "false", out result))
                return defaultValue;
            return result;
        }
        public static bool IsExactlyEqualToBoolValue(this string input, bool theValueToCompareWith)
        {
            bool parsingOutput;
            if (!bool.TryParse(input, out parsingOutput))
                return false;
            return parsingOutput == theValueToCompareWith;
        }
        public static bool IsNotExactlyEqualToBoolValue(this string input, bool theValueToCompareWith)
        {
            bool parsingOutput;
            if (!bool.TryParse(input, out parsingOutput))
                return true;
            return parsingOutput != theValueToCompareWith;
        }


        #region MarkUp Helpers
        public static string NotEmptyAttributeString(this string name, string value)
        {
            return value.IsNullOrEmpty()
                   ? String.Empty
                   : "{0}=\"{1}\"".ApplyFormat(name, value);
        }
        #endregion

        #region Generic Types
        public static Type GetGenericType(this string type, params string[] typeArgs)
        {
            Type openType = Type.GetType(type, true);
            Type[] types = typeArgs
                           .Select(tname => Type.GetType(tname))
                           .Where(x => x != null)
                           .Select(x => x!)
                           .ToArray();
            return openType.MakeGenericType(types);
        }
        public static object GetGenericTypeInstance(this string type, params string[] typeArgs)
        {
            Type genericType = type.GetGenericType(typeArgs);
            return Activator.CreateInstance(genericType);
        }
        public static object GetGenericTypeInstance(this string type, string[] typeArgs, object[] parameters)
        {
            Type genericType = type.GetGenericType(typeArgs);
            return Activator.CreateInstance(genericType, parameters);
        }
        #endregion

        public static string MapPath(string path)
        {
            return Path.Combine(
                (string)AppDomain.CurrentDomain.GetData("ContentRootPath"),
                path);
        }

        public static string ServerMapPath(this string virtualPath, HttpContext httpContext)
        {
            //HttpContext httpContext = HttpContext.Current;

            string rootPath = httpContext != null
                              ? MapPath("~/")
                              : Environment.CurrentDirectory;

            virtualPath = virtualPath.Remove("~/").Replace('/', '\\');

            return Path.Combine(rootPath, virtualPath);
        }

        public static string ServerMapPath(this string virtualPath)
        {
            if (virtualPath.StartsWith("~/") == false)
            {
                return virtualPath;
            }

            string result = Environment.CurrentDirectory;
            virtualPath = virtualPath.Remove("~/");
            string[] directories = virtualPath.Split('/');
            foreach (string directory in directories)
            {
                result = Path.Combine(result, directory);
            }
            return result;
        }

        #region CSV File
        public static DataTable ReadCSV(this string filePath, char separator = ';')
        {
            DataTable table = new DataTable();

            using (var reader = File.OpenText(filePath))
            {
                if (reader.EndOfStream == true) return table;
                //columns
                var columns = from columnName in reader.ReadLine().Split(separator)
                              select new DataColumn(columnName);
                table.Columns.AddRange(columns.ToArray());
                //rows
                while (reader.EndOfStream == false)
                {
                    string line = reader.ReadLine();
                    if (line.IsNullOrEmpty()) continue;

                    var values = line.Split(separator);
                    var row = table.NewRow();
                    for (int index = 0; index < table.Columns.Count; index++) row[index] = values[index];
                    table.Rows.Add(row);
                }
                reader.Close();
            }
            return table;
        }

        public static void WriteCsv(this string filePath, DataTable table, char separator = ';', string[] columnNames = null)
        {
            if (columnNames == null) columnNames = table.Columns.Cast<DataColumn>().Select(c => c.ColumnName).ToArray();
            StringBuilder content = new StringBuilder();
            content.AppendLine(columnNames.Join(separator.ToString()));
            foreach (DataRow row in table.Rows)
            {
                bool firstColumn = true;
                foreach (var columnName in columnNames)
                {
                    if (firstColumn) { firstColumn = false; } else { content.Append(separator); }
                    content.Append(row[columnName]);
                }
                content.AppendLine();
            }
            File.WriteAllText(filePath, content.ToString(), Encoding.UTF8);
        }
        #endregion

        public static string Clear(this string sourceString)
        {
            #region chars map

            var map = new Dictionary<char, string>(33);
            map.Add('а', "a");
            map.Add('б', "b");
            map.Add('в', "v");
            map.Add('г', "g");
            map.Add('д', "d");
            map.Add('е', "e");
            map.Add('ё', "e");
            map.Add('ж', "zh");
            map.Add('з', "z");
            map.Add('и', "i");
            map.Add('й', "y");
            map.Add('к', "k");
            map.Add('л', "l");
            map.Add('м', "m");
            map.Add('н', "n");
            map.Add('о', "o");
            map.Add('п', "p");
            map.Add('р', "r");
            map.Add('с', "s");
            map.Add('т', "t");
            map.Add('у', "u");
            map.Add('ф', "f");
            map.Add('х', "h");
            map.Add('ц', "c");
            map.Add('ч', "ch");
            map.Add('ш', "sh");
            map.Add('щ', "sh");
            map.Add('ь', "");
            map.Add('ы', "i");
            map.Add('ъ', "");
            map.Add('э', "e");
            map.Add('ю', "u");
            map.Add('я', "ya");

            map.Add('А', "A");
            map.Add('Б', "B");
            map.Add('В', "V");
            map.Add('Г', "G");
            map.Add('Д', "D");
            map.Add('Е', "E");
            map.Add('Ё', "E");
            map.Add('Ж', "Zh");
            map.Add('З', "Z");
            map.Add('И', "I");
            map.Add('Й', "Y");
            map.Add('К', "K");
            map.Add('Л', "L");
            map.Add('М', "M");
            map.Add('Н', "N");
            map.Add('О', "O");
            map.Add('П', "P");
            map.Add('Р', "R");
            map.Add('С', "S");
            map.Add('Т', "T");
            map.Add('У', "U");
            map.Add('Ф', "F");
            map.Add('Х', "H");
            map.Add('Ц', "C");
            map.Add('Ч', "Ch");
            map.Add('Ш', "Sh");
            map.Add('Щ', "Sh");
            map.Add('Ь', "");
            map.Add('Ы', "I");
            map.Add('Ъ', "");
            map.Add('Э', "E");
            map.Add('Ю', "U");
            map.Add('Я', "Ya");

            map.Add('a', "a");
            map.Add('b', "b");
            map.Add('c', "c");
            map.Add('d', "d");
            map.Add('e', "e");
            map.Add('f', "f");
            map.Add('g', "g");
            map.Add('h', "h");
            map.Add('i', "i");
            map.Add('j', "j");
            map.Add('k', "k");
            map.Add('l', "l");
            map.Add('m', "m");
            map.Add('n', "n");
            map.Add('o', "o");
            map.Add('p', "p");
            map.Add('q', "q");
            map.Add('r', "r");
            map.Add('s', "s");
            map.Add('t', "t");
            map.Add('u', "u");
            map.Add('v', "v");
            map.Add('w', "w");
            map.Add('x', "x");
            map.Add('y', "y");
            map.Add('z', "z");

            map.Add('A', "A");
            map.Add('B', "B");
            map.Add('C', "C");
            map.Add('D', "D");
            map.Add('E', "E");
            map.Add('F', "F");
            map.Add('G', "G");
            map.Add('H', "H");
            map.Add('I', "I");
            map.Add('J', "J");
            map.Add('K', "K");
            map.Add('L', "L");
            map.Add('M', "M");
            map.Add('N', "N");
            map.Add('O', "O");
            map.Add('P', "P");
            map.Add('Q', "Q");
            map.Add('R', "R");
            map.Add('S', "S");
            map.Add('T', "T");
            map.Add('U', "U");
            map.Add('V', "V");
            map.Add('W', "W");
            map.Add('X', "X");
            map.Add('Y', "Y");

            map.Add('1', "1");
            map.Add('2', "2");
            map.Add('3', "3");
            map.Add('4', "4");
            map.Add('5', "5");
            map.Add('6', "6");
            map.Add('7', "7");
            map.Add('8', "8");
            map.Add('9', "9");
            map.Add('0', "0");

            map.Add('-', "-");
            map.Add(' ', " ");
            map.Add('.', ".");
            map.Add('_', "_");

            #endregion

            var sb = new StringBuilder(sourceString.Count());
            sourceString.Where(x => map.ContainsKey(x)).ForEach(x => sb.Append(x));

            return sb.ToString();
        }

        public static string TranslitToEnglish(
            this string russian,
            int maxLength = 512,
            bool replaceOthers = true,
            char otherSubstitute = '-',
            char spaceSubstitute = '-',
            bool toLower = true,
            bool deleteDoubleSpace = true)
        {

            #region chars map

            var map = new Dictionary<char, string>(33);
            map.Add('а', "a");
            map.Add('б', "b");
            map.Add('в', "v");
            map.Add('г', "g");
            map.Add('д', "d");
            map.Add('е', "e");
            map.Add('ё', "e");
            map.Add('ж', "zh");
            map.Add('з', "z");
            map.Add('и', "i");
            map.Add('й', "y");
            map.Add('к', "k");
            map.Add('л', "l");
            map.Add('м', "m");
            map.Add('н', "n");
            map.Add('о', "o");
            map.Add('п', "p");
            map.Add('р', "r");
            map.Add('с', "s");
            map.Add('т', "t");
            map.Add('у', "u");
            map.Add('ф', "f");
            map.Add('х', "h");
            map.Add('ц', "c");
            map.Add('ч', "ch");
            map.Add('ш', "sh");
            map.Add('щ', "sh");
            map.Add('ь', "");
            map.Add('ы', "i");
            map.Add('ъ', "");
            map.Add('э', "e");
            map.Add('ю', "u");
            map.Add('я', "ya");

            map.Add('А', "A");
            map.Add('Б', "B");
            map.Add('В', "V");
            map.Add('Г', "G");
            map.Add('Д', "D");
            map.Add('Е', "E");
            map.Add('Ё', "E");
            map.Add('Ж', "Zh");
            map.Add('З', "Z");
            map.Add('И', "I");
            map.Add('Й', "Y");
            map.Add('К', "K");
            map.Add('Л', "L");
            map.Add('М', "M");
            map.Add('Н', "N");
            map.Add('О', "O");
            map.Add('П', "P");
            map.Add('Р', "R");
            map.Add('С', "S");
            map.Add('Т', "T");
            map.Add('У', "U");
            map.Add('Ф', "F");
            map.Add('Х', "H");
            map.Add('Ц', "C");
            map.Add('Ч', "Ch");
            map.Add('Ш', "Sh");
            map.Add('Щ', "Sh");
            map.Add('Ь', "");
            map.Add('Ы', "I");
            map.Add('Ъ', "");
            map.Add('Э', "E");
            map.Add('Ю', "U");
            map.Add('Я', "Ya");

            map.Add('a', "a");
            map.Add('b', "b");
            map.Add('c', "c");
            map.Add('d', "d");
            map.Add('e', "e");
            map.Add('f', "f");
            map.Add('g', "g");
            map.Add('h', "h");
            map.Add('i', "i");
            map.Add('j', "j");
            map.Add('k', "k");
            map.Add('l', "l");
            map.Add('m', "m");
            map.Add('n', "n");
            map.Add('o', "o");
            map.Add('p', "p");
            map.Add('q', "q");
            map.Add('r', "r");
            map.Add('s', "s");
            map.Add('t', "t");
            map.Add('u', "u");
            map.Add('v', "v");
            map.Add('w', "w");
            map.Add('x', "x");
            map.Add('y', "y");
            map.Add('z', "z");

            map.Add('A', "A");
            map.Add('B', "B");
            map.Add('C', "C");
            map.Add('D', "D");
            map.Add('E', "E");
            map.Add('F', "F");
            map.Add('G', "G");
            map.Add('H', "H");
            map.Add('I', "I");
            map.Add('J', "J");
            map.Add('K', "K");
            map.Add('L', "L");
            map.Add('M', "M");
            map.Add('N', "N");
            map.Add('O', "O");
            map.Add('P', "P");
            map.Add('Q', "Q");
            map.Add('R', "R");
            map.Add('S', "S");
            map.Add('T', "T");
            map.Add('U', "U");
            map.Add('V', "V");
            map.Add('W', "W");
            map.Add('X', "X");
            map.Add('Y', "Y");

            map.Add('1', "1");
            map.Add('2', "2");
            map.Add('3', "3");
            map.Add('4', "4");
            map.Add('5', "5");
            map.Add('6', "6");
            map.Add('7', "7");
            map.Add('8', "8");
            map.Add('9', "9");
            map.Add('0', "0");

            #endregion

            var result = new StringBuilder();
            int i = 0;
            foreach (char c in russian)
            {
                ++i;
                if (c == ' ')
                {
                    if (i >= maxLength)
                    {
                        break;
                    }
                    result.Append(spaceSubstitute);
                }
                else if (map.ContainsKey(c)) result.Append(map[c]);
                else if (!replaceOthers) result.Append(c);
                else result.Append(otherSubstitute);
            }
            string resultKey = result.ToString()
                                     .Trim(spaceSubstitute);

            if (toLower) resultKey = resultKey.ToLower();
            if (deleteDoubleSpace)
            {
                string doubleSpace = "{0}{0}".ApplyFormat(spaceSubstitute);
                while (resultKey.Contains(doubleSpace)) resultKey = resultKey.Replace(doubleSpace, spaceSubstitute.ToString());
            }
            return resultKey;
        }


        private static readonly object LockLog = new object();
        public static void AppendTextToFile(this string path, string format, HttpContext httpContext, params object[] parameters)
        {
            lock (LockLog)
            {
                var filePath = path.ServerMapPath(httpContext);

                // if (!File.Exists(filePath)) File.Create(filePath);

                using (var writer = System.IO.File.AppendText(filePath))
                {
                    writer.WriteLine(format, parameters);
                }
            }
        }

        public static string Convert(this string source, System.Text.Encoding encoding)
        {
            if (string.IsNullOrEmpty(source)) return string.Empty;
            Encoding utf8 = Encoding.Default;
            byte[] utf8Bytes = encoding.GetBytes(source);
            byte[] win1251Bytes = Encoding.Convert(utf8, encoding, utf8Bytes);

            return encoding.GetString(win1251Bytes);
        }

        public static string ConvertToBase64(this string source)
        {
            if (string.IsNullOrEmpty(source)) return string.Empty;
            return System.Convert.ToBase64String(new UTF8Encoding(false).GetBytes(source));
        }

        public static string ConvertFromBase64(this string source)
        {
            if (string.IsNullOrEmpty(source)) return string.Empty;
            return new UTF8Encoding(false).GetString(System.Convert.FromBase64String(source));
        }

        public static bool CheckBase64Format(this string source)
        {
            if (string.IsNullOrEmpty(source)) return true;

            try
            {
                System.Convert.FromBase64CharArray(source.ToCharArray(), 0, source.Length);
            }
            catch
            {
                return false;
            }

            return true;
        }

        //public static string ContentBlockReplace(this string content)
        //{
        //    return content;
        //    //return  content.Replace("<!-- REPLACE: CLOSE_BLOCK_CONTENT -->", "</div>")
        //    //        .Replace("<!-- REPLACE: CLOSE_BLOCK -->", "</div>")
        //    //        .Replace("<!-- REPLACE: OPEN_BLOCK -->", "<div class=\"block\">")
        //    //        .Replace("<!-- REPLACE: OPEN_BLOCK_CONTENT -->", "<div class=\"block_content\">");
        //}


        private static readonly Dictionary<string, string> MIMETypesDictionary = new Dictionary<string, string>
  {
    {"ai", "application/postscript"},
    {"aif", "audio/x-aiff"},
    {"aifc", "audio/x-aiff"},
    {"aiff", "audio/x-aiff"},
    {"asc", "text/plain"},
    {"atom", "application/atom+xml"},
    {"au", "audio/basic"},
    {"avi", "video/x-msvideo"},
    {"bcpio", "application/x-bcpio"},
    {"bin", "application/octet-stream"},
    {"bmp", "image/bmp"},
    {"cdf", "application/x-netcdf"},
    {"cgm", "image/cgm"},
    {"class", "application/octet-stream"},
    {"cpio", "application/x-cpio"},
    {"cpt", "application/mac-compactpro"},
    {"csh", "application/x-csh"},
    {"css", "text/css"},
    {"dcr", "application/x-director"},
    {"dif", "video/x-dv"},
    {"dir", "application/x-director"},
    {"djv", "image/vnd.djvu"},
    {"djvu", "image/vnd.djvu"},
    {"dll", "application/octet-stream"},
    {"dmg", "application/octet-stream"},
    {"dms", "application/octet-stream"},
    {"doc", "application/msword"},
    {"docx","application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
    {"dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template"},
    {"docm","application/vnd.ms-word.document.macroEnabled.12"},
    {"dotm","application/vnd.ms-word.template.macroEnabled.12"},
    {"dtd", "application/xml-dtd"},
    {"dv", "video/x-dv"},
    {"dvi", "application/x-dvi"},
    {"dxr", "application/x-director"},
    {"eps", "application/postscript"},
    {"etx", "text/x-setext"},
    {"exe", "application/octet-stream"},
    {"ez", "application/andrew-inset"},
    {"gif", "image/gif"},
    {"gram", "application/srgs"},
    {"grxml", "application/srgs+xml"},
    {"gtar", "application/x-gtar"},
    {"hdf", "application/x-hdf"},
    {"hqx", "application/mac-binhex40"},
    {"htm", "text/html"},
    {"html", "text/html"},
    {"ice", "x-conference/x-cooltalk"},
    {"ico", "image/x-icon"},
    {"ics", "text/calendar"},
    {"ief", "image/ief"},
    {"ifb", "text/calendar"},
    {"iges", "model/iges"},
    {"igs", "model/iges"},
    {"jnlp", "application/x-java-jnlp-file"},
    {"jp2", "image/jp2"},
    {"jpe", "image/jpeg"},
    {"jpeg", "image/jpeg"},
    {"jpg", "image/jpeg"},
    {"js", "application/x-javascript"},
    {"kar", "audio/midi"},
    {"latex", "application/x-latex"},
    {"lha", "application/octet-stream"},
    {"lzh", "application/octet-stream"},
    {"m3u", "audio/x-mpegurl"},
    {"m4a", "audio/mp4a-latm"},
    {"m4b", "audio/mp4a-latm"},
    {"m4p", "audio/mp4a-latm"},
    {"m4u", "video/vnd.mpegurl"},
    {"m4v", "video/x-m4v"},
    {"mac", "image/x-macpaint"},
    {"man", "application/x-troff-man"},
    {"mathml", "application/mathml+xml"},
    {"me", "application/x-troff-me"},
    {"mesh", "model/mesh"},
    {"mid", "audio/midi"},
    {"midi", "audio/midi"},
    {"mif", "application/vnd.mif"},
    {"mov", "video/quicktime"},
    {"movie", "video/x-sgi-movie"},
    {"mp2", "audio/mpeg"},
    {"mp3", "audio/mpeg"},
    {"mp4", "video/mp4"},
    {"mpe", "video/mpeg"},
    {"mpeg", "video/mpeg"},
    {"mpg", "video/mpeg"},
    {"mpga", "audio/mpeg"},
    {"ms", "application/x-troff-ms"},
    {"msh", "model/mesh"},
    {"mxu", "video/vnd.mpegurl"},
    {"nc", "application/x-netcdf"},
    {"oda", "application/oda"},
    {"ogg", "application/ogg"},
    {"pbm", "image/x-portable-bitmap"},
    {"pct", "image/pict"},
    {"pdb", "chemical/x-pdb"},
    {"pdf", "application/pdf"},
    {"pgm", "image/x-portable-graymap"},
    {"pgn", "application/x-chess-pgn"},
    {"pic", "image/pict"},
    {"pict", "image/pict"},
    {"png", "image/png"},
    {"pnm", "image/x-portable-anymap"},
    {"pnt", "image/x-macpaint"},
    {"pntg", "image/x-macpaint"},
    {"ppm", "image/x-portable-pixmap"},
    {"ppt", "application/vnd.ms-powerpoint"},
    {"pptx","application/vnd.openxmlformats-officedocument.presentationml.presentation"},
    {"potx","application/vnd.openxmlformats-officedocument.presentationml.template"},
    {"ppsx","application/vnd.openxmlformats-officedocument.presentationml.slideshow"},
    {"ppam","application/vnd.ms-powerpoint.addin.macroEnabled.12"},
    {"pptm","application/vnd.ms-powerpoint.presentation.macroEnabled.12"},
    {"potm","application/vnd.ms-powerpoint.template.macroEnabled.12"},
    {"ppsm","application/vnd.ms-powerpoint.slideshow.macroEnabled.12"},
    {"ps", "application/postscript"},
    {"qt", "video/quicktime"},
    {"qti", "image/x-quicktime"},
    {"qtif", "image/x-quicktime"},
    {"ra", "audio/x-pn-realaudio"},
    {"ram", "audio/x-pn-realaudio"},
    {"ras", "image/x-cmu-raster"},
    {"rdf", "application/rdf+xml"},
    {"rgb", "image/x-rgb"},
    {"rm", "application/vnd.rn-realmedia"},
    {"roff", "application/x-troff"},
    {"rtf", "text/rtf"},
    {"rtx", "text/richtext"},
    {"sgm", "text/sgml"},
    {"sgml", "text/sgml"},
    {"sh", "application/x-sh"},
    {"shar", "application/x-shar"},
    {"silo", "model/mesh"},
    {"sit", "application/x-stuffit"},
    {"skd", "application/x-koan"},
    {"skm", "application/x-koan"},
    {"skp", "application/x-koan"},
    {"skt", "application/x-koan"},
    {"smi", "application/smil"},
    {"smil", "application/smil"},
    {"snd", "audio/basic"},
    {"so", "application/octet-stream"},
    {"spl", "application/x-futuresplash"},
    {"src", "application/x-wais-source"},
    {"sv4cpio", "application/x-sv4cpio"},
    {"sv4crc", "application/x-sv4crc"},
    {"svg", "image/svg+xml"},
    {"swf", "application/x-shockwave-flash"},
    {"t", "application/x-troff"},
    {"tar", "application/x-tar"},
    {"tcl", "application/x-tcl"},
    {"tex", "application/x-tex"},
    {"texi", "application/x-texinfo"},
    {"texinfo", "application/x-texinfo"},
    {"tif", "image/tiff"},
    {"tiff", "image/tiff"},
    {"tr", "application/x-troff"},
    {"tsv", "text/tab-separated-values"},
    {"txt", "text/plain"},
    {"ustar", "application/x-ustar"},
    {"vcd", "application/x-cdlink"},
    {"vrml", "model/vrml"},
    {"vxml", "application/voicexml+xml"},
    {"wav", "audio/x-wav"},
    {"wbmp", "image/vnd.wap.wbmp"},
    {"wbmxl", "application/vnd.wap.wbxml"},
    {"wml", "text/vnd.wap.wml"},
    {"wmlc", "application/vnd.wap.wmlc"},
    {"wmls", "text/vnd.wap.wmlscript"},
    {"wmlsc", "application/vnd.wap.wmlscriptc"},
    {"wrl", "model/vrml"},
    {"xbm", "image/x-xbitmap"},
    {"xht", "application/xhtml+xml"},
    {"xhtml", "application/xhtml+xml"},
    {"xls", "application/vnd.ms-excel"},
    {"xml", "application/xml"},
    {"xpm", "image/x-xpixmap"},
    {"xsl", "application/xml"},
    {"xlsx","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
    {"xltx","application/vnd.openxmlformats-officedocument.spreadsheetml.template"},
    {"xlsm","application/vnd.ms-excel.sheet.macroEnabled.12"},
    {"xltm","application/vnd.ms-excel.template.macroEnabled.12"},
    {"xlam","application/vnd.ms-excel.addin.macroEnabled.12"},
    {"xlsb","application/vnd.ms-excel.sheet.binary.macroEnabled.12"},
    {"xslt", "application/xslt+xml"},
    {"xul", "application/vnd.mozilla.xul+xml"},
    {"xwd", "image/x-xwindowdump"},
    {"xyz", "chemical/x-xyz"},
    {"zip", "application/zip"}
  };

        public static string GetMimeType(this string fileName)
        {
            //get file extension
            string extension = Path.GetExtension(fileName).ToLowerInvariant();

            if (extension.Length > 0 &&
                MIMETypesDictionary.ContainsKey(extension.Remove(0, 1)))
            {
                return MIMETypesDictionary[extension.Remove(0, 1)];
            }
            return "unknown/unknown";
        }

        public static string ExistsOrCreateDirectory(this string path, HttpContext httpContext)
        {
            if (path.StartsWith("~")) path = path.ServerMapPath(httpContext);
            if (!Directory.Exists(path)) Directory.CreateDirectory(path);
            return path;
        }

        public static string AddPrifixIsNotEmpty(this string input, string prefix)
        {
            if (!input.IsNullOrEmpty()) return "{0}{1}".ApplyFormat(prefix, input);
            return string.Empty;
        }

        public static string AddPrifixIsNotEmpty(this string input, string prefix, int count)
        {
            if (input.IsNullOrEmpty()) return string.Empty;

            StringBuilder builder = new StringBuilder();
            for (int i = 0; i < count; i++) builder.Append(prefix);
            return "{0}{1}".ApplyFormat(builder.ToString(), input);
        }
        public static string AddSufixIsNotEmpty(this string input, string sufix)
        {
            if (!input.IsNullOrEmpty()) return "{1}{0}".ApplyFormat(sufix, input);
            return string.Empty;
        }

        public static byte TryParseToByte(this string input, byte defaultValue)
        {
            byte result;
            if (byte.TryParse(input, out result)) return result;
            return defaultValue;
        }

        public static Decimal? TryParseToDecimalNulable(this string input)
        {
            Decimal decimalValue = 0;
            if (!Decimal.TryParse(input, out decimalValue)) return null;
            return decimalValue;
        }

        public static Double? TryParseToDoubleNulable(this string input)
        {
            Double decimalValue = 0;
            if (!Double.TryParse(input, out decimalValue)) return null;
            return decimalValue;
        }

        public static decimal GetValueDecimal(this string input)
        {
            decimal value;
            if (decimal.TryParse(input, NumberStyles.Any, ci.NumberFormat, out value))
                return value;
            return decimal.MinValue;
        }

        public static Guid TryParseToGuid(this string value, Guid defaultValue)
        {
            Guid td;
            if (Guid.TryParse(value, out td)) return td;
            return defaultValue;
        }

        public static Guid? TryParseToGuidNulable(this string value, Guid? defaultValue)
        {
            Guid td;
            if (Guid.TryParse(value, out td)) return td;
            return defaultValue;
        }

        public static DateTime TryParseToDateXMLNullable(this string value, DateTime defaultValue)
        {
            if (value.IsNullOrEmpty()) return defaultValue;
            var collections = value.ParseToSplitCollection('T');
            if (collections.Count() == 1)
            {
                collections = collections.AddToNewList("00:00:00");
            }
            var dtvalues = collections.FirstOrDefault().ParseToInt32Collection("-").ToArray();
            var timevalues = collections.LastOrDefault().ParseToInt32Collection(":").ToArray();
            DateTime dt = DateTime.MaxValue;
            if (dtvalues.Count() == 3)
            {
                try
                {
                    defaultValue = new DateTime(dtvalues[0], dtvalues[1], dtvalues[2], timevalues[0], timevalues[1], timevalues[2]);
                }
                catch (Exception) { }
            }
            return defaultValue;
        }

        public static DateTime? TryParseToDateXMLNullable(this string value)
        {
            var result = value.TryParseToDateXMLNullable(DateTime.MinValue);
            if (result == DateTime.MinValue) return null;
            return result;
        }

        public static string GetShortFIO(this string value)
        {
            string result = "";
            if (value == null || value.Trim() == "")
                return result;
            string[] arrayString = value.Split(' ');
            for (int i = 0; i < arrayString.Length; i++)
            {
                if (i == 0)
                {
                    result = arrayString[i];
                }
                else
                {
                    result += " " + arrayString[i].ToUpper().FirstOrDefault() + ".";
                }
            }
            return result;
        }

        public static string GetDigit(this string value)
        {
            return string.Join("", value.Where(c => char.IsDigit(c))); ;
        }

        public static string GZipCompress(this string s, CompressionMode mode, Encoding encoding)
        {
            if (mode == CompressionMode.Compress)
            {
                using (var outputStream = new MemoryStream())
                {
                    using (var compressionStream = new GZipStream(outputStream, CompressionMode.Compress))
                    using (var inputStream = new MemoryStream(encoding.GetBytes(s)))
                        inputStream.CopyTo(compressionStream);

                    return System.Convert.ToBase64String(outputStream.ToArray());
                }
            }
            else
            {
                using (var outputStream = new MemoryStream())
                {
                    using (var inputStream = new MemoryStream(System.Convert.FromBase64String(s)))
                    using (var compressionStream = new GZipStream(inputStream, mode))
                        compressionStream.CopyTo(outputStream);

                    return encoding.GetString(outputStream.ToArray());
                }
            }
        }

        public static string ReadFileTextToEnd(this string fileName)
        {
            var reader = new StreamReader(fileName);
            return reader.ReadToEnd();
        }



        public static string GetMD5Hash(this string value)
        {
            if (string.IsNullOrWhiteSpace(value)) return string.Empty;
            using (MD5 md5 = MD5.Create())
            {
                return md5.ComputeHash(Encoding.UTF8.GetBytes(value)).GetHashString();
            }
        }

        public static string ToKeyHashMD5(this string value, int size = 256)
        {
            string hash = value.GetMD5Hash();
            string trans = value.TranslitToEnglish();
            string result = trans;
            if (trans.Length > size)
            {
                result = trans.Substring(0, (size - hash.Length - 1)) + "_" + hash;
            }

            return result;
        }


        public static string ToKeyHash(this string value, int size = 256)
        {
            string hash = value.GetHashString();
            string trans = value.TranslitToEnglish();
            string result = trans;
            if (trans.Length > size)
            {
                result = trans.Substring(0, (size - hash.Length - 1)) + "_" + hash;
            }

            return result;
        }


        public static byte[] GetHash(this string inputString)
        {
            using (HashAlgorithm algorithm = SHA256.Create())
                return algorithm.ComputeHash(Encoding.UTF8.GetBytes(inputString));
        }

        public static string GetHashString(this string inputString) => GetHash(inputString).GetHashString();

        public static string GetHashString(this byte[] bytes)
        {
            StringBuilder sb = new StringBuilder();
            foreach (byte b in bytes)
                sb.Append(b.ToString("X2"));

            return sb.ToString();
        }

        public static string RemoveFilenameForbidden(
            this string inputString,
            bool toLower = false,
            bool deleteDoubleSpace = true,
            bool deleteLastSpace = true)
        {
            #region Allowed chars

            string allowedSymbols = string.Empty;
            allowedSymbols += "abcdefghijklmnopqrstuvwxyz";
            allowedSymbols += "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

            allowedSymbols += "абвгдеёжзийклмнопрстуфхцчшщъыьэюя";
            allowedSymbols += "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ";

            allowedSymbols += "0123456789";

            allowedSymbols += "~.#№()-_ ";

            #endregion

            var result = new StringBuilder();

            foreach (char c in inputString)
            {
                if (allowedSymbols.Contains(c))
                    result.Append(c);
                else
                    result.Append(' ');
            }
            string resultKey = result.ToString();

            if (toLower) resultKey = resultKey.ToLower();
            if (deleteDoubleSpace)
            {
                while (resultKey.Contains("  ")) resultKey = resultKey.Replace("  ", " ");
            }
            if (deleteLastSpace)
            {
                while (resultKey.Last() == ' ') resultKey = resultKey.Substring(0, resultKey.Length - 1);
            }
            return resultKey;
        }
        public static string RemovePathForbidden(
            this string inputString,
            bool toLower = false,
            bool deleteDoubleSpace = true,
            bool deleteLastSpace = true)
        {
            #region Allowed chars

            string allowedSymbols = string.Empty;
            allowedSymbols += "abcdefghijklmnopqrstuvwxyz";
            allowedSymbols += "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

            allowedSymbols += "абвгдеёжзийклмнопрстуфхцчшщъыьэюя";
            allowedSymbols += "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ";

            allowedSymbols += "0123456789";

            allowedSymbols += "/~.#№()-_ ";

            #endregion

            var result = new StringBuilder();

            foreach (char c in inputString)
            {
                if (allowedSymbols.Contains(c))
                    result.Append(c);
                else
                    result.Append(' ');
            }
            string resultKey = result.ToString();

            if (toLower) resultKey = resultKey.ToLower();
            if (deleteDoubleSpace)
            {
                while (resultKey.Contains("  ")) resultKey = resultKey.Replace("  ", " ");
            }
            if (deleteLastSpace)
            {
                while (resultKey.Last() == ' ') resultKey = resultKey.Substring(0, resultKey.Length - 1);
            }
            return resultKey;
        }

        public static string CreateUserToken(this Guid guid)
        {
            return guid.ToString().Replace("-", string.Empty);
        }

        public static string AppendToken(this string str, int tokenLength = 16)
        {
            return str + str.GenerateToken(tokenLength);
        }

        private static readonly Random _random = new Random();

        private const string _alphabetLower = "abcdefghijklmnopqrstuvwxyz";
        private const string _alphabetUpper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

        private const string _digitDec = "0123456789";
        private const string _digitHex = "abcdef";

        public static string GenerateToken(this string str, int tokenLength, GenerateTokenAlphabetMode alphabetMode = GenerateTokenAlphabetMode.AnyCase, GenerateTokenDigitMode digitMode = GenerateTokenDigitMode.Dec)
        {
            string allowedChars = string.Empty;

            switch (alphabetMode)
            {
                case GenerateTokenAlphabetMode.None:
                    break;

                case GenerateTokenAlphabetMode.AnyCase:
                    allowedChars += _alphabetLower + _alphabetUpper;
                    break;

                case GenerateTokenAlphabetMode.LowerCase:
                    allowedChars += _alphabetLower;
                    break;

                case GenerateTokenAlphabetMode.UpperCase:
                    allowedChars += _alphabetUpper;
                    break;
            }

            switch (digitMode)
            {
                case GenerateTokenDigitMode.None:
                    break;

                case GenerateTokenDigitMode.Hex:
                    allowedChars += _digitDec + _digitHex;
                    break;

                case GenerateTokenDigitMode.Dec:
                    allowedChars += _digitDec;
                    break;
            }

            if (allowedChars.Length < 1) return string.Empty;

            StringBuilder sb = new StringBuilder(tokenLength);

            for (int i = 0; i < tokenLength; i++)
            {
                int charIndex = _random.Next() % allowedChars.Length;
                sb.Append(allowedChars[charIndex]);
            }

            return sb.ToString();
        }

        public static string ToLinuxPath(this string path)
        {
            return path.Replace("\\", "/");
        }

        public static string TrimPath(this string path)
        {
            return path.TrimStart('/', '\\');
        }

        public static string ReadEmbeddedResource(this string resourceName)
        {
            // Получаем путь к сборке
            var assembly = System.Reflection.Assembly.GetExecutingAssembly();

            // Читаем ресурс
            using (Stream? stream = assembly.GetManifestResourceStream(resourceName))
            {
                if (stream == null) throw new FileNotFoundException($"Resource '{resourceName}' not found");
                using (StreamReader reader = new StreamReader(stream))
                {
                    return reader.ReadToEnd();
                }
            }
        }

        public static string CreateSQLiteConnectionString(this string filePath, string mode = "ReadWrite")
        {
            return $"Data Source={filePath};Mode={mode};";
        }
    }
}