using System.Collections.Concurrent;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Reflection;

namespace WCS.Core;

public static class Extentions
{
    private static readonly ConcurrentDictionary<object, object> ExObjs = new();

    private static readonly ConcurrentDictionary<World, ConcurrentDictionary<object, object>> ExObjsOfWorld = new();

    public static T GetEx<T>(object entity)
    {
        lock (ExObjs)
        {
            if (!ExObjs.ContainsKey(entity))
            {
                var obj = Activator.CreateInstance(typeof(T), entity);
                ExObjs[entity] = obj;
            }

            return (T)ExObjs[entity];
        }
    }

    public static PLC Ex(this PLCInfo source, World world)
    {
        return GetExOfWorld<PLC>(source, world);
    }

    private static T GetExOfWorld<T>(this object source, World world)
    {
        if (!ExObjsOfWorld.TryGetValue(world, out var objs))
        {
            ExObjsOfWorld.TryAdd(world, new ConcurrentDictionary<object, object>());
            objs = ExObjsOfWorld[world];
        }

        if (!objs.TryGetValue(source, out var obj))
            lock (objs)
            {
                if (!objs.ContainsKey(source))
                {
                    obj = Activator.CreateInstance(typeof(T), source, world);
                    objs.TryAdd(source, obj);
                }

                obj = objs[source];
            }

        return (T)obj;
    }

    public static DataBlock Ex(this DBInfo source, World world)
    {
        return GetExOfWorld<DataBlock>(source, world);
    }

    public static DataBlock[] GetDataBlocks(this World source)
    {
        ExObjsOfWorld.TryGetValue(source, out var dic);
        if (dic == null)
            return new DataBlock[0];
        var res = dic.Values.OfType<DataBlock>().ToArray();
        return res;
    }

    public static WorldEx Ex(this World source)
    {
        return GetEx<WorldEx>(source);
    }


    public static void AddSafe<T>(this List<T> source, T item)
    {
        lock (source)
        {
            source.Add(item);
        }
    }

    public static void AddSafe<T>(this List<T> source, IEnumerable<T> item)
    {
        lock (source)
        {
            source.AddRange(item);
        }
    }

    public static string Description<T>(this T source) where T : struct, Enum
    {
        var name = Enum.GetName(source);
        var f = source.GetType().GetField(name);
        var attr = f.GetCustomAttribute<DescriptionAttribute>();
        if (attr == null)
            return source.ToString();
        return attr.Description;
    }

    public static object Copy(this object source, Type t)
    {
        var obj = Activator.CreateInstance(t);
        foreach (var p in t.GetProperties())
        {
            var p2 = source.GetType().GetProperty(p.Name);
            var value = p2.GetValue(source);
            p.SetValue(obj, value);
        }

        return obj;
    }

    public static object Copy(this object source, Type t, DateTime farme)
    {
        var obj = Activator.CreateInstance(t);
        foreach (var p in t.GetProperties())
        {
            var p2 = source.GetType().GetProperty(p.Name);
            if (p2 == null) continue;
            var value = p2.GetValue(source);
            //判断一下P2的类型是否为字符串
            if (p2.PropertyType == typeof(string))
            {
                var sValue = (string)value;
                value = sValue.Trim('\0', '\a', '\b', '\f', '\n', '\r', '\t', '\v').Trim();
            }

            p.SetValue(obj, value);
        }

        t.GetProperty("Frame").SetValue(obj, farme);
        return obj;
    }

    public static T Copy<T>(this object source)
    {
        return (T)source.Copy(typeof(T), DateTime.Now);
    }
}

public static class BitExtension
{
    #region ushort

    /// <summary>
    ///     设置指定位置的位值的值
    /// </summary>
    /// <param name="value">ushort对象</param>
    /// <param name="position">指定位置</param>
    /// <param name="flag">值</param>
    /// <returns></returns>
    public static ushort SetBit(this ushort value, int position, bool flag)
    {
        return SetBits(value, position, 1, flag ? (byte)1 : (byte)0);
    }

    /// <summary>
    ///     批量设置指定位置的位值的值
    /// </summary>
    /// <param name="value">ushort对象</param>
    /// <param name="position">开始位置</param>
    /// <param name="length">长度</param>
    /// <param name="bits">值</param>
    /// <returns></returns>
    public static ushort SetBits(this ushort value, int position, int length, ushort bits)
    {
        if (length <= 0 || position >= 16) return value;

        var mask = (2 << (length - 1)) - 1;

        value &= (ushort)~(mask << position);
        value |= (ushort)((bits & mask) << position);

        return value;
    }

    /// <summary>
    ///     获取指定位置的值
    /// </summary>
    /// <param name="value">ushort对象</param>
    /// <param name="position">指定位置</param>
    /// <returns></returns>
    public static bool GetBit(this ushort value, int position)
    {
        return GetBits(value, position, 1) == 1;
    }

    /// <summary>
    ///     批量获取指定位置的值
    /// </summary>
    /// <param name="value">ushort对象</param>
    /// <param name="position">开始位值</param>
    /// <param name="length">长度</param>
    /// <returns></returns>
    public static ushort GetBits(this ushort value, int position, int length)
    {
        if (length <= 0 || position >= 16) return 0;

        var mask = (2 << (length - 1)) - 1;

        return (ushort)((value >> position) & mask);
    }

    #endregion ushort

    #region byte

    /// <summary>
    ///     设置指定位置的值
    /// </summary>
    /// <param name="value">byte对象</param>
    /// <param name="position">指定位置</param>
    /// <param name="flag">设置值</param>
    /// <returns></returns>
    public static byte SetBit(this byte value, int position, bool flag)
    {
        if (position >= 8) return value;

        var mask = (2 << (1 - 1)) - 1;

        value &= (byte)~(mask << position);
        value |= (byte)(((flag ? 1 : 0) & mask) << position);

        return value;
    }

    /// <summary>
    ///     获取指定位置的值
    /// </summary>
    /// <param name="value">byte对象</param>
    /// <param name="position">指定位置</param>
    /// <returns></returns>
    public static bool GetBit(this byte value, int position)
    {
        if (position >= 8) return false;

        var mask = (2 << (1 - 1)) - 1;

        return (byte)((value >> position) & mask) == 1;
    }

    #endregion byte

    #region uint

    /// <summary>
    ///     设置指定位置的位值的值
    /// </summary>
    /// <param name="value">uint对象</param>
    /// <param name="position">指定位置</param>
    /// <param name="flag">值</param>
    /// <returns></returns>
    public static uint SetBit(this uint value, int position, bool flag)
    {
        return SetBits(value, position, 1, flag ? (byte)1 : (byte)0);
    }

    /// <summary>
    ///     批量设置指定位置的位值的值
    /// </summary>
    /// <param name="value">uint对象</param>
    /// <param name="position">开始位置</param>
    /// <param name="length">长度</param>
    /// <param name="bits">值</param>
    /// <returns></returns>
    public static uint SetBits(this uint value, int position, int length, uint bits)
    {
        if (length <= 0 || position >= 32) return value;

        var mask = (2 << (length - 1)) - 1;

        value &= (uint)~(mask << position);
        value |= (uint)((bits & mask) << position);

        return value;
    }

    /// <summary>
    ///     获取指定位置的值
    /// </summary>
    /// <param name="value">uint对象</param>
    /// <param name="position">指定位置</param>
    /// <returns></returns>
    public static bool GetBit(this uint value, int position)
    {
        return GetBits(value, position, 1) == 1;
    }

    /// <summary>
    ///     批量获取指定位置的值
    /// </summary>
    /// <param name="value">uint对象</param>
    /// <param name="position">开始位值</param>
    /// <param name="length">长度</param>
    /// <returns></returns>
    public static uint GetBits(this uint value, int position, int length)
    {
        if (length <= 0 || position >= 32) return 0;

        var mask = (2 << (length - 1)) - 1;

        return (uint)((value >> position) & mask);
    }

    #endregion uint

    #region ulong

    /// <summary>
    ///     设置指定位置的位值的值
    /// </summary>
    /// <param name="value">ulong对象</param>
    /// <param name="position">指定位置</param>
    /// <param name="flag">值</param>
    /// <returns></returns>
    public static ulong SetBit(this ulong value, int position, bool flag)
    {
        return SetBits(value, position, 1, flag ? (byte)1 : (byte)0);
    }

    /// <summary>
    ///     批量设置指定位置的位值的值
    /// </summary>
    /// <param name="value">ulong对象</param>
    /// <param name="position">开始位置</param>
    /// <param name="length">长度</param>
    /// <param name="bits">值</param>
    /// <returns></returns>
    public static ulong SetBits(this ulong value, int position, int length, ulong bits)
    {
        if (length <= 0 || position >= 64) return value;

        var mask = (ulong)(2 << (length - 1)) - 1;

        value &= ~(mask << position);
        value |= (bits & mask) << position;

        return value;
    }

    /// <summary>
    ///     获取指定位置的值
    /// </summary>
    /// <param name="value">ulong对象</param>
    /// <param name="position">指定位置</param>
    /// <returns></returns>
    public static bool GetBit(this ulong value, int position)
    {
        return GetBits(value, position, 1) == 1;
    }

    /// <summary>
    ///     批量获取指定位置的值
    /// </summary>
    /// <param name="value">ulong对象</param>
    /// <param name="position">开始位值</param>
    /// <param name="length">长度</param>
    /// <returns></returns>
    public static ulong GetBits(this ulong value, int position, int length)
    {
        if (length <= 0 || position >= 64) return 0;

        var mask = (ulong)(2 << (length - 1)) - 1;

        return (value >> position) & mask;
    }

    #endregion ulong
}

public static class ExpressionExtensions
{
    public static string ExpToString(this LambdaExpression source)
    {
        var str = "";
        try
        {
            str = LambdaToString(source);
        }
        catch
        {
            str = source.ToString();
        }

        var arr = str.Split("Convert(");
        arr = arr.Select(v =>
        {
            if (!v.Contains(','))
                return v;
            var index = v.IndexOf(',');
            var index2 = v.IndexOf(')', index);
            var sub = v.Substring(index, index2 - index + 1);
            var str = v.Replace(sub, "");
            return str;
        }).ToArray();
        var res = string.Join("", arr);
        return res;
    }

    public static string LambdaToString(LambdaExpression expression)
    {
        var replacements = new Dictionary<string, string>();
        WalkExpression(replacements, expression);
        var body = expression.ToString();
        foreach (var parm in expression.Parameters)
        {
            var parmName = parm.Name;
            var parmTypeName = parm.Type.Name;
            body = body.Replace(parmName + " =>", "(" + parmTypeName + " v) =>");
        }

        foreach (var replacement in replacements) body = body.Replace(replacement.Key, replacement.Value);
        return body;
    }

    private static void WalkExpression(Dictionary<string, string> replacements, Expression expression)
    {
        switch (expression.NodeType)
        {
            case ExpressionType.MemberAccess:
                var replacementExpression = expression.ToString();
                if (replacementExpression.Contains("value("))
                {
                    var replacementValue = Expression.Lambda(expression).Compile().DynamicInvoke().ToString();
                    if (!replacements.ContainsKey(replacementExpression))
                        replacements.Add(replacementExpression, replacementValue);
                }

                break;
            case ExpressionType.GreaterThan:
            case ExpressionType.GreaterThanOrEqual:
            case ExpressionType.LessThan:
            case ExpressionType.LessThanOrEqual:
            case ExpressionType.OrElse:
            case ExpressionType.AndAlso:
            case ExpressionType.Equal:
            case ExpressionType.NotEqual:
                var bexp = expression as BinaryExpression;
                WalkExpression(replacements, bexp.Left);
                WalkExpression(replacements, bexp.Right);
                break;
            case ExpressionType.Call:
                var mcexp = expression as MethodCallExpression;
                foreach (var argument in mcexp.Arguments) WalkExpression(replacements, argument);
                break;
            case ExpressionType.Lambda:
                var lexp = expression as LambdaExpression;
                WalkExpression(replacements, lexp.Body);
                break;
            case ExpressionType.Constant:
                //do nothing
                break;
            case ExpressionType.Convert:
                var exp = expression as UnaryExpression;
                WalkExpression(replacements, exp.Operand);
                break;
        }
    }


    private static bool When<T>(this T source, Expression<Func<T, bool>> exp) where T : class
    {
        var str = exp.ExpToString();
        try
        {
            var res = exp.Compile().Invoke(source);
            str += res ? " 成立" : " 不成立";
            return res;
        }
        catch (Exception ex)
        {
            str += ex.GetBaseException().Message;
            throw;
        }
        finally
        {
            Console.WriteLine(str);
            Console.WriteLine("------------------------------------");
        }
    }
}