using Luban.Job.Common.RawDefs;
using Luban.Job.Common.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using Bright.Collections;

namespace Luban.Job.Common.Defs
{

    public class DefEnum : DefTypeBase
    {
        public class Item
        {
            public string Name { get; set; }

            public string Value { get; set; }

            public string Alias { get; set; }

            public string AliasOrName => string.IsNullOrWhiteSpace(Alias) ? Name : Alias;

            public int IntValue { get; set; }

            public string Comment { get; set; }

            public string EscapeComment => DefUtil.EscapeCommentByCurrentLanguage(Comment);

            public Dictionary<string, string> Tags { get; set; }

            public bool HasTag(string attrName)
            {
                return Tags != null && Tags.ContainsKey(attrName);
            }

            public string GetTag(string attrName)
            {
                return Tags != null && Tags.TryGetValue(attrName, out var value) ? value : null;
            }
        }

        public bool IsFlags { get; }

        public bool IsUniqueItemId { get; }

        public List<Item> Items { get; } = new List<Item>();

        private readonly Dictionary<string, int> _nameOrAlias2Value = new();

        private readonly Dictionary<int, string> _vaule2Name = new();

        public bool TryValueByNameOrAlias(string name, out int value)
        {
            return _nameOrAlias2Value.TryGetValue(name, out value);
        }


        public int GetValueByNameOrAlias(string name)
        {
            // TODO flags ?
            if (!name.Contains('|'))
            {
                return GetBasicValueByNameOrAlias(name);
            }
            int combindValue = 0;
            foreach (var s in name.Split('|'))
            {
                combindValue |= GetBasicValueByNameOrAlias(s.Trim());
            }
            return combindValue;
        }

        private int GetBasicValueByNameOrAlias(string name)
        {
            if (_nameOrAlias2Value.TryGetValue(name, out var value))
            {
                return value;
            }
            else if (int.TryParse(name, out value))
            {
                if (!_vaule2Name.ContainsKey(value))
                {
                    throw new Exception($"{value} 不是 enum:'{FullName}'的有效枚举值");
                }
                return value;
            }
            else
            {
                throw new Exception($"'{name}' 不是enum:'{FullName}'的有效枚举值");
            }
        }

        public DefEnum(PEnum e)
        {
            Name = e.Name;
            Namespace = e.Namespace;
            IsFlags = e.IsFlags;
            IsUniqueItemId = e.IsUniqueItemId;
            Comment = e.Comment;
            Tags = DefUtil.ParseAttrs(e.Tags);

            foreach (var item in e.Items)
            {
                Items.Add(new Item
                {
                    Name =
                    item.Name,
                    Alias = item.Alias,
                    Value = item.Value,
                    Comment = string.IsNullOrWhiteSpace(item.Comment) ? item.Alias : item.Comment,
                    Tags = DefUtil.ParseAttrs(item.Tags),
                });
            }
        }

        public override void Compile()
        {
            var fullName = FullName;

            int lastEnumValue = -1;
            var names = new HashSet<string>();
            foreach (var item in Items)
            {
                string value = item.Value.ToLower();
                if (!names.Add(item.Name))
                {
                    throw new Exception($"enum:'{fullName}' 字段:'{item.Name}' 重复");
                }
                if (string.IsNullOrEmpty(value))
                {
                    //  A,
                    item.IntValue = ++lastEnumValue;
                    item.Value = item.IntValue.ToString();
                }
                else if (int.TryParse(item.Value, out var v))
                {
                    //  A = 5,
                    item.IntValue = v;
                    lastEnumValue = v;
                }
                else if (value.StartsWith("0x"))
                {

                    if (int.TryParse(value.Substring(2), System.Globalization.NumberStyles.HexNumber, null, out var x))
                    {
                        item.IntValue = x;
                        lastEnumValue = x;
                    }
                    else
                    {
                        throw new Exception($"enum:'{fullName}' 枚举名:'{item.Name}' value:'{item.Value}' 非法");
                    }
                }
                else if (IsFlags)
                {
                    //  D = A | B | C,
                    string[] itemNames = item.Value.Split('|').Select(s => s.Trim()).ToArray();
                    foreach (var n in itemNames)
                    {
                        var index = Items.FindIndex(i => i.Name == n);
                        if (index < 0)
                        {
                            throw new Exception($"enum:'{fullName}' 枚举名:'{item.Name}' 值:'{item.Value}' 非法");
                        }
                        item.IntValue |= Items[index].IntValue;
                    }
                }
                else
                {
                    throw new Exception($"enum:'{fullName}' 枚举名:'{item.Name}' value:'{item.Value}' 非法");
                }

                if (!string.IsNullOrWhiteSpace(item.Name) && !_nameOrAlias2Value.TryAdd(item.Name, item.IntValue))
                {
                    throw new Exception($"enum:'{fullName}' 枚举名:'{Name}' 重复");
                }

                if (!string.IsNullOrWhiteSpace(item.Alias) && !_nameOrAlias2Value.TryAdd(item.Alias, item.IntValue))
                {
                    throw new Exception($"enum:'{fullName}' 枚举名:'{Name}' alias:'{item.Alias}' 重复");
                }
                if (_vaule2Name.TryGetValue(item.IntValue, out var itemName))
                {
                    if (IsUniqueItemId)
                    {
                        throw new Exception($"enum:'{fullName}' 枚举值:{item.IntValue} 重复. 枚举名:'{itemName}' <=> '{item.Name}'");
                    }
                }
                else
                {
                    _vaule2Name.Add(item.IntValue, item.Name);
                }
            }
        }

    }
}
