using RtfPipe;
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;


namespace KarleyLibrary.Erweiterungen
{
    /// <summary>
    /// Bietet Erweiterungen für die Arbeit mit Strings
    /// </summary>
    public static class StringErweiterung
    {

        public static bool IsValidISO(string? input)
        {
            if (input is null)
            {
                return true;
            }

            byte[] bytes = Encoding.GetEncoding("ISO-8859-1").GetBytes(input);
            String result = Encoding.GetEncoding("ISO-8859-1").GetString(bytes);
            return String.Equals(input, result);

        }
        /// <summary>
        /// Wandelt Zeilenumbrüche in einem String in das Equivalent von HTML um.
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static string Nl2Br(string input)
        {
            input = input.Replace(@"\r\n", "<br />");

            return input;
        }

        /// <summary>
        /// Entfernt RTF Tags von einem String
        /// </summary>
        /// <param name="input">Der RTF String der bereinigt werden soll</param>
        /// <returns>Gibt einen <see cref="String"/> Wert zurück</returns>
        public static string RtfToString(this string? input)
        {
            if (input is null)
            {
                return String.Empty;
            }

            return Regex.Replace(input, @"\{\*?\\[^{}]+}|[{}]|\\\n?[A-Za-z]+\n?(?:-?\d+)?[ ]?", "")
                .Replace("\0", "")
                .Replace(@"\'fc", "ü")
                .Replace(@"\'e4", "ä")
                .Replace(@"\'f6", "ö")
                .Replace(@"\'df", "ß")
                .Replace(@"\'c4", "ä")
                .Replace(@"\'80", "EUR")
                ;

        }

        public static string HtmlToDruckHtml(this string? input)
        {
            if (input is null)
            {
                return String.Empty;
            }

            return input
                .Replace("<strong>", "<span class=\"font-weight-bold\">")
                .Replace("</strong>", "</span>")
                .Replace("<b>", "<span class=\"font-weight-bold\">")
                .Replace("</b>", "</span>");
        }

        /// <summary>
        /// Versucht einen Rtf-String in HTML umzuwandeln.
        /// <para>
        /// Wenn die Umwandlung fehlschlägt, wird versucht der input mit <see cref="RtfToString(string?)"/> in einen Reintext umzuwandeln. \r\n wird dabei durch ein br Tag getauscht
        /// </para>
        /// </summary>
        /// <param name="input">Der RTF-String der in HTMLL umgewandelt werden soll.</param>
        /// <param name="fürDruckformular">Gibt an, ob die Umwandlung für ein Druckformular geschehen soll. Wenn true, dann werden alle strong und b Tags durch spans mit der Klasse font-weight-bold ersetzt.</param>
        /// <returns></returns>
        public static string RtfToHtml(this string? input, bool fürDruckformular)
        {
            if (input is null)
            {
                return String.Empty;
            }

            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

            string html = String.Empty;

            // Wir können hier je nach RTF String rausfliegen.
            try
            {
                html = Rtf.ToHtml(input);
                // Wir entfernen sämtliche Style attribute aus dem HTML, damit das Formular einheitlich bleibt in Schriftgröße und Schriftart.
                html = Regex.Replace(html, "(\\s)style=\"([^\"]*)\"", "");
                html = html.Replace("<p><br></p>", "");
                html = html.Replace("<div><br>", "<div>");
            }
            catch (Exception)
            {
                // Wenn wir rausfliegen, dann nehmen wir einfach den RtfText als Plaintext
                html = RtfToString(input)
                    .Replace("\r\n", "<br />");
            }

            if (fürDruckformular)
            {
                html = html
                    .Replace("<strong>", "<span class=\"font-weight-bold\">")
                    .Replace("<b>", "<span class=\"font-weight-bold\">")
                    .Replace("</b>", "</span>")
                    .Replace("</strong>", "</span>")
                    ;
            }


            return html;

        }

        /// <summary>
        /// Bereinigt einen Blob Text
        /// </summary>
        /// <param name="input">Der zu bereinigende input Text</param>
        /// <returns>Gibt ein <see cref="String"/> Wert zurück</returns>
        public static string RemoveBlob(this string input)
        {
            // Als erstes werden alle Bilder gelöscht
            input = Regex.Replace(input, @"(?={.pict)((.|\n)*)}", string.Empty);

            //Zeilenumbruch			
            input = input.Replace(@"\pard", "");
            input = input.Replace(@"\par", "<br>");
            input = input.Replace(@"\line", "<br>");

            //Formatierung
            input = input.Replace(@"\ulnone", "</u>");
            input = input.Replace(@"\ul", "<u>");
            input = input.Replace(@"\b0", "</b>");
            input = input.Replace(@"\b", "<b>");


            input = input.Replace(@"\rtf1\ansi\ansicpg1252\deff0\deflang1031", "");

            input = input.Replace("Verdana;", "");
            input = input.Replace("Arial;", "");
            input = input.Replace("Times New Roman;", "");
            input = input.Replace("MS Sans Serif;", "");

            input = input.Replace(@"\fonttbl", "");
            input = input.Replace(@"\fnil", "");
            input = input.Replace(@"\viewkind4", "");
            input = input.Replace(@"\uc1\pard", "");
            input = input.Replace(@"\fcharset0", "");
            input = input.Replace(@"\fswiss", "");
            input = input.Replace(@"\uc1", "");
            input = input.Replace(@"\froman", "");
            input = input.Replace(@"\lang1023", "");


            input = input.Replace(@"\f0", "");
            input = input.Replace(@"\f1", "");
            input = input.Replace(@"\f2", "");
            input = input.Replace(@"\fs26", "");
            input = input.Replace(@"\fs24", "");
            input = input.Replace(@"\fs18", "");
            input = input.Replace(@"\fs16", "");
            input = input.Replace(@"\fs20", "");
            input = input.Replace(@"\fs14", "");

            input = input.Replace("{", "");
            input = input.Replace("}", "");

            //umlaute/sonderzeichen
            input = input.Replace(@"\'e4", "ä");
            input = input.Replace(@"\'c4", "Ä");
            input = input.Replace(@"\'f6", "ö");
            input = input.Replace(@"\'d6", "Ö");
            input = input.Replace(@"\'fc", "ü");
            input = input.Replace(@"\'dc", "Ü");
            input = input.Replace(@"\'df", "ß");
            input = input.Replace(@"\'80", "€");
            input = input.Replace(@"\'a7", "§");

            input = input.Replace(@"\lang1031", "");

            input = input.Replace(@"\rdblquote", "”");
            input = input.Replace(@"\ldblquote", "“");
            input = input.Replace(@"\'ae", "®");
            input = input.Replace(@"\'b0", "&deg;");


            return input;
        }

        public static string OnlyDigits(string value)
        {
            Regex regex = new Regex(@"\D");
            return regex.Replace(value, string.Empty);
        }

        /// <summary>
        /// Bereinigt einen String und entfernt 0 vom Anfang, * vom Anfang und Ende und _ komplett aus dem String
        /// </summary>
        /// <param name="artikelnummer">Der zu bereinigende String</param>
        /// <returns>Gibt einen <see cref="String"/> Wert zurück</returns>
        public static string TrimArtikelNummer(string artikelnummer)
        {
            if (artikelnummer == null)
            {
                return "";
            }

            while (artikelnummer.StartsWith("0"))
            {
                artikelnummer = artikelnummer.Remove(0, 1);
            }

            while (artikelnummer.StartsWith("*"))
            {
                artikelnummer = artikelnummer.Remove(0, 1);
            }

            artikelnummer = artikelnummer.Trim('*');

            artikelnummer = artikelnummer.Replace("_", "");

            return artikelnummer;
        }

        /// <summary>
        /// Führt einen Regex durch und überprüft ob ein String eine Email ist
        /// </summary>
        /// <param name="email">Der zu überprüfende String</param>
        /// <returns>Gibt einen <see cref="bool"/> Wert zurück der angibt ob der String eine Email ist</returns>
        public static bool IstEmail(string email) => Regex.IsMatch(email, @"^([0-9a-zA-Z]([-\.\w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$");

        /// <summary>
        /// Konvertiert einen String zwischen zwei Encodings
        /// </summary>
        /// <param name="input">Der zu konvertierende String</param>
        /// <param name="from">Das aktuelle Encoding des zu konvertierenden Strings</param>
        /// <param name="to">Das Encoding das der String nach der Konvertierung haben soll</param>
        /// <returns></returns>
        public static string ConvertEncoding(string? input, Encoding from, Encoding to)
        {
            if (input is null)
            {
                return String.Empty;
            }

            byte[] fromBytes = from.GetBytes(input);
            byte[] toBytes = Encoding.Convert(from, to, fromBytes);
            return to.GetString(toBytes);
        }

        public static string Utf8ToIso88591(string? input) => ConvertEncoding(input, Encoding.UTF8, Encoding.GetEncoding("ISO-8859-1"));
        public static string MaxLength(string? target, int length)
        {
            if (target is null)
            {
                return String.Empty;
            }

            return target.Length > length ? target.Substring(0, length - 1) : target;
        }

        public static string RandomString(int length, string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
        {
            Random random = new Random();
            return new string(Enumerable.Repeat(charset, length).Select(s => s[random.Next(s.Length)]).ToArray());
        }

        public static string ParseEan(string ean)
        {
            if (ean == null)
            {
                return "";
            }

            if (string.IsNullOrWhiteSpace(ean))
            {
                return string.Empty;
            }

            if (ean.Length > 13)
            {
                return string.Empty;
            }

            while (ean.Length < 13)
            {
                ean = "0" + ean;
            }


            return ean;
        }

        /// <summary>
        /// Entfernt Zeichen Leerzeichen etc, das sie nur einmal vorkommen, nur 1 Character aggebbar
        /// </summary>
        /// <param name="s">String in dem Ersetzt werden soll</param>
        /// <param name="remove">Zeichen das ersetzt werden soll</param>
        /// <returns></returns>
        public static string RemoveMultipleCharOccurence(string s, char remove)
        {
            StringBuilder sb = new StringBuilder(s.Length);

            bool removeNextIfMatch = false;
            foreach (char c in s)
            {
                if (c == remove)
                {
                    if (removeNextIfMatch)
                    {
                        continue;
                    }
                    else
                    {
                        removeNextIfMatch = true;
                    }
                }
                else
                {
                    removeNextIfMatch = false;
                }

                sb.Append(c);
            }

            return sb.ToString();
        }
        /// <summary>
        ///  Entfernt Zeichen Leerzeichen etc, das sie nur einmal vorkommen, mehrere Characters nutzbar
        /// </summary>
        /// <param name="s">String in dem ersetzt werden soll</param>
        /// <param name="remove">Array der Character die man sucht</param>
        /// <returns></returns>
        public static string RemoveMultipleCharOccurence(string s, char[] remove)
        {
            string s1 = s;
            foreach (char c in remove)
            {
                s1 = RemoveMultipleCharOccurence(s1, c);
            }

            return s1;
        }

        /// <summary>
        /// Wandelt HTML-Inhalt im String zu der UTF-8 Version um.
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static string FormatString(this string? input)
        {
            if (input is null)
            {
                return String.Empty;
            }

            input = input.Replace("&amp;", "");
            input = input.Replace("&auml;", "ä");
            input = input.Replace("&Auml;", "Ä");
            input = input.Replace("&uuml;", "ü");
            input = input.Replace("&Uuml;", "Ü");
            input = input.Replace("&ouml;", "ö");
            input = input.Replace("&Ouml;", "Ö");
            input = input.Replace("&szlig;", "ß");
            input = input.Replace("&quot;", "\"");
            input = input.Replace("&quot;", "\"");
            input = input.Replace("&nbsp;", "");

            return input;
        }

        /// <summary>
        /// Öffnet eine URL im default Webbrowser.
        /// <para>
        /// Diese Methode muss in .NET CORE verwendet werden, aufgrund eines Fehlers bei Process.Start();
        /// </para>
        /// </summary>
        /// <param name="url"></param>
        public static void OpenUrl(string url)
        {
            try
            {
                Process.Start(url);
            }
            catch
            {
                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    url = url.Replace("&", "^&");
                    Process.Start(new ProcessStartInfo("cmd", $"/c start {url}") { CreateNoWindow = true });
                }
                else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
                {
                    Process.Start("xdg-open", url);
                }
                else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
                {
                    Process.Start("open", url);
                }
                else
                {
                    throw;
                }
            }
        }

        public static string ParseUrl(string url)
        {
            url = url.Replace("https://", "");
            url = url.Replace("http://", "");
            url = url.Replace("www.", "");
            return $"//{url}";
        }

        public static string FirstToUpper(this string s)
        {
            return char.ToUpper(s[0]) + s.ToLower()[1..];
        }

        public static string RemoveHTML(this string s)
        {
            if (String.IsNullOrWhiteSpace(s))
            {
                return String.Empty;
            }

            return Regex.Replace(s, "<.*?>", String.Empty);
        }

        public static string GetUntilOrEmpty(this string text, string stopAt = "-")
        {
            if (!String.IsNullOrWhiteSpace(text))
            {
                int charLocation = text.IndexOf(stopAt, StringComparison.Ordinal);

                if (charLocation > 0)
                {
                    return text.Substring(0, charLocation);
                }
            }

            return String.Empty;
        }

        public static string SafeSubstring(this string? text, int start, int length)
        {
            if (text is null)
            {
                return String.Empty;
            }

            if (start > text.Length)
            {
                return text;
            }
            else
            {
                int tmpLen = start + length > text.Length - 1 ? (text.Length - 1) - start : length;
                if (tmpLen <= 0)
                {
                    return text;
                }

                return text.Substring(start, tmpLen);
            }
        }

        public static bool IsValidVat(this string? text)
        {
            if (String.IsNullOrWhiteSpace(text))
            {
                return true;
            }

            string pattern = @"(?xi)^(
(AT)?U[0-9]{8} |                              # Austria
(BE)?0[0-9]{9} |                              # Belgium
(BG)?[0-9]{9,10} |                            # Bulgaria
(HR)?[0-9]{11} |                              # Croatia
(CY)?[0-9]{8}[A-Z] |                          # Cyprus
(CZ)?[0-9]{8,10} |                            # Czech Republic
(DE)?[0-9]{9} |                               # Germany
(DK)?[0-9]{8} |                               # Denmark
(EE)?[0-9]{9} |                               # Estonia
(EL)?[0-9]{9} |                               # Greece
ES[A-Z][0-9]{7}(?:[0-9]|[A-Z]) |              # Spain
(FI)?[0-9]{8} |                               # Finland
(FR)?[0-9A-Z]{2}[0-9]{9} |                    # France
(GB)?([0-9]{9}([0-9]{3})?|[A-Z]{2}[0-9]{3}) | # United Kingdom
(HU)?[0-9]{8} |                               # Hungary
(IE)?[0-9]{7}[A-Z]{1,2}   |                   # Ireland
(IE)?[0-9][A-Z][0-9]{5}[A-Z] |                # Ireland (2)
(IT)?[0-9]{11} |                              # Italy
(LT)?([0-9]{9}|[0-9]{12}) |                   # Lithuania
(LU)?[0-9]{8} |                               # Luxembourg
(LV)?[0-9]{11} |                              # Latvia
(MT)?[0-9]{8} |                               # Malta
(NL)?[0-9]{9}B[0-9]{2} |                      # Netherlands
(PL)?[0-9]{10} |                              # Poland
(PT)?[0-9]{9} |                               # Portugal
(RO)?[0-9]{2,10} |                            # Romania
(SE)?[0-9]{12} |                              # Sweden
(SI)?[0-9]{8} |                               # Slovenia
(SK)?[0-9]{10}                                # Slovakia
)$";

            return new Regex(pattern, RegexOptions.Singleline | RegexOptions.IgnoreCase).Match(text).Success;
        }

        public static bool IsValidEan(string code)
        {
            Regex _gtinRegex = new Regex("^(\\d{8}|\\d{12,14})$");
            if (!_gtinRegex.IsMatch(code)) return false;
            code = code.PadLeft(14, '0');
            int sum = code.Select((c, i) => (c - '0') * ((i % 2 == 0) ? 3 : 1)).Sum();
            return (sum % 10) == 0;
        }
        public static bool ValidateBIC(string bic)
        {
            bic = bic.ToUpper();
            Regex regex = new Regex(@"[A-Z]{6,6}[A-Z2-9][A-NP-Z0-9]([A-Z0-9]{3,3}){0,1}");
            return regex.Match(bic).Success;
        }
        public static bool ValidateIBAN(string iban)
        {
            iban = iban.ToUpper(); //IN ORDER TO COPE WITH THE REGEX BELOW
            if (String.IsNullOrEmpty(iban))
            {
                return false;
            }
            else if (System.Text.RegularExpressions.Regex.IsMatch(iban, "^[A-Z0-9]"))
            {
                iban = iban.Replace(" ", String.Empty);
                string bank =
                $"{iban[4..]}{iban.Substring(0, 4)}";
                int asciiShift = 55;
                StringBuilder sb = new StringBuilder();
                foreach (char c in bank)
                {
                    int v;
                    if (Char.IsLetter(c))
                    {
                        v = c - asciiShift;
                    }
                    else
                    {
                        v = int.Parse(c.ToString());
                    }

                    sb.Append(v);
                }
                string checkSumString = sb.ToString();
                int checksum = int.Parse(checkSumString.Substring(0, 1));
                for (int i = 1; i < checkSumString.Length; i++)
                {
                    int v = int.Parse(checkSumString.Substring(i, 1));
                    checksum *= 10;
                    checksum += v;
                    checksum %= 97;
                }
                return checksum == 1;
            }
            else
            {
                return false;
            }
        }

        public static string ToHumanReadableFileSize(decimal bytes)
        {
            string[] sizes = { "B", "KB", "MB", "GB", "TB" };
            int order = 0;
            while (bytes >= 1024 && order < sizes.Length - 1)
            {
                order++;
                bytes = bytes / 1024;
            }
            return String.Format("{0:0.##} {1}", bytes, sizes[order]);
        }

        /// <summary>
        /// Generiert für einen vollständigen Dateipfad einen einzigartigen Dateinamen
        /// </summary>
        /// <param name="pfad">Ein vollständiger Pfad zu einer Datei.</param>
        /// <returns></returns>
        public static string GetUniqueFileName(string pfad)
        {
            bool found = false;
            int counter = 1;

            string? dir = Path.GetDirectoryName(pfad);
            if (dir is null)
            {
                throw new ArgumentNullException("Dir darf nicht null sein", nameof(dir));
            }

            string filename = Path.GetFileNameWithoutExtension(pfad);
            string extension = new FileInfo(pfad).Extension;
            while (!found)
            {
                if (!File.Exists(Path.Combine(dir, filename + extension)))
                {
                    found = true;
                }
                else
                {
                    if (filename.Contains((counter - 1).ToString()))
                    {
                        filename = filename.Remove(filename.Length - counter.ToString().Length);
                    }

                    filename += counter;
                    counter++;
                }
            }

            return Path.Combine(dir, filename + extension);
        }


    }
}
