using System.Diagnostics.SymbolStore;
using System.Text;

namespace CompilerDesignIFlr1
{
    internal class LR1Unit
    {
        internal LR1Unit() { }

        internal static string LanguageMode = "English";
        internal static Dictionary<string, string> EnglishToChinese = [];
        internal static void Init(string filePath)
        {
            string fileContent = File.ReadAllText(filePath);
            EnglishToChinese = fileContent
                .Split('#')
                .Select(x => x.Split(':').Select(x => x.Trim()).ToList()).Where(x=>x.Count == 2).ToList().ToDictionary(x => x[0], x => x[1]);
        }

        internal LR1Unit(string name, string grammar)
        {
            Type = "Token";
            Name = name;
            Grammar = [grammar];
        }

        internal LR1Unit(string name, string value, string grammar)
        {
            Type = "Token";
            Name = name;
            Grammar = [grammar];
            Value = value;
        }

        internal LR1Unit(string name, List<string> grammar)
        {
            Type = "Grammar";
            Name = name;
            Grammar = grammar;
        }

        internal LR1Unit(
            int id,
            string type,
            string name,
            HashSet<string> prospect,
            int pointPosition,
            List<string> grammar
        )
        {
            Id = id;
            Type = type;
            Name = name;
            Prospect = prospect;
            PointPosition = pointPosition;
            Grammar = grammar;
        }

        public LR1Unit(
            int id,
            string type,
            string name,
            int quadrupleIndex,
            string value,
            HashSet<string> prospect,
            int pointPosition,
            List<string> grammar
        )
        {
            Id = id;
            Type = type;
            Name = name;
            QuadrupleIndex = quadrupleIndex;
            Value = value;
            Prospect = prospect;
            PointPosition = pointPosition;
            Grammar = grammar;
        }

        internal LR1Unit Clone() =>
            new LR1Unit(
                Id,
                Type,
                Name,
                0,
                Value,
                new HashSet<string>(Prospect),
                PointPosition,
                [.. Grammar]
            );

        internal int Id { get; set; } = -1;
        internal string Type { get; set; } = "";
        internal string Name { get; set; } = "";
        internal int QuadrupleIndex { get; set; } = 0;
        internal string Value { get; set; } = "";
        internal HashSet<string> Prospect { get; set; } = [];
        internal int PointPosition { get; set; } = 0;
        internal List<string> Grammar { get; set; } = [];

        internal string? Next() => PointPosition >= Grammar.Count ? null : Grammar[PointPosition];

        internal bool ReadyToReduce() => PointPosition >= Grammar.Count;

        internal bool CanReduce(List<LR1Unit> stack)
        {
            for (int i = 1; i <= Grammar.Count; i++)
            {
                if (stack[^i].Name != Grammar[^i])
                    return false;
            }
            return true;
        }

        public override string ToString()
        {
            string GetName(string s)
            {
                return LanguageMode switch
                {
                    "Chinese" => EnglishToChinese.TryGetValue(s, out var result) ? result : s,
                    "English" => s,
                    _ => throw new Exception("Language mode is not supported.")
                };
            }
            StringBuilder sb = new();
            sb.Append($"{Id, 2} ");
            sb.Append(GetName(Name)).Append(" ::= ");
            for (int i = 0; i < Grammar.Count; i++)
            {
                if (PointPosition == i)
                    sb.Append(". ");
                sb.Append(GetName(Grammar[i]) + " ");
            }
            if (PointPosition == Grammar.Count)
                sb.Append('.');
            foreach (var item in Prospect)
            {
                sb.Append(", " + GetName(item));
            }
            return sb.ToString();
        }

        public override bool Equals(object? obj)
        {
            if (obj is not LR1Unit other)
                return false;

            return Type == other.Type
                && Name == other.Name
                && Id == other.Id
                && PointPosition == other.PointPosition
                && Grammar.SequenceEqual(other.Grammar)
                && Prospect.SetEquals(other.Prospect);
        }

        public override int GetHashCode()
        {
            HashCode hash = new HashCode();
            hash.Add(Type);
            hash.Add(Name);
            hash.Add(PointPosition);
            foreach (var item in Grammar)
                hash.Add(item);
            foreach (var item in Prospect)
                hash.Add(item);
            return hash.ToHashCode();
        }

        internal bool Nullable() => Grammar.Count == 0;

        internal LR1Unit ToNext()
        {
            var unit = Clone();
            unit.PointPosition++;
            return unit;
        }
    }
}