using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace FLY.ModbusModule
{
    /// <summary>
    /// 模拟一个PLC 的寄存器环境，每种操作寄存器的通信协议，都直接操作它
    /// </summary>
    public class PLCRegister
    {
        UInt16[] m_D_regs;
        bool[] m_M_coils;

        public UInt16[] D_regs
        {
            get
            {
                return m_D_regs;
            }
        }
        bool[] M_coils
        {
            get
            {
                return m_M_coils;
            }
        }

        public PLCRegister(int coil_cnt, int reg_cnt)
        {
            m_M_coils = new bool[coil_cnt];
            m_D_regs = new ushort[reg_cnt];
        }

        #region 寄存器事件触发
        public class RegChangedEventArgs : EventArgs
        {
            public enum REG_TYPE
            {
                REG,
                COIL
            }
            public RegChangedEventArgs(REG_TYPE type, int addr, int len)
            {
                RegType = type;
                Addr = addr;
                Len = len;
            }
            public REG_TYPE RegType { get; set; }
            public int Addr { get; set; }
            public int Len { get; set; }

            public bool IsCover(int addr)
            {
                if ((addr >= Addr) && (addr < (Addr + Len)))
                {
                    return true;
                }
                else
                    return false;
            }
            public bool IsCover(int addr, int len)
            {
                if (addr >= Addr + Len)
                    return false;
                else if (Addr >= (addr + len))
                    return false;
                else
                    return true;
            }
        }
        public delegate void RegChangedEventHandler(object sender, RegChangedEventArgs e);
        public event RegChangedEventHandler RegChanged;
        void NotifyRegChanged(RegChangedEventArgs.REG_TYPE type, int addr, int len)
        {
            if (RegChanged != null)
            {
                RegChanged.Invoke(this, new RegChangedEventArgs(type, addr, len));
            }
        }
        #endregion
        #region float
        public float GetFloat(int addr)
        {
            List<byte> buf = new List<byte>();
            buf.AddRange(BitConverter.GetBytes(D_regs[addr]));
            buf.AddRange(BitConverter.GetBytes(D_regs[addr + 1]));
            return BitConverter.ToSingle(buf.ToArray(), 0);
        }
        public void SetFloat(int addr, float value)
        {
            if (GetFloat(addr) != value)
            {
                byte[] buf = BitConverter.GetBytes(value);
                D_regs[addr] = BitConverter.ToUInt16(buf, 0);
                D_regs[addr + 1] = BitConverter.ToUInt16(buf, 2);
                NotifyRegChanged(RegChangedEventArgs.REG_TYPE.REG, addr, 2);
            }
        }
        #endregion
        #region int32
        public int GetInt32(int addr)
        {
            List<byte> buf = new List<byte>();
            buf.AddRange(BitConverter.GetBytes(D_regs[addr]));
            buf.AddRange(BitConverter.GetBytes(D_regs[addr + 1]));
            return BitConverter.ToInt32(buf.ToArray(), 0);
        }
        public void SetInt32(int addr, int value)
        {
            int v = GetInt32(addr);
            if (v != value)
            {
                byte[] buf = BitConverter.GetBytes(value);
                D_regs[addr] = BitConverter.ToUInt16(buf, 0);
                D_regs[addr + 1] = BitConverter.ToUInt16(buf, 2);
                NotifyRegChanged(RegChangedEventArgs.REG_TYPE.REG, addr, 2);
            }
        }
        #endregion
        #region uint16
        public void SetUInt16(int addr, UInt16 value)
        {
            if (GetUInt16(addr) != value)
            {
                D_regs[addr] = value;
                NotifyRegChanged(RegChangedEventArgs.REG_TYPE.REG, addr, 1);
            }
        }
        public UInt16 GetUInt16(int addr)
        {
            return D_regs[addr];
        }

        public void SetInt16(int addr, Int16 value)
        {
            if (GetInt16(addr) != value)
            {
                D_regs[addr] = (UInt16)value;
                NotifyRegChanged(RegChangedEventArgs.REG_TYPE.REG, addr, 1);
            }
        }
        public Int16 GetInt16(int addr)
        {
            return (Int16)D_regs[addr];
        }
        #endregion
        #region bool
        public void SetBool(int addr, bool value)
        {
            if (GetBool(addr) != value)
            {
                M_coils[addr] = value;
                NotifyRegChanged(RegChangedEventArgs.REG_TYPE.COIL, addr, 1);
            }
        }
        public bool GetBool(int addr)
        {
            return M_coils[addr];
        }
        #endregion
        #region get
        public void GetValue(int addr, bool[] value)
        {
            for (int i = 0; i < value.Length; i++)
            {
                int a = i + addr;
                if (a >= 0 && a < M_coils.Length)
                    value[i] = M_coils[i + addr];
            }
        }
        public void GetValue(int addr, UInt16[] value)
        {
            for (int i = 0; i < value.Length; i++)
            {
                int a = i + addr;
                if (a >= 0 && a < D_regs.Length)
                    value[i] = D_regs[i + addr];
            }
        }
        #endregion

        #region set
        public void SetValue(int addr, bool[] value)
        {
            for (int i = 0; i < value.Length; i++)
            {
                int a = i + addr;
                if (a >= 0 && a < M_coils.Length)
                    M_coils[i + addr] = value[i];
            }
            NotifyRegChanged(RegChangedEventArgs.REG_TYPE.COIL, addr, value.Length);
        }
        public void SetValue(int addr, UInt16[] value)
        {
            for (int i = 0; i < value.Length; i++)
            {
                int a = i + addr;
                if (a >= 0 && a < D_regs.Length)
                    D_regs[i + addr] = value[i];
            }
            NotifyRegChanged(RegChangedEventArgs.REG_TYPE.REG, addr, value.Length);
        }
        #endregion

        public int RegsCount { get { return D_regs.Length; } }
        public int CoilsCount { get { return M_coils.Length; } }
    }
}
