using System.Reactive.Linq;
using System.Text;

namespace InnovEnergy.Lib.Utils;

public static class StringParsing
{
    public static IObservable<String> ParseLines(this IObservable<ReadOnlyMemory<Byte>> source, String eol = "\n")
    {
        return ParseLines(source, Encoding.UTF8, eol);
    }

    public static IObservable<String> ParseLines(this IObservable<ReadOnlyMemory<Byte>> source, Encoding encoding, String eol = "\n")
    {
        return source
              .Select(d => encoding.GetString(d.Span))
              .ParseLines(eol);
    }

    public static IObservable<String> ParseLines(this IObservable<String> source, String eol = "\n")
    {
        var accumulator = new StringBuilder();
         
        return source
              .Append(eol)
              .SelectMany(SplitLines);
         
        IEnumerable<String> SplitLines(String s)
        {
            if (!s.Contains(eol))
            {
                accumulator.Append(s);
                yield break;
            }
    
            var split = s.Split(eol); // scatter
    
            yield return accumulator.Append(split[0]).ToString(); // gather    
          
            foreach (var line in split[1..^1])
                yield return line;
            
            accumulator.Clear();
            accumulator.Append(split[^1]);
        }        
    }

    public static IEnumerable<String> ParseLines(this IEnumerable<ReadOnlyMemory<Byte>> source, String eol = "\n")
    {
        return ParseLines(source, Encoding.UTF8, eol);
    }

    public static IEnumerable<String> ParseLines(this IEnumerable<ReadOnlyMemory<Byte>> source, Encoding encoding, String eol = "\n")
    {
        return source
              .Select(d => encoding.GetString(d.Span))
              .ParseLines(eol);
    }

    public static IEnumerable<String> ParseLines(this IEnumerable<String> source, String eol = "\n")
    {
        var accumulator = new StringBuilder();
         
        return source
              .Append(eol)
              .SelectMany(SplitLines);
         
        IEnumerable<String> SplitLines(String s)
        {
            if (!s.Contains(eol))
            {
                accumulator.Append(s);
                yield break;
            }
    
            var split = s.Split(eol); // scatter
    
            yield return accumulator.Append(split[0]).ToString(); // gather    
          
            foreach (var line in split[1..^1])
                yield return line;
            
            accumulator.Clear();
            accumulator.Append(split[^1]);
        }        
    }
}