using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Runtime.Serialization;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;

namespace WCS.Core
{
    public class EntityEx<T>
    {
        public T Entity { get; private set; }
        public EntityEx(T ent)
        {
            this.Entity = ent;
        }
    }


    public class Device
    {
        static ConcurrentDictionary<string, Device> all = new ConcurrentDictionary<string, Device>();
        public static IEnumerable<Device> All
        {
            get
            {
                return all.Values;
            }
        }

        public static Device Find(string code)
        {
            if (all.TryGetValue(code, out var dev))
                return dev;
            else
                throw new Exception($"未找到设备{code}");
        }


        public string Code { get; set; } = "";

        public Dictionary<string, ProtocolInfo> Protocols { get; private set; } = new Dictionary<string, ProtocolInfo>();

        public Device(string code)
        {
            if (all.ContainsKey(code))
                throw new Exception($"{code}设备号重复");
            this.Code = code;
            all[code] = this;
        }

        ConcurrentDictionary<Type, object> ProtocolObjs(World world)
        {
            if (!ProtocolObjsOfWorld.TryGetValue(world, out var pobjs))
            {
                pobjs = new ConcurrentDictionary<Type, object>();
                ProtocolObjsOfWorld[world] = pobjs;
            }
            return pobjs;
        }

        ConcurrentDictionary<World, ConcurrentDictionary<Type, object>> ProtocolObjsOfWorld = new ConcurrentDictionary<World, ConcurrentDictionary<Type, object>>();

        public void AddProtocol(Type type, ProtocolInfo info)
        {
            if (!Protocols.TryAdd(type.AssemblyQualifiedName, info))
                throw new Exception($"{Code}重复添加协议{type.Name}"); 
        }
        public void AddProtocol<T>(ProtocolInfo info)
        {
            AddProtocol(typeof(T), info);
        }

        public bool HasProtocol(Type type)
        {
            return Protocols.ContainsKey(type.AssemblyQualifiedName);
        }
        public bool HasProtocol<T>()
        {
            return HasProtocol(typeof(T));
        }

        public T Protocol<T>(World world)
        {
            return (T)Protocol(typeof(T), world);
        }

        public object Protocol(Type protocolType, World world)
        {
            if (!HasProtocol(protocolType))
                throw new Exception($"{Code}不包含协议:{protocolType.Name}");

            if (!ProtocolObjs(world).ContainsKey(protocolType))
            {
                var type = typeof(Generator<,>);
                type = type.MakeGenericType(protocolType, Configs.ProtocolProxyBaseType);
                var m = type.GetMethod("Create", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);

                var info = Protocols[protocolType.AssemblyQualifiedName];
                var obj = m.Invoke(null, new object[] { new object[] { this, info, protocolType, world } });
                ProtocolObjs(world)[protocolType] = obj;
            }
            return ProtocolObjs(world)[protocolType];
        }

        public override string ToString()
        {
            return Code;
        }

         
        [JsonIgnore]
        public IEnumerable<Device> Targets
        {
            get
            {
                return GetFlags("Target").Select(v => Device.all[v]).ToList(); 
            }
        }

        [JsonIgnore]
        public IEnumerable<Device> Sources
        {
            get 
            {
                return Device.All.Where(v => v.GetFlags("Target").Contains(this.Code)).ToArray();
            }
        }

        public void AddTarget(params string[] codes)
        {           
            foreach (var code in codes)
            {
                if (HasFlag("Target", code))
                    throw new Exception($"{this.Code}已经存在目的地:{code}");
                AddFlag("Target", code);
            }            
        }


        [JsonIgnore]
        public Device? Parent
        {
            get
            {
                var code = GetFlag("Parent");
                if (string.IsNullOrEmpty(code))
                    return null;
                return Device.Find(code);
            }
            set
            {
                SetFlag("Parent", value.Code);
            }
        } 
         

        public Dictionary<string, List<string>> Flags { get; private set; } = new Dictionary<string, List<string>>();

        public void AddFlag(string key,string flag)
        {
            if (!Flags.ContainsKey(key))
                Flags[key] = new List<string>();
            Flags[key].Add(flag);
        }

        public void SetFlag(string key, string flag)
        { 
            var list=new List<string>();
            list.Add(flag);
            Flags[key] = list;
        }

        public IEnumerable<string> GetFlags(string key)
        {
            if (!Flags.ContainsKey(key))
                return new List<string>();
            return Flags[key];
        }

        public string? GetFlag(string key)
        {
            return GetFlags(key).FirstOrDefault();
        }


        public bool HasFlag(string key, string flag)
        {
            if(!Flags.ContainsKey(key))
                return false;
            return Flags[key].Contains(flag);
        }

        public void AddFlag(string flag)
        {
            SetFlag("Globals", flag);
        }

        public bool HasFlag(string flag)
        {
            return HasFlag("Globals", flag);
        } 

        public void AddFlag<T>(T flag) where T : struct, Enum
        {
            var key = typeof(T).AssemblyQualifiedName;
            if (Flags.ContainsKey(key))
            {
                dynamic value = Enum.Parse<T>(Flags[key].First());
                var str = (value | flag).ToString(); 
                SetFlag(key, str);
            }
            else
            {
                var str = flag.ToString();
                SetFlag(key, str);
            }
        }

        public bool HasFlag<T>(T flag) where T : struct, Enum
        {
            var key = typeof(T).AssemblyQualifiedName;
            if (!Flags.ContainsKey(key))
                return false;
            dynamic value = Enum.Parse<T>(Flags[key].First());
            return value.HasFlag(flag);
        }



    }

 
    public class Device<T> : EntityEx<Device>
    {
        public T Data { get; set; }
        public Device(Device device,World world) : base(device)
        {
            Data = Entity.Protocol<T>(world);
        }

        public override string ToString()
        {
            return Entity.Code;
        } 
    }

    public class Device<T, T2> : Device<T>
    {
        public T2 Data2 { get; set; }
        public Device(Device device, World world) : base(device, world)
        {
            Data2 = Entity.Protocol<T2>(world);
        }

    }

    public class Device<T, T2, T3> : Device<T, T2>
    {
        public T3 Data3 { get; set; }
        public Device(Device device, World world) : base(device, world)
        {
            Data3 = Entity.Protocol<T3>(world);
        }
    }

    public class Device<T, T2, T3, T4> : Device<T, T2, T3>
    {
        public T4 Data4 { get; set; }
        public Device(Device device, World world) : base(device, world)
        {
            Data4 = Entity.Protocol<T4>(world);
        }
    }

    public class Device<T, T2, T3, T4, T5> : Device<T, T2, T3, T4>
    {
        public T5 Data5 { get; set; }
        public Device(Device device, World world) : base(device, world)
        {
            Data5 = Entity.Protocol<T5>(world);
        }
    }

    public class Device<T, T2, T3, T4, T5, T6> : Device<T, T2, T3, T4, T5>
    {
        public T6 Data6 { get; set; }
        public Device(Device device, World world) : base(device, world)
        {
            Data6 = Entity.Protocol<T6>(world);
        }
    }
}
