using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace Dacryptero.Search.Parser
{
    public static class StringTools
    {
        /// <summary>
        /// This matches a list of escaped and unescaped keywords without a delimiter. The unescaped
        /// version looks like:
        /// <br/>
        /// <c>(?:\"(?:\\.|[^\"\\])*\"|\^=|[^\s\"&amp;\|\^()])</c>
        /// </summary>
        public const string EscapingPattern = "(?:\\\"(?:\\\\.|[^\\\"\\\\])*\\\"|\\^=|[^\\s\\\"&\\|\\^()])";

    
        public const string OpNamePattern = "(?:\\\"(?:\\\\.|[^\\\"\\\\])*\\\"|[\\w-])";

        public delegate (string key,(string op, string value)? v) TwoOpHandler(string input);

        public static TwoOpHandler SplitTwoOp(string opPattern)
        {
            var pattern = $"^({OpNamePattern}+)(?:({opPattern})({EscapingPattern}+))?$";
            var regex = new Regex(pattern, RegexOptions.Compiled);
            return input =>
            {
                var match = regex.Match(input);
                if (!match.Success)
                    return (input, null);
                if (match.Groups[2].Success)
                    return (match.Groups[1].Value, (match.Groups[2].Value, match.Groups[3].Value));
                else return (match.Groups[1].Value, null);
            };
        }

        public delegate ReadOnlyMemory<string> ListOpHandler(string input);

        public static ListOpHandler SplitList(string delimiter, bool once)
        {
            var pattern = $"^({OpNamePattern}+)(?:{delimiter}({OpNamePattern}+)){(once ? "?" : "*")}$";
            var regex = new Regex(pattern, RegexOptions.Compiled);
            return input =>
            {
                var match = regex.Match(input);
                if (!match.Success)
                    return new[]{ input };
                var list = new List<string>(match.Groups[2].Captures.Count + 1);
                list.Add(match.Groups[1].Value);
                list.AddRange(match.Groups[2].Captures.Select(x => x.Value));
                return list.ToArray();
            };
        }
 
        public static string UnescapeText(string input)
        {
            var sb = new StringBuilder(input.Length);
            var escaping = false;
            for (int i = 0; i < input.Length; ++i)
            {
                switch (input[i])
                {
                    case '"': escaping = !escaping; break;
                    case '\\':
                        if (!escaping)
                        {
                            sb.Append('\\');
                            break;
                        }
                        if (i + 1 >= input.Length)
                            break;
                        sb.Append(input[++i] switch
                        {
                            'n' => '\n',
                            'r' => '\r',
                            't' => '\t',
                            _ => input[i],
                        });
                        break;
                    default: sb.Append(input[i]); break;
                }
            }
            return sb.ToString();
        }
    }
}