using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using NModbus;
using HBLConsole.Service;
using System.Threading;
using HBLConsole.Model;


namespace HBLConsole.Communication
{
    public class ModbusTcpHelper
    {

        private volatile static ModbusTcpHelper _Instance;
        public static ModbusTcpHelper GetInstance => _Instance ?? (_Instance = new ModbusTcpHelper());
        private ModbusTcpHelper() { }

        private ModbusFactory modbusFactory;
        private IModbusMaster master;
        private TcpClient tcpClient;
        private string IPAdress;
        private int Port;

        //public Action ConnectOk { get; set; }

        /// <summary>
        /// 判断是否连接成功
        /// </summary>
        public bool Connected => tcpClient == null ? false : tcpClient.Connected;

        /// <summary>
        /// ModbusTcp 连接设备
        /// </summary>
        /// <param name="ip">ip 地址</param>
        /// <param name="port">端口号，默认502</param>
        public void ModbusTcpConnect(string ip, int port = 502)
        {
            MessageLog.GetInstance.Show($"设备【{ip}:{port}】连接中。。。。");
            IPAdress = ip;
            Port = port;
            modbusFactory = new ModbusFactory();
            Connect();
            master.Transport.ReadTimeout = 2000;//读取超时时间
            master.Transport.WriteTimeout = 2000;//写入超时时间
            master.Transport.Retries = 10;//重试次数
            //ActionOperate.GetInstance.Send("ConnectOk");
            //ActionOperate.GetInstance.Send($"{GVL.GeneralConfig.DeviceType.ToString()}/ConnectOk");
            MessageLog.GetInstance.Show($"设备【{ip}:{port}】连接成功");
        }


        private void Connect()
        {
            bool ErrorFlag = false;
            while (!Connected)
            {
                try
                {
                    tcpClient = new TcpClient(IPAdress, Port);
                    master = modbusFactory.CreateMaster(tcpClient);
                }
                catch (Exception ex)
                {
                    if (!ErrorFlag)
                    {
                        MessageLog.GetInstance.Show($"ModbusTcp 连接失败，IP = {IPAdress},Port = {Port}");
                        MessageLog.GetInstance.Show(ex.ToString());
                        ErrorFlag = true;
                    }
                    Thread.Sleep(3000);
                }
            }
            MessageLog.GetInstance.Show("ModbusTcp 连接成功！");
        }

        public void Readbool(ushort startAddress, ushort len, Action<bool[]> action)
        {
            object result;
            result = Read(startAddress, ReadType.Coils, len);
            if (result != null)
            {
                if (result is bool[] bools)
                {
                    if (bools.Length == len)
                    {
                        action(bools);
                    }
                }
            }
        }

        public object Read(ushort startAddress, ReadType readType, ushort num = 1, byte slaveAddress = 1)
        {
            object result = new object();
            if (tcpClient == null) return result;
            if (num <= 0) return result;
            try
            {
                switch (readType)
                {
                    case ReadType.Coils:
                        result = master.ReadCoils(slaveAddress, startAddress, num);
                        break;
                    case ReadType.Inputs:
                        result = master.ReadInputs(slaveAddress, startAddress, num);
                        break;
                    case ReadType.HoldingRegisters:
                        result = master.ReadHoldingRegisters(slaveAddress, startAddress, num);
                        break;
                    case ReadType.InputRegisters:
                        result = master.ReadInputRegisters(slaveAddress, startAddress, num);
                        break;
                    default:
                        break;
                }
                if (result == null) return new object();
                if (result is bool[] bools)
                {
                    if (bools.Length == 1)
                        return bools[0];
                    else
                        return bools;
                }
                if (result is ushort[] ushorts)
                {
                    if (ushorts.Length == 1)
                        return ushorts[0];
                    else
                        return ushorts;
                }
            }
            catch (Exception ex)
            {
                MessageLog.GetInstance.Show(ex.ToString());
                if (ex.InnerException is SocketException)
                {
                    tcpClient = null;
                    Connect();
                }
            }
            return result;
        }

        public bool Write(ushort startAddress, WriteType writeType, object InputValue, byte slaveAddress = 1)
        {
            bool result = false;
            if (tcpClient == null) return result;
            if (!(InputValue is bool || InputValue is bool[] || InputValue is ushort || InputValue is ushort[])) return result;
            try
            {
                switch (writeType)
                {
                    case WriteType.Coils:
                        if (InputValue is bool boolValue)
                            master.WriteSingleCoil(slaveAddress, startAddress, boolValue);

                        if (InputValue is bool[] boolsValue)
                            master.WriteMultipleCoils(slaveAddress, startAddress, boolsValue);
                        break;
                    case WriteType.HoldingRegisters:
                        if (InputValue is ushort ushortValue)
                            master.WriteSingleRegister(slaveAddress, startAddress, ushortValue);

                        if (InputValue is ushort[] ushortsValue)
                        {
                            int len = 100;
                            if (ushortsValue.Length > len)
                            {
                                List<ushort[]> ushortLists = new List<ushort[]>();
                                for (int i = 0; i < ushortsValue.Length / len; i++)
                                {
                                    ushortLists.Add(ushortsValue.Skip(0).Take(len).ToArray());
                                }
                                int y = ushortsValue.Length % len;

                                if (y > 0)
                                {
                                    ushortLists.Add(ushortsValue.Skip(ushortsValue.Length - y).Take(y).ToArray());
                                }
                                foreach (var item in ushortLists)
                                {
                                    master.WriteMultipleRegisters(slaveAddress, startAddress, item);
                                    startAddress += (ushort)item.Length;
                                }
                            }
                            else
                            {
                                master.WriteMultipleRegisters(slaveAddress, startAddress, ushortsValue);
                            }

                        }

                        break;
                    default:
                        break;
                }
            }
            catch (Exception ex)
            {
                MessageLog.GetInstance.Show(ex.ToString());
                if (ex.InnerException is SocketException)
                {
                    tcpClient = null;
                    Connect();
                }
                return false;
            }
            return true;
        }


        #region 批量数据读取
        /// <summary>
        /// 读取多个线圈
        /// </summary>
        /// <param name="startAddress"></param>
        /// <param name="num"></param>
        /// <param name="slaveAddress"></param>
        /// <returns></returns>
        public bool[] ReadCoils(ushort startAddress, ushort num, byte slaveAddress = 1)
        {
            return master.ReadCoils(slaveAddress, startAddress, num);
        }

        /// <summary>
        /// 读取多个输入线圈
        /// </summary>
        /// <param name="startAddress"></param>
        /// <param name="num"></param>
        /// <param name="slaveAddress"></param>
        /// <returns></returns>
        public bool[] ReadInputs(ushort startAddress, ushort num, byte slaveAddress = 1)
        {
            return master.ReadInputs(slaveAddress, startAddress, num);
        }

        /// <summary>
        /// 读取多个保持寄存器
        /// </summary>
        /// <param name="startAddress"></param>
        /// <param name="num"></param>
        /// <param name="slaveAddress"></param>
        /// <returns></returns>
        public ushort[] ReadHoldingRegisters(ushort startAddress, ushort num, byte slaveAddress = 1)
        {
            return master.ReadHoldingRegisters(slaveAddress, startAddress, num);
        }

        /// <summary>
        /// 读取多个输入寄存器
        /// </summary>
        /// <param name="startAddress"></param>
        /// <param name="num"></param>
        /// <param name="slaveAddress"></param>
        /// <returns></returns>
        public ushort[] ReadInputRegisters(ushort startAddress, ushort num, byte slaveAddress = 1)
        {
            return master.ReadInputRegisters(slaveAddress, startAddress, num);
        }
        #endregion

        #region 批量数据写入
        /// <summary>
        /// 写入多个线圈
        /// </summary>
        /// <param name="startAddress"></param>
        /// <param name="value"></param>
        /// <param name="slaveAddress"></param>
        public void WriteMultipleCoils(ushort startAddress, bool[] value, byte slaveAddress = 1)
        {
            master.WriteMultipleCoils(slaveAddress, startAddress, value);
        }

        /// <summary>
        /// 写入多个寄存器
        /// </summary>
        /// <param name="startAddress"></param>
        /// <param name="value"></param>
        /// <param name="slaveAddress"></param>
        public void WriteMultipleRegisters(ushort startAddress, ushort[] value, byte slaveAddress = 1)
        {
            master.WriteMultipleRegisters(slaveAddress, startAddress, value);
        }
        #endregion

        #region 单个数据读取
        /// <summary>
        /// 读取单个线圈
        /// </summary>
        /// <param name="startAddress"></param>
        /// <param name="slaveAddress"></param>
        /// <returns></returns>
        public bool ReadCoils(ushort startAddress, byte slaveAddress = 1)
        {
            var result = master.ReadCoils(slaveAddress, startAddress, 1);
            if (result == null) return false;
            if (result.Length == 1) return result[0];
            return false;
            //return master.ReadCoils(slaveAddress, startAddress, 1)[0];
        }

        /// <summary>
        /// 读取单个输入线圈
        /// </summary>
        /// <param name="startAddress"></param>
        /// <param name="slaveAddress"></param>
        /// <returns></returns>
        public bool ReadInputs(ushort startAddress, byte slaveAddress = 1)
        {
            var result = master.ReadInputs(slaveAddress, startAddress, 1);
            if (result == null) return false;
            if (result.Length == 1) return result[0];
            return false;
            //return master.ReadInputs(slaveAddress, startAddress, 1)[0];
        }

        /// <summary>
        /// 读取单个保持寄存器
        /// </summary>
        /// <param name="startAddress"></param>
        /// <param name="slaveAddress"></param>
        /// <returns></returns>
        public ushort ReadHoldingRegisters(ushort startAddress, byte slaveAddress = 1)
        {
            if (tcpClient == null) return 0;
            ushort[] result = null;
            try
            {
                result = master.ReadHoldingRegisters(slaveAddress, startAddress, 1);
            }
            catch (Exception ex)
            {
                MessageLog.GetInstance.Show(ex.ToString());
                tcpClient = null;
                Connect();
            }

            if (result == null) return 0;
            if (result.Length == 1) return result[0];
            return 0;
            //return master.ReadHoldingRegisters(slaveAddress, startAddress, 1)[0];
        }

        /// <summary>
        /// 读取单个输入寄存器
        /// </summary>
        /// <param name="startAddress"></param>
        /// <param name="slaveAddress"></param>
        /// <returns></returns>
        public ushort ReadInputRegisters(ushort startAddress, byte slaveAddress = 1)
        {
            var result = master.ReadInputRegisters(slaveAddress, startAddress, 1);
            if (result == null) return 0;
            if (result.Length == 1) return result[0];
            return 0;
            //return master.ReadInputRegisters(slaveAddress, startAddress, 1)[0];
        }
        #endregion

        #region 单个数据写入
        /// <summary>
        /// 写入单个线圈
        /// </summary>
        /// <param name="startAddress"></param>
        /// <param name="value"></param>
        /// <param name="slaveAddress"></param>
        public void WriteSingleCoil(ushort startAddress, bool value, byte slaveAddress = 1)
        {
            master.WriteSingleCoil(slaveAddress, startAddress, value);
        }

        /// <summary>
        /// 写入单个寄存器
        /// </summary>
        /// <param name="startAddress"></param>
        /// <param name="value"></param>
        /// <param name="slaveAddress"></param>
        public void WriteSingleRegister(ushort startAddress, ushort value, byte slaveAddress = 1)
        {
            master.WriteSingleRegister(slaveAddress, startAddress, value);
        }
        #endregion





    }


    public enum ReadType
    {
        /// <summary>
        /// 读线圈
        /// </summary>
        Coils,
        /// <summary>
        /// 读输入线圈
        /// </summary>
        Inputs,
        /// <summary>
        /// 读保持寄存器
        /// </summary>
        HoldingRegisters,
        /// <summary>
        /// 读输入寄存器
        /// </summary>
        InputRegisters,
    }

    public enum WriteType
    {
        /// <summary>
        /// 写线圈
        /// </summary>
        Coils,
        /// <summary>
        /// 写保持寄存器
        /// </summary>
        HoldingRegisters,
    }

}
