﻿namespace StringExtensions.ConsoleClient
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Linq;
    using System.Security.Cryptography;
    using System.Text;
    using System.Text.RegularExpressions;

    /// <summary>
    /// Contains extension methods defined for the <see cref="System.String"/> class.
    /// </summary>
    public static class StringExtensions
    {
        /// <summary>
        /// Creates a MD5 hash.
        /// </summary>
        /// <param name="input">String for which hash is computed.</param>
        /// <returns>MD5 hash of the input string.</returns>
        public static string ToMd5Hash(this string input)
        {
            var md5Hash = MD5.Create();

            // Convert the input string to a byte array and compute the hash.
            var data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(input));

            // Create a new StringBuilder to collect the bytes
            // and create a string.
            var builder = new StringBuilder();

            // Loop through each byte of the hashed data 
            // and format each one as a hexadecimal string.
            for (var i = 0; i < data.Length; i++)
            {
                builder.Append(i.ToString("x2"));
            }

            // Return the hexadecimal string.
            return builder.ToString();
        }

        /// <summary>
        /// Checks if the string is equal to one of the truthy values.
        /// </summary>
        /// <param name="input">String for conversion to boolean.</param>
        /// <remarks>
        /// This method is case insensitive.
        /// </remarks>
        /// <returns>True if input can be converted to boolean and false if it cant.</returns>
        public static bool ToBoolean(this string input)
        {
            var stringTrueValues = new[] { "true", "ok", "yes", "1", "да" };
            return stringTrueValues.Contains(input.ToLower());
        }

        /// <summary>
        /// Tries to parse the string as a short integer.
        /// </summary>
        /// <param name="input">String to convert.</param>
        /// <returns>Convert string to short.</returns>
        public static short ToShort(this string input)
        {
            short.TryParse(input, out short shortValue);
            return shortValue;
        }

        /// <summary>
        /// Tries to parse the string as an integer.
        /// <param name="input">String to convert.</param>
        /// </summary>
        /// <returns>Converted string to integer.</returns>
        public static int ToInteger(this string input)
        {
            int.TryParse(input, out int integerValue);
            return integerValue;
        }

        /// <summary>
        /// Tries to parse the string as a long.
        /// <param name="input">String to convert.</param>
        /// </summary>
        /// <returns>Converted string to long.</returns>
        public static long ToLong(this string input)
        {
            long.TryParse(input, out long longValue);
            return longValue;
        }

        /// <summary>
        /// Tries to parse the string as a DateTime.
        /// </summary>
        /// <param name="input">String to convert.</param>
        /// <returns>Converted string to DateTime.</returns>
        public static DateTime ToDateTime(this string input)
        {
            DateTime.TryParse(input, out DateTime dateTimeValue);
            return dateTimeValue;
        }

        /// <summary>
        /// Capitalizes the first letter of the string.
        /// </summary>
        /// <param name="input">String input.</param>
        /// <returns>Current string with first capitalized letter.</returns>
        private static string CapitalizeFirstLetter(this string input)
        {
            if (string.IsNullOrEmpty(input))
            {
                return input;
            }

            return input
                .Substring(0, 1)
                .ToUpper(CultureInfo.CurrentCulture)
                + input.Substring(1, input.Length - 1);
        }

        /// <summary>
        /// Get the substring between the first occurence.
        /// </summary>
        /// <param name="input">String to search.</param>
        /// <param name="startString">Left delimitation string.</param>
        /// <param name="endString">Right delimitation string.</param>
        /// <param name="startFrom">Start position of search.</param>
        /// <returns>String between delimitations.</returns>
        public static string GetStringBetween(this string input, string startString, string endString, int startFrom = 0)
        {
            input = input.Substring(startFrom);
            startFrom = 0;

            if (!input.Contains(startString) || !input.Contains(endString))
            {
                return string.Empty;
            }

            var startPosition = input
                .IndexOf(startString, startFrom, StringComparison.Ordinal)
                + startString.Length;

            if (startPosition == -1)
            {
                return string.Empty;
            }

            var endPosition = input.IndexOf(endString, startPosition, StringComparison.Ordinal);

            if (endPosition == -1)
            {
                return string.Empty;
            }

            return input.Substring(startPosition, endPosition - startPosition);
        }

        /// <summary>
        /// Converts cyrillic letters to latin.
        /// </summary>
        /// <param name="input">String to convert.</param>
        /// <returns>String with latin letters.</returns>
        private static string ConvertCyrillicToLatinLetters(this string input)
        {
            var bulgarianLetters = new[]
            {
                "а", "б", "в", "г", "д", "е", "ж", "з", "и", "й", "к", "л", "м", "н", "о", "п",
                "р", "с", "т", "у", "ф", "х", "ц", "ч", "ш", "щ", "ъ", "ь", "ю", "я"
            };

            var latinRepresentationsOfBulgarianLetters = new[]
            {
                "a", "b", "v", "g", "d", "e", "j", "z", "i", "y", "k",
                "l", "m", "n", "o", "p", "r", "s", "t", "u", "f", "h",
                "c", "ch", "sh", "sht", "u", "i", "yu", "ya"
            };

            for (var i = 0; i < bulgarianLetters.Length; i++)
            {
                input = input.Replace(bulgarianLetters[i], latinRepresentationsOfBulgarianLetters[i]);
                input = input.Replace(bulgarianLetters[i].ToUpper(), latinRepresentationsOfBulgarianLetters[i].CapitalizeFirstLetter());
            }

            return input;
        }

        /// <summary>
        /// Converts latin letters to cyrillic.
        /// </summary>
        /// <param name="input">String to convert.</param>
        /// <returns>String with cyrillical letters.</returns>
        public static string ConvertLatinToCyrillicKeyboard(this string input)
        {
            var latinLetters = new[]
            {
                "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p",
                "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"
            };

            var bulgarianRepresentationOfLatinKeyboard = new[]
            {
                "а", "б", "ц", "д", "е", "ф", "г", "х", "и", "й", "к",
                "л", "м", "н", "о", "п", "я", "р", "с", "т", "у", "ж",
                "в", "ь", "ъ", "з"
            };

            for (var i = 0; i < latinLetters.Length; i++)
            {
                input = input.Replace(latinLetters[i], bulgarianRepresentationOfLatinKeyboard[i]);
                input = input.Replace(latinLetters[i].ToUpper(), bulgarianRepresentationOfLatinKeyboard[i].ToUpper());
            }

            return input;
        }

        /// <summary>
        /// Creates a valid username from string.
        /// </summary>
        /// <param name="input">String to convert.</param>
        /// <returns>Valid username.</returns>
        public static string ToValidUsername(this string input)
        {
            input = input.ConvertCyrillicToLatinLetters();
            return Regex.Replace(input, @"[^a-zA-z0-9_\.]+", string.Empty);
        }

        /// <summary>
        ///  Converts string to valid latin filename.
        /// </summary>
        /// <param name="input">String to convert.</param>
        /// <returns>Valide filename.</returns>
        public static string ToValidLatinFileName(this string input)
        {
            input = input.Replace(" ", "-").ConvertCyrillicToLatinLetters();
            return Regex.Replace(input, @"[^a-zA-z0-9_\.\-]+", string.Empty);
        }

        /// <summary>
        /// Gets substring of first character.
        /// </summary>
        /// <param name="input">String to convert.</param>
        /// <param name="charsCount">Length of returned string.</param>
        /// <returns>Substring starting at index 0 and ending at <see cref="charsCount"/></returns>
        public static string GetFirstCharacters(this string input, int charsCount)
        {
            return input.Substring(0, Math.Min(input.Length, charsCount));
        }

        /// <summary>
        /// Get file extension.
        /// </summary>
        /// <param name="fileName">Filename extension.</param>
        /// <returns>File extension.</returns>
        public static string GetFileExtension(this string fileName)
        {
            if (string.IsNullOrWhiteSpace(fileName))
            {
                return string.Empty;
            }

            var fileParts = fileName.Split(new[] { "." }, StringSplitOptions.None);

            if (fileParts.Count() == 1 || string.IsNullOrEmpty(fileParts.Last()))
            {
                return string.Empty;
            }

            return fileParts
                .Last()
                .Trim()
                .ToLower();
        }

        /// <summary>
        /// Convert file extension to content type.
        /// </summary>
        /// <param name="fileExtension">File extension to convert.</param>
        /// <returns>Content type.</returns>
        public static string ToContentType(this string fileExtension)
        {
            var fileExtensionToContentType = new Dictionary<string, string>
            {
                { "jpg", "image/jpeg" },
                { "jpeg", "image/jpeg" },
                { "png", "image/x-png" },
                {
                    "docx",
                    "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
                },
                { "doc", "application/msword" },
                { "pdf", "application/pdf" },
                { "txt", "text/plain" },
                { "rtf", "application/rtf" }
            };

            if (fileExtensionToContentType.ContainsKey(fileExtension.Trim()))
            {
                return fileExtensionToContentType[fileExtension.Trim()];
            }

            return "application/octet-stream";
        }

        /// <summary>
        /// Converts string to byte array.
        /// </summary>
        /// <param name="input">String to convert.</param>
        /// <returns>Input string as byte array.</returns>
        public static byte[] ToByteArray(this string input)
        {
            var bytesArray = new byte[input.Length * sizeof(char)];
            Buffer.BlockCopy(input.ToCharArray(), 0, bytesArray, 0, bytesArray.Length);

            return bytesArray;
        }
    }
}