using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Services.SlugServices
{
    public static class SlugService
    {
        public static string Create(bool toLower, params string[] values)
        {
            return Create(toLower, string.Join("-", values));
        }

        /// <summary>
        ///     Creates a slug.
        ///     References:
        ///     http://www.unicode.org/reports/tr15/tr15-34.html
        ///     https://meta.stackexchange.com/questions/7435/non-us-ascii-characters-dropped-from-full-profile-url/7696#7696
        ///     http://stackoverflow.com/questions/25259/how-do-you-include-a-webpage-title-as-part-of-a-webpage-url/25486#25486
        ///     http://stackoverflow.com/questions/3769457/how-can-i-remove-accents-on-a-string
        /// </summary>
        /// <param name="toLower"></param>
        /// <param name="normalised"></param>
        /// <returns></returns>
        public static string Create(bool toLower, string value)
        {
            if (value == null)
            {
                return "";
            }

            string normalised = value.Normalize(NormalizationForm.FormKD);

            const int maxlen = 300;
            int len = normalised.Length;
            bool prevDash = false;
            StringBuilder sb = new StringBuilder(len);
            char c;

            for (int i = 0; i < len; i++)
            {
                c = normalised[i];
                if (c >= 'a' && c <= 'z' || c >= '0' && c <= '9')
                {
                    if (prevDash)
                    {
                        sb.Append('-');
                        prevDash = false;
                    }
                    sb.Append(c);
                }
                else if (c >= 'A' && c <= 'Z')
                {
                    if (prevDash)
                    {
                        sb.Append('-');
                        prevDash = false;
                    }
                    // Tricky way to convert to lowercase
                    if (toLower)
                    {
                        sb.Append((char)(c | 32));
                    }
                    else
                    {
                        sb.Append(c);
                    }
                }
                else if (c == ' ' || c == ',' || c == '.' || c == '/' || c == '\\' || c == '-' || c == '_' || c == '=')
                {
                    if (!prevDash && sb.Length > 0)
                    {
                        prevDash = true;
                    }
                }
                else
                {
                    string swap = ConvertEdgeCases(c, toLower);

                    if (swap != null)
                    {
                        if (prevDash)
                        {
                            sb.Append('-');
                            prevDash = false;
                        }
                        sb.Append(swap);
                    }
                }

                if (sb.Length == maxlen)
                {
                    break;
                }
            }
            return sb.ToString();
        }

        private static string ConvertEdgeCases(char c, bool toLower)
        {
            string swap = null;
            switch (c)
            {
                case 'ı':
                    swap = "i";
                    break;

                case 'ł':
                    swap = "l";
                    break;

                case 'Ł':
                    swap = toLower ? "l" : "L";
                    break;

                case 'đ':
                    swap = "d";
                    break;

                case 'ß':
                    swap = "ss";
                    break;

                case 'ø':
                    swap = "o";
                    break;

                case 'Þ':
                    swap = "th";
                    break;

                case '#':
                    swap = "sharp";
                    break;
            }
            return swap;
        }
    }
}
