using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ServiceStack.Text;

/// <summary>
/// Helpful extensions on ReadOnlySpan&lt;char&gt;
/// Previous extensions on StringSegment available from: https://gist.github.com/mythz/9825689f0db7464d1d541cb62954614c
/// </summary>
public static class StringSpanExtensions
{
    /// <summary>
    /// Returns null if Length == 0, string.Empty if value[0] == NonWidthWhitespace, otherwise returns value.ToString()
    /// </summary>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static string Value(this ReadOnlySpan<char> value) => value.IsEmpty 
        ? null 
        : value.Length == 1 && value[0] == TypeConstants.NonWidthWhiteSpace 
            ? ""
            : value.ToString();

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    internal static object Value(this object obj) =>
        obj is string value && value.Length == 1 && value[0] == TypeConstants.NonWidthWhiteSpace
            ? ""
            : obj;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool IsNullOrEmpty(this ReadOnlySpan<char> value) => value.IsEmpty || (value.Length == 1 && value[0] == TypeConstants.NonWidthWhiteSpace);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool IsNullOrWhiteSpace(this ReadOnlySpan<char> value) => value.IsNullOrEmpty() || value.IsWhiteSpace();

    [Obsolete("Use value[index]")]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static char GetChar(this ReadOnlySpan<char> value, int index) => value[index];

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static string Substring(this ReadOnlySpan<char> value, int pos) => value.Slice(pos, value.Length - pos).ToString();

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static string Substring(this ReadOnlySpan<char> value, int pos, int length) => value.Slice(pos, length).ToString();

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool CompareIgnoreCase(this ReadOnlySpan<char> value, ReadOnlySpan<char> text) => value.Equals(text, StringComparison.OrdinalIgnoreCase);

    public static ReadOnlySpan<char> FromCsvField(this ReadOnlySpan<char> text)
    {
        //TODO replace with native Replace() when exists
        if (text.IsNullOrEmpty())
            return text;
            
        var delim = CsvConfig.ItemDelimiterString;
        if (delim.Length == 1)
        {
            if (text[0] != delim[0])
                return text;
        }
        else if (!text.StartsWith(delim.AsSpan(), StringComparison.Ordinal))
        {
            return text;
        }
            
        var ret = text.Slice(CsvConfig.ItemDelimiterString.Length, text.Length - CsvConfig.EscapedItemDelimiterString.Length)
            .ToString().Replace(CsvConfig.EscapedItemDelimiterString, CsvConfig.ItemDelimiterString);
            
        if (ret == string.Empty)
            return TypeConstants.EmptyStringSpan;

        return ret.AsSpan();
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool ParseBoolean(this ReadOnlySpan<char> value)
    {
        //Lots of kids like to use '1', HTML checkboxes use 'on' as a soft convention
        switch (value.Length)
        {
            case 0:
                return false;
            case 1:
                switch (value[0])
                {
                    case '1':
                    case 't':
                    case 'T':
                    case 'y':
                    case 'Y':
                        return true;
                    case '0':
                    case 'f':
                    case 'F':
                    case 'n':
                    case 'N':
                        return false;
                }
                break;
            case 2:
                if (value[0] == 'o' && value[1] == 'n')
                    return true;
                break;
            case 3:
                if (value[0] == 'o' && value[1] == 'f' && value[1] == 'f')
                    return false;
                break;
        }

        return MemoryProvider.Instance.ParseBoolean(value);
    }

    public static bool TryParseBoolean(this ReadOnlySpan<char> value, out bool result) =>
        MemoryProvider.Instance.TryParseBoolean(value, out result);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool TryParseDecimal(this ReadOnlySpan<char> value, out decimal result) =>
        MemoryProvider.Instance.TryParseDecimal(value, out result);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool TryParseFloat(this ReadOnlySpan<char> value, out float result) => 
        MemoryProvider.Instance.TryParseFloat(value, out result);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool TryParseDouble(this ReadOnlySpan<char> value, out double result) => 
        MemoryProvider.Instance.TryParseDouble(value, out result);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static decimal ParseDecimal(this ReadOnlySpan<char> value) => 
        MemoryProvider.Instance.ParseDecimal(value);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static decimal ParseDecimal(this ReadOnlySpan<char> value, bool allowThousands) => 
        MemoryProvider.Instance.ParseDecimal(value, allowThousands);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static float ParseFloat(this ReadOnlySpan<char> value) => 
        MemoryProvider.Instance.ParseFloat(value);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double ParseDouble(this ReadOnlySpan<char> value) => 
        MemoryProvider.Instance.ParseDouble(value);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static sbyte ParseSByte(this ReadOnlySpan<char> value) =>
        SignedInteger<sbyte>.ParseSByte(value);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static byte ParseByte(this ReadOnlySpan<char> value) => 
        UnsignedInteger<byte>.ParseByte(value);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static short ParseInt16(this ReadOnlySpan<char> value) => 
        SignedInteger<short>.ParseInt16(value);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static ushort ParseUInt16(this ReadOnlySpan<char> value) => 
        UnsignedInteger<ushort>.ParseUInt16(value);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static int ParseInt32(this ReadOnlySpan<char> value) =>
        SignedInteger<int>.ParseInt32(value);
        
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static uint ParseUInt32(this ReadOnlySpan<char> value) => 
        UnsignedInteger<uint>.ParseUInt32(value);
        
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static long ParseInt64(this ReadOnlySpan<char> value) => 
        SignedInteger<long>.ParseInt64(value);
        
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static ulong ParseUInt64(this ReadOnlySpan<char> value) => 
        UnsignedInteger<ulong>.ParseUInt64(value);
        
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Guid ParseGuid(this ReadOnlySpan<char> value) =>
        DefaultMemory.Provider.ParseGuid(value);
        
    public static object ParseSignedInteger(this ReadOnlySpan<char> value)
    {
        var longValue = ParseInt64(value);
        if (longValue >= int.MinValue && longValue <= int.MaxValue)
            return (int)longValue;
        return longValue;
    }
        
    public static bool TryReadLine(this ReadOnlySpan<char> text, out ReadOnlySpan<char> line, ref int startIndex)
    {
        if (startIndex >= text.Length)
        {
            line = TypeConstants.NullStringSpan;
            return false;
        }

        text = text.Slice(startIndex);

        var nextLinePos = text.IndexOfAny('\r', '\n');
        if (nextLinePos == -1)
        {
            var nextLine = text.Slice(0, text.Length);
            startIndex += text.Length;
            line = nextLine;
            return true;
        }
        else
        {
            var nextLine = text.Slice(0, nextLinePos);

            startIndex += nextLinePos + 1;

            if (text[nextLinePos] == '\r' && text.Length > nextLinePos + 1 && text[nextLinePos + 1] == '\n')
                startIndex += 1;

            line = nextLine;
            return true;
        }
    }

    public static bool TryReadPart(this ReadOnlySpan<char> text, string needle, out ReadOnlySpan<char> part, ref int startIndex)
    {
        if (startIndex >= text.Length)
        {
            part = TypeConstants.NullStringSpan;
            return false;
        }

        text = text.Slice(startIndex);
        var nextPartPos = text.IndexOf(needle);
        if (nextPartPos == -1)
        {
            var nextPart = text.Slice(0, text.Length);
            startIndex += text.Length;
            part = nextPart;
            return true;
        }
        else
        {
            var nextPart = text.Slice(0, nextPartPos);
            startIndex += nextPartPos + needle.Length;
            part = nextPart;
            return true;
        }
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static ReadOnlySpan<char> Advance(this ReadOnlySpan<char> text, int to) => text.Slice(to, text.Length - to);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static ReadOnlySpan<char> AdvancePastWhitespace(this ReadOnlySpan<char> literal)
    {
        var i = 0;
        while (i < literal.Length && char.IsWhiteSpace(literal[i]))
            i++;

        return i == 0 ? literal : literal.Slice(i < literal.Length ? i : literal.Length);
    }

    public static ReadOnlySpan<char> AdvancePastChar(this ReadOnlySpan<char> literal, char delim)
    {
        var i = 0;
        var c = (char) 0;
        while (i < literal.Length && (c = literal[i]) != delim)
            i++;

        if (c == delim)
            return literal.Slice(i + 1);

        return i == 0 ? literal : literal.Slice(i < literal.Length ? i : literal.Length);
    }

    [Obsolete("Use Slice()")]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static ReadOnlySpan<char> Subsegment(this ReadOnlySpan<char> text, int startPos) => text.Slice(startPos, text.Length - startPos);

    [Obsolete("Use Slice()")]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static ReadOnlySpan<char> Subsegment(this ReadOnlySpan<char> text, int startPos, int length) => text.Slice(startPos, length);

    public static ReadOnlySpan<char> LeftPart(this ReadOnlySpan<char> strVal, char needle)
    {
        if (strVal.IsEmpty) return strVal;
        var pos = strVal.IndexOf(needle);
        return pos == -1
            ? strVal
            : strVal.Slice(0, pos);
    }

    public static ReadOnlySpan<char> LeftPart(this ReadOnlySpan<char> strVal, string needle)
    {
        if (strVal.IsEmpty) return strVal;
        var pos = strVal.IndexOf(needle);
        return pos == -1
            ? strVal
            : strVal.Slice(0, pos);
    }

    public static ReadOnlySpan<char> RightPart(this ReadOnlySpan<char> strVal, char needle)
    {
        if (strVal.IsEmpty) return strVal;
        var pos = strVal.IndexOf(needle);
        return pos == -1
            ? strVal
            : strVal.Slice(pos + 1);
    }

    public static ReadOnlySpan<char> RightPart(this ReadOnlySpan<char> strVal, string needle)
    {
        if (strVal.IsEmpty) return strVal;
        var pos = strVal.IndexOf(needle);
        return pos == -1
            ? strVal
            : strVal.Slice(pos + needle.Length);
    }

    public static ReadOnlySpan<char> LastLeftPart(this ReadOnlySpan<char> strVal, char needle)
    {
        if (strVal.IsEmpty) return strVal;
        var pos = strVal.LastIndexOf(needle);
        return pos == -1
            ? strVal
            : strVal.Slice(0, pos);
    }

    public static ReadOnlySpan<char> LastLeftPart(this ReadOnlySpan<char> strVal, string needle)
    {
        if (strVal.IsEmpty) return strVal;
        var pos = strVal.LastIndexOf(needle);
        return pos == -1
            ? strVal
            : strVal.Slice(0, pos);
    }

    public static ReadOnlySpan<char> LastRightPart(this ReadOnlySpan<char> strVal, char needle)
    {
        if (strVal.IsEmpty) return strVal;
        var pos = strVal.LastIndexOf(needle);
        return pos == -1
            ? strVal
            : strVal.Slice(pos + 1);
    }

    public static ReadOnlySpan<char> LastRightPart(this ReadOnlySpan<char> strVal, string needle)
    {
        if (strVal.IsEmpty) return strVal;
        var pos = strVal.LastIndexOf(needle);
        return pos == -1
            ? strVal
            : strVal.Slice(pos + needle.Length);
    }

    public static void SplitOnFirst(this ReadOnlySpan<char> strVal, char needle, out ReadOnlySpan<char> first, out ReadOnlySpan<char> last)
    {
        first = default;
        last = default;
        if (strVal.IsEmpty) return;
            
        var pos = strVal.IndexOf(needle);
        if (pos == -1)
        {
            first = strVal;
        }
        else
        {
            first = strVal.Slice(0, pos);
            last = strVal.Slice(pos + 1);
        }
    }

    public static void SplitOnFirst(this ReadOnlySpan<char> strVal, string needle, out ReadOnlySpan<char> first, out ReadOnlySpan<char> last)
    {
        first = default;
        last = default;
        if (strVal.IsEmpty) return;
            
        var pos = strVal.IndexOf(needle);
        if (pos == -1)
        {
            first = strVal;
        }
        else
        {
            first = strVal.Slice(0, pos);
            last = strVal.Slice(pos + needle.Length);
        }
    }

    public static void SplitOnLast(this ReadOnlySpan<char> strVal, char needle, out ReadOnlySpan<char> first, out ReadOnlySpan<char> last)
    {
        first = default;
        last = default;
        if (strVal.IsEmpty) return;
            
        var pos = strVal.LastIndexOf(needle);
        if (pos == -1)
        {
            first = strVal;
        }
        else
        {
            first = strVal.Slice(0, pos);
            last = strVal.Slice(pos + 1);
        }
    }

    public static void SplitOnLast(this ReadOnlySpan<char> strVal, string needle, out ReadOnlySpan<char> first, out ReadOnlySpan<char> last)
    {
        first = default;
        last = default;
        if (strVal.IsEmpty) return;
            
        var pos = strVal.LastIndexOf(needle);
        if (pos == -1)
        {
            first = strVal;
        }
        else
        {
            first = strVal.Slice(0, pos);
            last = strVal.Slice(pos + needle.Length);
        }
    }

    public static ReadOnlySpan<char> WithoutExtension(this ReadOnlySpan<char> filePath)
    {
        if (filePath.IsNullOrEmpty())
            return TypeConstants.NullStringSpan;

        var extPos = filePath.LastIndexOf('.');
        if (extPos == -1) return filePath;

        var dirPos = filePath.LastIndexOfAny(PclExport.DirSeps);
        return extPos > dirPos ? filePath.Slice(0, extPos) : filePath;
    }

    public static ReadOnlySpan<char> GetExtension(this ReadOnlySpan<char> filePath)
    {
        if (filePath.IsNullOrEmpty())
            return TypeConstants.NullStringSpan;

        var extPos = filePath.LastIndexOf('.');
        return extPos == -1 ? TypeConstants.NullStringSpan : filePath.Slice(extPos);
    }

    public static ReadOnlySpan<char> ParentDirectory(this ReadOnlySpan<char> filePath)
    {
        if (filePath.IsNullOrEmpty())
            return TypeConstants.NullStringSpan;

        var dirSep = filePath.IndexOf(PclExport.Instance.DirSep) != -1
            ? PclExport.Instance.DirSep
            : filePath.IndexOf(PclExport.Instance.AltDirSep) != -1
                ? PclExport.Instance.AltDirSep
                : (char)0;

        if (dirSep == 0)
            return TypeConstants.NullStringSpan;

        ReadOnlySpan<char> first;
        MemoryExtensions.TrimEnd(filePath, dirSep).SplitOnLast(dirSep, out first, out _); 
        return first;
    }

    public static ReadOnlySpan<char> TrimEnd(this ReadOnlySpan<char> value, params char[] trimChars)
    {
        if (value.IsEmpty) return TypeConstants.NullStringSpan;
        if (trimChars == null || trimChars.Length == 0)
            return value.TrimHelper(1);
        return value.TrimHelper(trimChars, 1);
    }

    private static ReadOnlySpan<char> TrimHelper(this ReadOnlySpan<char> value, int trimType)
    {
        if (value.IsEmpty) return TypeConstants.NullStringSpan;
        int end = value.Length - 1;
        int start = 0;
        if (trimType != 1)
        {
            start = 0;
            while (start < value.Length && char.IsWhiteSpace(value[start]))
                ++start;
        }
        if (trimType != 0)
        {
            end = value.Length - 1;
            while (end >= start && char.IsWhiteSpace(value[end]))
                --end;
        }
        return value.CreateTrimmedString(start, end);
    }

    private static ReadOnlySpan<char> TrimHelper(this ReadOnlySpan<char> value, char[] trimChars, int trimType)
    {
        if (value.IsEmpty) return TypeConstants.NullStringSpan;
        int end = value.Length - 1;
        int start = 0;
        if (trimType != 1)
        {
            for (start = 0; start < value.Length; ++start)
            {
                char ch = value[start];
                int index = 0;
                while (index < trimChars.Length && (int)trimChars[index] != (int)ch)
                    ++index;
                if (index == trimChars.Length)
                    break;
            }
        }
        if (trimType != 0)
        {
            for (end = value.Length - 1; end >= start; --end)
            {
                char ch = value[end];
                int index = 0;
                while (index < trimChars.Length && (int)trimChars[index] != (int)ch)
                    ++index;
                if (index == trimChars.Length)
                    break;
            }
        }
        return value.CreateTrimmedString(start, end);
    }

    private static ReadOnlySpan<char> CreateTrimmedString(this ReadOnlySpan<char> value, int start, int end)
    {
        if (value.IsEmpty) return TypeConstants.NullStringSpan;
        int length = end - start + 1;
        if (length == value.Length)
            return value;
        if (length == 0)
            return TypeConstants.NullStringSpan;
        return value.Slice(start, length);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static ReadOnlySpan<char> SafeSlice(this ReadOnlySpan<char> value, int startIndex) => SafeSlice(value, startIndex, value.Length);

    public static ReadOnlySpan<char> SafeSlice(this ReadOnlySpan<char> value, int startIndex, int length)
    {
        if (value.IsEmpty) return TypeConstants.NullStringSpan;
        if (startIndex < 0) startIndex = 0;
        if (value.Length >= startIndex + length)
            return value.Slice(startIndex, length);

        return value.Length > startIndex ? value.Slice(startIndex) : TypeConstants.NullStringSpan;
    }

    public static string SubstringWithEllipsis(this ReadOnlySpan<char> value, int startIndex, int length)
    {
        if (value.IsEmpty) return string.Empty;
        var str = value.SafeSlice(startIndex, length);
        return str.Length == length
            ? str.ToString() + "..."
            : str.ToString();
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static int IndexOf(this ReadOnlySpan<char> value, string other) => value.IndexOf(other.AsSpan());

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static int IndexOf(this ReadOnlySpan<char> value, string needle, int start, StringComparison comparisonType=StringComparison.Ordinal)
    {
        var pos = value.Slice(start).IndexOf(needle.AsSpan(), comparisonType);
        return pos == -1 ? -1 : start + pos;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static int LastIndexOf(this ReadOnlySpan<char> value, string other) => value.LastIndexOf(other.AsSpan());

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static int LastIndexOf(this ReadOnlySpan<char> value, string needle, int start)
    {
        var pos = value.Slice(start).LastIndexOf(needle.AsSpan());
        return pos == -1 ? -1 : start + pos;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool EqualTo(this ReadOnlySpan<char> value, string other) => value.Equals(other.AsSpan(), StringComparison.Ordinal);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool EqualTo(this ReadOnlySpan<char> value, ReadOnlySpan<char> other) => value.Equals(other, StringComparison.Ordinal);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool EqualsOrdinal(this ReadOnlySpan<char> value, string other) => value.Equals(other.AsSpan(), StringComparison.Ordinal);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool StartsWith(this ReadOnlySpan<char> value, string other) => value.StartsWith(other.AsSpan(), StringComparison.OrdinalIgnoreCase);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool StartsWith(this ReadOnlySpan<char> value, string other, StringComparison comparison) => value.StartsWith(other.AsSpan(), comparison);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool EndsWith(this ReadOnlySpan<char> value, string other, StringComparison comparison) => value.EndsWith(other.AsSpan(), comparison);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool EndsWith(this ReadOnlySpan<char> value, string other) => value.EndsWith(other.AsSpan(), StringComparison.OrdinalIgnoreCase);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool EqualsIgnoreCase(this ReadOnlySpan<char> value, ReadOnlySpan<char> other) => value.Equals(other, StringComparison.OrdinalIgnoreCase);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool StartsWithIgnoreCase(this ReadOnlySpan<char> value, ReadOnlySpan<char> other) => value.StartsWith(other, StringComparison.OrdinalIgnoreCase);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool EndsWithIgnoreCase(this ReadOnlySpan<char> value, ReadOnlySpan<char> other) => value.EndsWith(other, StringComparison.OrdinalIgnoreCase);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Task WriteAsync(this Stream stream, ReadOnlySpan<char> value, CancellationToken token = default(CancellationToken)) =>
        MemoryProvider.Instance.WriteAsync(stream, value, token);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static ReadOnlySpan<char> SafeSubstring(this ReadOnlySpan<char> value, int startIndex) => SafeSubstring(value, startIndex, value.Length);

    public static ReadOnlySpan<char> SafeSubstring(this ReadOnlySpan<char> value, int startIndex, int length)
    {
        if (value.IsEmpty) return TypeConstants.NullStringSpan;
        if (startIndex < 0) startIndex = 0;
        if (value.Length >= (startIndex + length))
            return value.Slice(startIndex, length);

        return value.Length > startIndex ? value.Slice(startIndex) : TypeConstants.NullStringSpan;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static StringBuilder Append(this StringBuilder sb, ReadOnlySpan<char> value) =>
        MemoryProvider.Instance.Append(sb, value);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static byte[] ParseBase64(this ReadOnlySpan<char> value) => MemoryProvider.Instance.ParseBase64(value);

    public static ReadOnlyMemory<byte> ToUtf8(this ReadOnlySpan<char> value) =>
        MemoryProvider.Instance.ToUtf8(value);

    public static ReadOnlyMemory<char> FromUtf8(this ReadOnlySpan<byte> value) =>
        MemoryProvider.Instance.FromUtf8(value);

    public static byte[] ToUtf8Bytes(this ReadOnlySpan<char> value) =>
        MemoryProvider.Instance.ToUtf8Bytes(value);

    public static string FromUtf8Bytes(this ReadOnlySpan<byte> value) =>
        MemoryProvider.Instance.FromUtf8Bytes(value);
        
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static List<string> ToStringList(this IEnumerable<ReadOnlyMemory<char>> from)
    {
        var to = new List<string>();
        if (from != null)
        {
            foreach (var item in from)
            {
                to.Add(item.ToString());
            }
        }
        return to;
    }
        
    public static int CountOccurrencesOf(this ReadOnlySpan<char> value, char needle)
    {
        var count = 0;
        var length = value.Length;
        for (var n = length - 1; n >= 0; n--)
        {
            if (value[n] == needle)
                count++;
        }
        return count;
    }
    
    public static int CountOccurrencesOf(this ReadOnlySpan<char> text, string needle, StringComparison comparisonType=StringComparison.Ordinal)
    {                  
        int count = 0, minIndex = text.IndexOf(needle, 0, comparisonType);
        while (minIndex != -1)
        {
            minIndex = text.IndexOf(needle, minIndex + needle.Length, comparisonType);
            count++;
        }
        return count;
    }

    public static ReadOnlySpan<char> WithoutBom(this ReadOnlySpan<char> value)
    {
        return value.Length > 0 && value[0] == 65279 
            ? value.Slice(1) 
            : value;
    }

    public static ReadOnlySpan<byte> WithoutBom(this ReadOnlySpan<byte> value)
    {
        return value.Length > 3 && value[0] == 0xEF && value[1] == 0xBB && value[2] == 0xBF 
            ? value.Slice(3) 
            : value;
    }

}