using System.Collections.Concurrent;
using System.Diagnostics;
using System.Reflection;

namespace WCS.Core;

public abstract class SystemBase : DescriptionClass
{
    public SystemBase()
    {
        var attr = GetType().GetCustomAttribute<BelongToAttribute>();
        if (attr == null) return;
        var wt = attr.WorldType;
        World = World.Worlds.First(v => v.GetType() == wt);
    }

    public World World { get; }

    public abstract List<object> GetObjects();

    public abstract void Update(List<WorkTimes> list);
}

public abstract class SystemBase<T> : SystemBase
{
    public SystemBase()
    {
        Objects = Create(); //.Select(v=>Activator.CreateInstance(typeof(T),v)).OfType<T>().ToList();
    }

    public List<T> Objects { get; set; }

    /// <summary>
    ///     对所有Objects并行循环执行Do
    /// </summary>
    protected abstract bool ParallelDo { get; }

    public override void Update(List<WorkTimes> list)
    {
        if (ParallelDo)
            Parallel.ForEach(Objects, new ParallelOptions { MaxDegreeOfParallelism = 256 }, obj =>
            {
                var sw = new Stopwatch();
                sw.Start();
                InvokeDo(obj);
                sw.Stop();
                list.AddSafe(new WorkTimes { Key = $"{obj?.ToString()}", Total = sw.ElapsedMilliseconds });
            });
        else
            foreach (var obj in Objects)
            {
                var sw = new Stopwatch();
                sw.Start();
                InvokeDo(obj);
                sw.Stop();
                list.AddSafe(new WorkTimes { Key = $"{obj?.ToString()}", Total = sw.ElapsedMilliseconds });
            }
    }

    private void InvokeDo(T obj)
    {
        var channel = new Channel
        {
            World = World.Description,
            Stage = "DoLogics",
            System = Description,
            Item = obj.ToString()
        };
        try
        {
            if (channel.Item == "1")
            {
                var a = 1;
            }

            Ltc.SetChannel(channel);
            World.OnInternalLog(channel, "开始");
            Do(obj);
        }
        catch (Exception ex)
        {
            World.OnError(channel, ex);
        }
        finally
        {
            World.OnInternalLog(channel, "结束");
            World.Publish();
        }
    }

    public abstract List<T> Create();

    public abstract void Do(T obj);

    public override List<object> GetObjects()
    {
        return Objects.OfType<object>().ToList();
    }
}

public abstract class DeviceSystem<T> : SystemBase<T> where T : EntityEx<Device>
{
    public override List<T> Create()
    {
        var t = typeof(T);
        while (true)
        {
            if (t.IsGenericType) break;
            t = t.BaseType;
        }

        if (t == typeof(EntityEx<Device>))
        {
            var list = Device.All
                .Where(Select)
                .Select(v => Activator.CreateInstance(typeof(T), v)).OfType<T>().ToList(); //此时才实例化Protocol 
            return list;
        }
        else
        {
            var types = t.GetGenericArguments();
            var list = Device.All.Where(v => types.All(v.HasProtocol))
                .Where(Select)
                .Select(v => Activator.CreateInstance(typeof(T), v, World)).OfType<T>().ToList(); //此时才实例化Protocol
            if (list.Count == 0)
            {
                //throw new Exception($"{this.GetType().Name}未匹配到任何Device");
            }

            return list;
        }
    }

    /// <summary>
    ///     筛选出需要实例化Protocol的Device
    /// </summary>
    /// <param name="dev"></param>
    /// <returns></returns>
    public abstract bool Select(Device dev);

    //public abstract List<Device> CreateDevices();
}

public abstract class ServiceSystem<T, TR> : SystemBase
{
    private readonly ConcurrentQueue<Action<List<WorkTimes>>> Actions = new();

    public TR Invoke(T obj)
    {
        var flag = false;
        var result = default(TR);
        Actions.Enqueue(list =>
        {
            var sw = new Stopwatch();
            sw.Start();
            try
            {
                result = InvokeDo(obj);
            }
            finally
            {
                sw.Stop();
                list.AddSafe(new WorkTimes { Key = $"{obj?.ToString()}", Total = sw.ElapsedMilliseconds });
                flag = true;
            }
        });
        SpinWait.SpinUntil(() => flag);
        return result;
    }

    protected abstract TR Do(T obj);

    public override List<object> GetObjects()
    {
        return new List<object>();
    }

    public override void Update(List<WorkTimes> list)
    {
        while (Actions.TryDequeue(out var act)) act(list);
    }

    private TR InvokeDo(T obj)
    {
        var channel = new Channel
        {
            World = World.Description,
            Stage = "DoLogics",
            System = Description,
            Item = obj.ToString()
        };
        try
        {
            Ltc.SetChannel(channel);
            World.OnInternalLog(channel, "开始");
            return Do(obj);
        }
        finally
        {
            World.OnInternalLog(channel, "结束");
            World.Publish();
        }
    }
}

public abstract class ServiceSystem<T> : SystemBase
{
    private readonly ConcurrentQueue<Action<List<WorkTimes>>> Actions = new();

    public void Invoke(T obj)
    {
        Actions.Enqueue(list =>
        {
            var sw = new Stopwatch();
            sw.Start();
            try
            {
                InvokeDo(obj);
            }
            finally
            {
                sw.Stop();
                list.AddSafe(new WorkTimes { Key = $"{obj?.ToString()}", Total = sw.ElapsedMilliseconds });
            }
        });
    }

    protected abstract void Do(T obj);

    private void InvokeDo(T obj)
    {
        var channel = new Channel
        {
            World = World.Description,
            Stage = "DoLogics",
            System = Description,
            Item = obj.ToString()
        };
        try
        {
            Ltc.SetChannel(channel);
            World.OnInternalLog(channel, "开始");
            Do(obj);
        }
        finally
        {
            World.OnInternalLog(channel, "结束");
            World.Publish();
        }
    }

    public override List<object> GetObjects()
    {
        return new List<object>();
    }

    public override void Update(List<WorkTimes> list)
    {
        while (Actions.TryDequeue(out var act)) act(list);
    }
}