using System.Collections;
using System.Runtime.InteropServices;

namespace WCS.Core;

public delegate void ValueChangedHandler<T>(PlcItem<T> sender, T value);

public delegate void ValueChangedHandler(PlcItem sender, object value);

public abstract class PlcItem
{
    public byte ArrayLength, StringLength;
    protected DataBlock Db;
    public ushort DB;

    public string Id;
    public string IP;

    private object lastValue;
    public string Name;
    public int Start; //按位计算 如，第2个字节，Start=8

    public PlcItem(string id, string name, DataBlock db, Type type, int start, byte arrLen = 1, byte strLength = 0)
    {
        Id = id;
        Name = name;
        Db = db;
        DataType = type;
        //this.Db.DBChanged += Db_DBChanged;
        Start = start;
        ArrayLength = arrLen;
        StringLength = strLength;

        DataSize = GetTypeLen(DataType);

        DataSizeOfBits = _getBitLen(DataType);
    }

    public Type DataType { get; }

    /// <summary>
    ///     数据类型所占的字节数
    /// </summary>
    public int DataSize { get; private set; }

    public int DataSizeOfBits { get; private set; }

    public object Value
    {
        get => getValue();
        set => setValue(value);
    }

    private int GetTypeLen(Type type)
    {
        var bitLen = _getBitLen(type);
        var mod = bitLen % 8;
        if (mod > 0)
            bitLen += 8 - mod;
        return bitLen / 8;
    }

    private int _getBitLen(Type type)
    {
        if (type.IsArray)
            return _getBitLen(type.GetElementType()) * ArrayLength;
        if (type == typeof(bool)) return 1;
        if (type.IsEnum) return Marshal.SizeOf(type.GetEnumUnderlyingType()) * 8;
        if (type == typeof(string)) return StringLength * 8;
        if (typeof(IList).IsAssignableFrom(type)) return 0;
        return Marshal.SizeOf(type) * 8;
    }

    protected abstract void setValue(object value);

    protected abstract object getValue();

    private bool ValueEquals(object obj1, object obj2)
    {
        if (obj1.GetType().IsArray)
        {
            var arr1 = obj1 as Array;
            if (obj2 is Array arr2)
                return !arr1.Cast<object>().Where((t, i) => !arr1.GetValue(i).Equals(arr2.GetValue(2))).Any();

            return true;
        }

        return obj1.Equals(obj2);
    }
}

public class PlcItem<T> : PlcItem
{
    public PlcItem(string id, string name, DataBlock db, int start, byte arrLen = 1, byte strLen = 0) : base(id, name,
        db, typeof(T), start, arrLen, strLen)
    {
    }

    public new T Value
    {
        get => (T)base.Value;
        set => base.Value = value;
    }

    protected override void setValue(object value)
    {
        var i = 0;
        while (true)
            try
            {
                Db.Write<T>(Start, (T)value, StringLength, ArrayLength);
                return;
            }
            catch (Exception ex)
            {
                if (i >= 3)
                    throw new Exception(
                        $"写入出错{ex.Message}--{Start}--{(T)value}--{StringLength}--{ArrayLength}--{Db.Entity.No}");
                i++;
                Thread.Sleep(100);
            }
    }

    protected override object getValue()
    {
        return Db.Read<T>(Start, StringLength, ArrayLength);
    }
}