using System.Collections.Concurrent;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using FreeRedis;

namespace WCS.Core;

/// <summary>
///     世界用来管理下属System的执行周期，此为默认世界。也可以通过继承此类创建多个不同世界，不同世界的执行周期相互独立，不受其它世界延迟干扰。
/// </summary>
[Description("默认世界")]
public abstract class World : DescriptionClass
{
    public SystemBase[] Systems
    {
        get { return SystemGroups.SelectMany(v => v.Value).ToArray(); }
    }

    [DllImport("winmm.dll", EntryPoint = "timeBeginPeriod")]
    public static extern uint MM_BeginPeriod(uint uMilliseconds);

    [DllImport("winmm.dll", EntryPoint = "timeEndPeriod")]
    public static extern uint MM_EndPeriod(uint uMilliseconds);

    public T GetSystem<T>() where T : SystemBase
    {
        var sys = Systems.FirstOrDefault(v => v.GetType() == typeof(T)) as T;
        if (sys == null) throw new Exception($"世界{GetType().Name}中不存在系统{typeof(T).Name}");
        return sys;
    }
    static ConcurrentDictionary<World, WorkTimes> WorldsInfo = new ConcurrentDictionary<World, WorkTimes>();
    protected virtual void FrameInfo(WorkTimes wt)
    {
        //WorldsInfo[this] = wt;
        if (wt.Total > Interval) Console.ForegroundColor = ConsoleColor.Red;
        Console.WriteLine(wt.GetInfo());
        Console.ResetColor();
    }
    static string LastInfo = "";
    static void Print()
    {
        var info = "\n" + string.Join("\n", WorldsInfo.Values.Select(v => v.GetInfo().PadRight(200))).PadRight(1000);

        if (info != LastInfo)
        {
            Console.CursorVisible = false;
            Console.WriteLine(info);
            Console.SetCursorPosition(0, 0);
            LastInfo = info;
        }

    }
    public void Log<T>(T log) where T : ILog
    {
        var channel = Ltc.GetChannel();
        if (channel != null)
            OnLog(Ltc.GetChannel(), log);
    }

    protected internal abstract void OnError(Channel channel, Exception exception);

    protected internal abstract void OnInternalLog(Channel channel, string msg);

    protected abstract void OnLog(Channel channel, object logObj);

    protected abstract IEnumerable<string> GetChannelMsg(Channel channel);

    internal void Publish()
    {
        var channel = Ltc.GetChannel();
        if (channel != null)
        {
            var msgs = GetChannelMsg(channel);
            var msg = string.Join("\n", msgs);
            this.Ex().Publish(channel, msg);
        }
    }

    #region Static

    /// <summary>
    ///     世界是否启用成功
    /// </summary>
    public static bool IsStart;

    private static List<World> _Worlds;

    internal static List<World> Worlds
    {
        get
        {
            if (_Worlds == null)
            {
                _Worlds = new List<World>();
                //_Worlds.Add(new World());//默认世界
                var arr = AppDomain.CurrentDomain.GetAssemblies().Select(v => v.GetTypes()).SelectMany(v => v)
                    .Where(v => typeof(World).IsAssignableFrom(v) && v != typeof(World))
                    .Select(v => Activator.CreateInstance(v)).OfType<World>().ToArray(); //自定义世界
                _Worlds.AddRange(arr);
            }

            return _Worlds;
        }
    }

    public static T GetWorldInstance<T>() where T : World
    {
        return (T)Worlds.First(v => v.GetType() == typeof(T));
    }

    public static T GetSystemInstance<T>() where T : SystemBase
    {
        try
        {
            return (T)Worlds.SelectMany(v => v.Systems).First(v => v.GetType() == typeof(T));
        }
        catch (Exception ex)
        {
            throw new Exception($"系统：{typeof(T).Name}未设置BelongToAttribute");
        }
    }

    public static void StartAll()
    {
        MM_BeginPeriod(1);
        Worlds.ForEach(w => w.Init());
        foreach (var w in Worlds)
        {
            if (w.SystemTypes.Length == 0) continue;
            w.Start();
        }

        var arr = Worlds.SelectMany(v => v.SystemGroups).SelectMany(v => v.Value)
            .SelectMany(v => v.GetObjects().OfType<EntityEx<Device>>().Select(d => new { Sys = v, Obj = d }))
            .GroupBy(v => v.Obj.Entity.Code)
            .Select(v => new
            {
                Code = v.Key, Systems = v.Select(d => d.Sys.GetType().Name).ToArray(),
                Worlds = v.Select(d => d.Sys.World.GetType().Name).Distinct().ToArray()
            })
            .Where(v => v.Systems.Length > 1).ToArray();
        if (arr.Length > 0)
        {
            var msgs = arr.Select(v =>
                    $"设备{v.Code}同时存在于{v.Systems.Length}个系统:({string.Join(',', v.Systems)}),{v.Worlds.Length}个世界:({string.Join(',', v.Worlds)})中")
                .ToArray();
            var str = string.Join('\n', msgs);
            Console.ForegroundColor = ConsoleColor.DarkYellow;
            Console.WriteLine(str);
            Console.ResetColor();
        }

        IsStart = true;

        //Task.Run(() =>
        //{

        //    var i = 0;
        //    while (IsStart)
        //    {
        //        i++;
        //        if (i % 30 == 1)
        //        {
        //            Console.Clear();
        //        }
        //        Print();
        //        Task.Delay(100).Wait();
        //    }
        //});
    }

    public static void StopAll()
    {
        MM_EndPeriod(1);
        foreach (var w in Worlds) w.Stop();
    }

    #endregion Static

    #region Dynamic

    public bool Stoped;
    private readonly Dictionary<Type, int> TypeOrder = new();
    private readonly Dictionary<int, List<SystemBase>> SystemGroups = new();
    protected Type[] SystemTypes;

    /// <summary>
    ///     周期最小间隔时间(毫秒)
    /// </summary>
    protected abstract int Interval { get; }

    public DateTime Frame { get; private set; }

    public World()
    {
        SystemTypes = GetSystemTypes();
    }

    protected virtual Type[] GetSystemTypes()
    {
        var sysTypes = AppDomain.CurrentDomain.GetAssemblies().Select(v => v.GetTypes()).SelectMany(v => v)
            .Where(v => !v.IsAbstract)
            .Where(v => typeof(SystemBase).IsAssignableFrom(v))
            .Where(v =>
            {
                var attr = v.GetCustomAttribute<BelongToAttribute>();
                if (attr == null) return GetType() == typeof(World);
                if (attr.WorldType == GetType()) return true;
                return false;
            }).ToArray();
        return sysTypes;
    }

    //public List<Device> Devices;

    /// <summary>
    ///     初始化，实例化世界下的所有系统
    /// </summary>
    public virtual void Init()
    {
        //Devices = Protocols.Generate(this);
        foreach (var type in SystemTypes)
        {
            var sysDesc = type.GetCustomAttribute<DescriptionAttribute>()?.Description;
            Ltc.SetChannel(new Channel
                { World = Description, Stage = "Init", System = sysDesc ?? type.Name, Item = "排序" });
            Set(type, 0);
        }

        var arr = TypeOrder.OrderBy(v => v.Value).Select(v => v.Key).ToArray();
        var gs = TypeOrder.GroupBy(v => v.Value).OrderBy(v => v.Key).ToArray();
        for (var i = 0; i < gs.Length; i++)
        {
            var g = gs[i];
            var list = new List<SystemBase>();
            var sysArr = g.Select(v =>
            {
                var sysDesc = v.Key.GetCustomAttribute<DescriptionAttribute>()?.Description;
                Ltc.SetChannel(new Channel
                    { World = Description, Stage = "Init", System = sysDesc ?? v.Key.Name, Item = "构造" });
                return Activator.CreateInstance(v.Key);
            }).OfType<SystemBase>().ToArray();
            list.AddRange(sysArr);
            SystemGroups.Add(i, list);
        }
    }

    private int Set(Type type, int level)
    {
        if (!SystemTypes.Contains(type))
            throw new Exception($"OrderAttribute设置错误,与目标不属于同一世界。类型：{type}。");
        if (level > 10) throw new Exception($"OrderAttribute设置错误,导致死循环。类型：{type}。");
        var attr = type.GetCustomAttribute<OrderAttribute>();
        if (attr != null)
        {
            level++;
            var num = Set(attr.SystemType, level);
            TypeOrder[type] = num + (int)attr.Order;
        }
        else
        {
            TypeOrder[type] = 0;
        }

        return TypeOrder[type];
    }

    /// <summary>
    ///     开启世界主循环
    /// </summary>
    public void Start()
    {
        Stoped = false;
        Task.Run(Loop); //不要使用Thread,可以使用ThreadPool
    }

    private void Loop()
    {
        var sw = new Stopwatch();
        while (!Stoped)
        {
            Frame = DateTime.Now;
            var wt = new WorkTimes();
            wt.Key = $"{Description} 周期:{Interval}";
            sw.Restart();
            try
            {
                BeforeUpdate(wt.Items);
                Update(wt.Items);
                AfterUpdate(wt.Items);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }

            sw.Stop();
            var workTimes = (int)sw.ElapsedMilliseconds;
            var ms = Interval - workTimes;
            //sw.Start();
            if (ms > 0) Thread.Sleep(ms); //不要使用Task.Delay().Wait()
            //sw.Stop();
            //var total = sw.ElapsedMilliseconds;
            wt.Total = workTimes;
            FrameInfo(wt);
        }
    }

    public void Stop()
    {
        Stoped = true;
    }

    private void Update(List<WorkTimes> list)
    {
        var wt = new WorkTimes();
        wt.Key = "读取PLC数据";
        var sw = new Stopwatch();
        sw.Start();
        LoadPlcData(wt.Items);
        sw.Stop();
        wt.Total = sw.ElapsedMilliseconds;
        list.AddSafe(wt);

        wt = new WorkTimes();
        wt.Key = "系统业务";
        sw.Restart();
        DoLogics(wt.Items);
        sw.Stop();
        wt.Total = sw.ElapsedMilliseconds;
        list.AddSafe(wt);
    }

    private void LoadPlcData(List<WorkTimes> list)
    {
        Parallel.ForEach(this.GetDataBlocks(), db =>
        {
            var channel = new Channel
            {
                World = GetType().Name,
                Stage = "LoadPlcData",
                System = "",
                Item = $"{db.Entity.PLCInfo.IP}_{db.Entity.No}"
            };
            var sw = new Stopwatch();
            sw.Start();
            try
            {
                db.RefreshData();
            }
            catch (Exception ex)
            {
                this.Ex().Publish(channel, ex.GetBaseException().Message);
            }

            sw.Stop();
            list.AddSafe(new WorkTimes
                { Key = $"{db.Entity.PLCInfo.IP}/{db.Entity.No}", Total = sw.ElapsedMilliseconds });
        });
    }

    private void DoLogics(List<WorkTimes> list)
    {
        foreach (var group in SystemGroups)
        {
            var wt = new WorkTimes();
            wt.Key = $"组{group.Key}";
            var sw = new Stopwatch();
            sw.Restart();
            Parallel.ForEach(group.Value, sys =>
            {
                var wt2 = new WorkTimes();
                wt2.Key = sys.Description;
                var sw2 = new Stopwatch();
                sw2.Start();
                try
                {
                    sys.Update(wt2.Items);
                }
                catch (Exception ex)
                {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine(ex.GetBaseException().Message);
                    Console.ResetColor();
                }

                sw2.Stop();
                wt2.Total = sw2.ElapsedMilliseconds;
                list.AddSafe(wt2);
            });
            sw.Stop();
            wt.Total = sw.ElapsedMilliseconds;
            //list.AddSafe(wt);
        }
    }

    protected virtual void BeforeUpdate(List<WorkTimes> list)
    {
    }

    protected virtual void AfterUpdate(List<WorkTimes> list)
    {
    }

    #endregion Dynamic
}

public interface ILog
{
}

public class WorldEx : EntityEx<World>
{
    private readonly ConcurrentQueue<string> ChannelList = new();
    private readonly RedisClient Redis = new(Configs.DebugRedisUrl);

    private DateTime SubTime = DateTime.Now;

    public WorldEx(World ent) : base(ent)
    {
        return;
        Redis.Subscribe("Login", (channel, msg) =>
        {
            ChannelList.Clear();
            foreach (var m in msg.ToString().Split(',')) ChannelList.Enqueue(m);

            SubTime = DateTime.Now;
            Console.WriteLine($"调试工具正在使用中,已订阅:{msg}");
        });
    }

    public void Publish(Channel channel, string msg)
    {
        if ((DateTime.Now - SubTime).TotalSeconds > 20)
            return;
        var flag = false;

        flag = ChannelList.Any(v =>
        {
            var b = Regex.Match(channel.ToString(), $"^{v.Replace("*", ".*")}$");
            return b.Success;
        });

        if (flag)
            Redis.Publish(channel.ToString(), msg);
    }
}

public class WorkTimes
{
    public string Key { get; set; } = "";
    public long Total { get; set; }
    public List<WorkTimes> Items { get; set; } = new();

    public override string ToString()
    {
        return $"{Key},明细:{Items.Count},耗时:{Total}";
    }

    public string GetInfo()
    {
        var str = $"[{ToString()}]";
        if (Items.Count > 0) str += $" > {Items.MaxBy(v => v.Total)?.GetInfo()}";
        return str;
    }
}

public abstract class AttrClass<T> where T : Attribute
{
    public AttrClass()
    {
        Attr = GetType().GetCustomAttribute<T>();
    }

    public T? Attr { get; }
}

public abstract class DescriptionClass : AttrClass<DescriptionAttribute>
{
    public string Description => Attr != null ? Attr.Description : GetType().Name;
}