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

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
    {
        protected DataBlock Db;
        public int Start;//按位计算 如，第2个字节，Start=8

        public byte ArrayLength, StringLength;

        public string Id;
        public string Name;
        public string IP;
        public ushort DB;
        public Type DataType { get; private set; }

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

        public int DataSizeOfBits { get; private set; }

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

            DataSize = (byte)GetTypeLen(DataType);

            DataSizeOfBits = _getBitLen(DataType);
        }

        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;
            else if (type.IsEnum)
                return Marshal.SizeOf(type.GetEnumUnderlyingType()) * 8;
            else if (type == typeof(string))
                return (StringLength) * 8;
            else
            {
                if (typeof(IList).IsAssignableFrom(type))
                    return 0;
                return Marshal.SizeOf(type) * 8;
            }
        }

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

        protected abstract void setValue(object value);

        protected abstract object getValue();

        private object lastValue;

        private bool ValueEquals(object obj1, object obj2)
        {
            if (obj1.GetType().IsArray)
            {
                var arr1 = obj1 as Array;
                var arr2 = obj2 as Array;
                if (arr2 == null)
                {
                    return true;
                }
                for (int i = 0; i < arr1.Length; i++)
                {
                    if (!arr1.GetValue(i).Equals(arr2.GetValue(2)))
                        return false;
                }
                return true;
            }
            else
            {
                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
            {
                return (T)base.Value;
            }
            set
            {
                base.Value = value;
            }
        }

        protected override void setValue(object value)
        {
            int i = 0;
            while (true)
            {
                try
                {
                    Db.Write<T>(Start, (T)value, StringLength, ArrayLength);
                    return;
                }
                catch
                {
                    if (i >= 3)
                        throw;
                    else
                    {
                        i++;
                        Thread.Sleep(100);
                    }
                }
            }
        }

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