using System;
using System.Collections.Generic;
using System.Text;
using System.Text.Json;
using Dacryptero.Search.AST;

namespace Dacryptero.Search.Sorting
{
    public class SortConfig
    {
        public List<AST.SortOption> Options { get; } = new List<AST.SortOption>();

        public Dictionary<(FoundType, string), int> OptionIndex { get; }
            = new Dictionary<(FoundType, string), int>();

        public SortConfig Negate()
        {
            var result = new SortConfig();
            foreach (var opt in Options)
            {
                result.Options.Add(new AST.SortOption(
                    opt.Target,
                    opt.Name,
                    opt.Direction switch
                    {
                        AST.SortOption.SortDirection.Ascending =>
                            AST.SortOption.SortDirection.Descending,
                        AST.SortOption.SortDirection.Descending =>
                            AST.SortOption.SortDirection.Ascending,
                        _ => opt.Direction,
                    }
                ));
            }
            return result;
        }

        public void Optimize()
        {
            Reduce();
            if (Options.Count == 0)
                Options.Add(new SortOption(SortOption.SortTarget.Score, null, SortOption.SortDirection.Descending));
            Options.RemoveAll(x => x.Target == SortOption.SortTarget.None);

            OptionIndex.Clear();
            for (int i = 0; i < Options.Count; ++i)
            {
                var opt = Options[i];
                if (opt.Name is string name)
                {
                    switch (opt.Target)
                    {
                        case SortOption.SortTarget.Attribute:
                            OptionIndex[(FoundType.Attribute, name)] = i;
                            break;
                        case SortOption.SortTarget.Field:
                            OptionIndex[(FoundType.Field, name)] = i;
                            break;
                    }
                }
            }
        }

        private bool Reduce()
        {
            var changed = false;
            for (int i = 0; i < Options.Count; ++i)
            {
                var sort1 = Options[i];
                for (int j = 0; j < i; ++j)
                {
                    var sort2 = Options[j];
                    if (sort1.Equals(sort2))
                    {
                        Options.RemoveAt(i);
                        i--;
                        changed = true;
                        break;
                    }
                    if (sort1.IsConflicting(sort2))
                    {
                        Options.RemoveAt(i);
                        Options.RemoveAt(j);
                        i -= 2;
                        changed = true;
                        break;
                    }
                }
            }
            return changed;
        }

        public void WriteTo(Utf8JsonWriter writer)
        {
            writer.WriteStartArray();
            foreach (var opt in Options)
            {
                opt.WriteTo(writer);
            }
            writer.WriteEndArray();
        }

        public override string ToString()
        {
            if (Options.Count == 0)
                return "no sortings";
            var sb = new StringBuilder();
            foreach (var opt in Options)
            {
                if (sb.Length != 0)
                    sb.Append(", ");
                sb.Append(opt);
            }
            return sb.ToString();
        }
    }
}