using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.ComponentModel;
using Misc;

namespace FLY.Modbus
{
    
    public class ClientTCP:IModbusClient, INotifyPropertyChanged
    {
        private int maxConCurrTrans = 1;
        /// <summary>
        /// 最大同时处理交易数
        /// </summary>
        public int MaxConCurrTrans
        {
            set { maxConCurrTrans = value; }
            get {
                return maxConCurrTrans;
            }
        }
        /// <summary>
        /// 当前交易数
        /// </summary>
        public int ConCurrTrans
        {
            get { return Transactions.Count(); }
        }

        TCPManager.TCPCConn mTCPCConn;
        /// <summary>
        /// 错误事件
        /// </summary>
        public event Action<Pack_Proto> ErrorEvent;
        /// <summary>
        /// 连接成功?
        /// </summary>
        public bool IsConnected 
        {
            get {
                return mTCPCConn.IsConnected;
            }
        }
        /// <summary>
        /// 标识,modbus tcp的参数
        /// </summary>
        public byte UnitID = 1;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="ip"></param>
        public ClientTCP(IPAddress ip):this(new IPEndPoint(ip,502))
        {

        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="ep"></param>
        public ClientTCP(IPEndPoint ep):this(ep,1)
        {
        
        }
        public override string ToString()
        {
            return mTCPCConn.RemoteEP.ToString();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="ep"></param>
        /// <param name="unitid"></param>
        public ClientTCP(IPEndPoint ep,byte unitid)
        {
            mTCPCConn = new TCPManager.TCPCConn(ep, ParsePacketInClient);

            mTCPCConn.Enable = true;

            mTCPCConn.ConnectEvent += new Action<TCPManager.TCPConn>(mTCPCConn_ConnectEvent);
            MaxConCurrTrans = 1;
            UnitID = unitid;
        }

        void mTCPCConn_ConnectEvent(TCPManager.TCPConn obj)
        {
            //清空全部tranid
            Transactions.Clear();
            NotifyPropertyChanged("IsConnected");
            
        }


        /// <summary>
        /// 从异端接收的数据,解释数据
        /// </summary>
        /// <param name="conn"></param>
        /// <param name="inBuf"></param>
        /// <returns></returns>
        int ParsePacketInClient(TCPManager.TCPConn conn, byte[] inBuf)
        {
            //格式——————————————                
            //MBAP
            //域                         长度          描述
            //事务标识符         2byte           MODBUS请求/响应的识别码    
            //协议标识符         2byte           0=MODBUS协议
            //——长度—         2byte           以下字节的数量
            //单元标识符         1byte           串行链路或其它总线过来的识别码
            

            //PDU
            //功能码——         1byte
            //数据                    nbyte
            int startIndex=0;
            do
            {
                Pack_Proto p = new Pack_Proto();
                int rlen;
                if (!p.TryParse(inBuf, startIndex, inBuf.Length, out rlen))
                {
                    //处理失败
                    return startIndex;
                }

                ParsePacket(conn, p);

                startIndex += rlen;

            } while (startIndex < inBuf.Length);
            return startIndex;
        }
        void ParsePacket(TCPManager.TCPConn conn, Pack_Proto p) 
        {
            if (Misc.MyBase.CHECKBIT(p.func, 7)) 
            {
                //出错!!!
                if (ErrorEvent != null)
                    ErrorEvent(p);

                //根据tranid 找回 Transaction
                Transactions.RemoveAll(_t => _t.tranid == p.tranid);
            }

            //if (p.func == 0x01 || p.func == 0x03)
            {
                //根据tranid 找回 Transaction
                var tlist = from _t in Transactions where _t.tranid == p.tranid select _t;
                if (tlist.Count() > 0)
                {
                    Transaction t = tlist.First();
                    t.ParsePacket(p);
                    //if( (p.func == 0x05) || (p.func==0x01))
                    //    Misc.Log.LogMessage("ReadCoilsAction", 0, "Receive Transid = {0} func= {1}",t.tranid, p.func);
                    Transactions.RemoveAll(_t => _t.tranid == p.tranid);
                }
            }
        }


        class Transaction
        {
            public UInt16 tranid;
            public object AsyncDelegate;
            public object AsyncState;
            public Action<Pack_Proto> ParsePacket;
        }

        List<Transaction> Transactions = new List<Transaction>();
        UInt16 tranid = 12;
        UInt16 GetFreeTranID() 
        {
            tranid++;
            return tranid;
        }

        /// <summary>
        /// 读多个 COIL
        /// </summary>
        /// <param name="addr"></param>
        /// <param name="cnt"></param>
        /// <param name="AsyncDelegate"></param>
        /// <param name="AsyncState"></param>
        /// <returns></returns>
        public ModbusClient_Errno Do_01(UInt16 addr, UInt16 cnt, Action<bool[],object> AsyncDelegate, object AsyncState)
        {
            if (Transactions.Count() >= MaxConCurrTrans) 
                return ModbusClient_Errno.TOO_MUCH_TRANS;
            List<byte> data = new List<byte>();
            data.AddRange(addr.GetBytes_Big_endian());
            data.AddRange(cnt.GetBytes_Big_endian());

            Transaction t = new Transaction();
            t.tranid = GetFreeTranID();
            t.AsyncDelegate = AsyncDelegate;
            t.AsyncState = AsyncState;

            t.ParsePacket = new Action<Pack_Proto>(delegate(Pack_Proto p)
            {
                if (p.func != 0x01)
                { 
                    //有问题!!!触发异常事件
                    return;
                }
                List<bool> blist = new List<bool>();
                int byte_len = p.buf[0];
                int index = 1;
                for (int i = 0; i < byte_len; i++) 
                {
                    for (int j = 0; j < 8; j++) 
                    {
                        if (Misc.MyBase.CHECKBIT(p.buf[index], j))
                            blist.Add(true);
                        else
                            blist.Add(false);
                        if (blist.Count() >= cnt)
                        {
                            //完成
                            i = byte_len;
                            break;
                        }
                    }
                    index++;
                }
                Transactions.Remove(t);
                //Misc.Log.LogMessage("ReadCoilsAction", 0, "Receive Coils Transid = "+p.tranid.ToString());
                ((Action<bool[], object>)t.AsyncDelegate)(blist.ToArray(), t.AsyncState);
            });
            Transactions.Add(t);

            //Misc.Log.LogMessage("ReadCoilsAction", 0, "Send Coils Transid = " + t.tranid.ToString());
            mTCPCConn.SendPacket(
                new Pack_Proto()
                {
                    tranid = t.tranid,
                    unitid = UnitID,
                    func = 0x01,
                    buf = data.ToArray()
                }.ToBytes());
            return ModbusClient_Errno.OK;
        }

        /// <summary>
        /// 读多个REG
        /// </summary>
        /// <param name="addr"></param>
        /// <param name="cnt"></param>
        /// <param name="AsyncDelegate"></param>
        /// <param name="AsyncState"></param>
        /// <returns></returns>
        public ModbusClient_Errno Do_03(UInt16 addr, UInt16 cnt, Action<UInt16[], object> AsyncDelegate, object AsyncState) 
        {
            if (Transactions.Count() >= MaxConCurrTrans) return ModbusClient_Errno.TOO_MUCH_TRANS;
            List<byte> data = new List<byte>();
            data.AddRange(addr.GetBytes_Big_endian());
            data.AddRange(cnt.GetBytes_Big_endian());

            Transaction t = new Transaction();
            t.tranid = GetFreeTranID();
            t.AsyncDelegate = AsyncDelegate;
            t.AsyncState = AsyncState;

            t.ParsePacket = new Action<Pack_Proto>(delegate(Pack_Proto p)
            {
                if (p.func != 0x03)
                {
                    //有问题!!!触发异常事件
                    return;
                }
                List<UInt16> blist = new List<UInt16>();
                int index = 1;

                while(index<p.buf.Count())
                {
                    blist.Add(COMMON.ToUInt16_Big_endian(p.buf, index));
                    index += 2;
                }
                Transactions.Remove(t);
                ((Action<UInt16[], object>)t.AsyncDelegate)(blist.ToArray(), t.AsyncState);
            });
            Transactions.Add(t);

            mTCPCConn.SendPacket(
                new Pack_Proto()
                {
                    tranid = t.tranid,
                    unitid = UnitID,
                    func = 0x03,
                    buf = data.ToArray()
                }.ToBytes());
            return ModbusClient_Errno.OK;
        }


        /// <summary>
        /// Write Single Coil
        /// </summary>
        /// <param name="addr"></param>
        /// <param name="dat"></param>
        /// <returns></returns>
        public ModbusClient_Errno Do_05(UInt16 addr, bool dat)
        {
            if (Transactions.Count() >= MaxConCurrTrans) 
                return ModbusClient_Errno.TOO_MUCH_TRANS;
            List<byte> data = new List<byte>();
            data.AddRange(addr.GetBytes_Big_endian());
            if (dat)
                data.Add(0xff);
            else
                data.Add(0);
            data.Add(0);

            Transaction t = new Transaction();
            t.tranid = GetFreeTranID();
            t.AsyncDelegate = null;
            t.AsyncState = null;

            t.ParsePacket = new Action<Pack_Proto>(delegate(Pack_Proto p)
            {
                if (p.func != 0x05)
                {
                    //有问题!!!触发异常事件
                    return;
                }
                Transactions.Remove(t);
            });
            Transactions.Add(t);

            mTCPCConn.SendPacket(
                new Pack_Proto()
                {
                    tranid = t.tranid,
                    unitid = UnitID,
                    func = 0x05,
                    buf = data.ToArray()
                }.ToBytes());
            return ModbusClient_Errno.OK;
        }


        /// <summary>
        /// Write Single Coil,带返回
        /// </summary>
        /// <param name="addr"></param>
        /// <param name="dat"></param>
        /// <param name="AsyncDelegate"></param>
        /// <param name="AsyncState"></param>
        /// <returns></returns>
        public ModbusClient_Errno Do_05(UInt16 addr, bool dat, Action<object> AsyncDelegate, object AsyncState)
        {
            if (Transactions.Count() >= MaxConCurrTrans) return ModbusClient_Errno.TOO_MUCH_TRANS;
            List<byte> data = new List<byte>();
            data.AddRange(addr.GetBytes_Big_endian());
            if (dat)
                data.Add(0xff);
            else
                data.Add(0);
            data.Add(0);

            Transaction t = new Transaction();
            t.tranid = GetFreeTranID();
            t.AsyncDelegate = AsyncDelegate;
            t.AsyncState = AsyncState;

            t.ParsePacket = new Action<Pack_Proto>(delegate(Pack_Proto p)
            {
                if (p.func != 0x05)
                {
                    //有问题!!!触发异常事件
                    return;
                }
                Transactions.Remove(t);
                ((Action<object>)t.AsyncDelegate)(t.AsyncState);
            });
            Transactions.Add(t);

            mTCPCConn.SendPacket(
                new Pack_Proto()
                {
                    tranid = t.tranid,
                    unitid = UnitID,
                    func = 0x05,
                    buf = data.ToArray()
                }.ToBytes());
            return ModbusClient_Errno.OK;
        }


        /// <summary>
        /// 写多个coil
        /// </summary>
        /// <param name="addr"></param>
        /// <param name="datas"></param>
        /// <returns></returns>
        public ModbusClient_Errno Do_0F(UInt16 addr, bool[] datas) 
        {
            if (Transactions.Count() >= MaxConCurrTrans) return ModbusClient_Errno.TOO_MUCH_TRANS;
            List<byte> data = new List<byte>();
            data.AddRange(addr.GetBytes_Big_endian());
            data.AddRange(((UInt16)datas.Count()).GetBytes_Big_endian());
            data.Add((byte)(Math.Ceiling(datas.Count() / 8.0)));

            byte b = 0;
            int j=0;
            for (int i = 0; i < datas.Count(); i++)
            {
                if (datas[i])
                    Misc.MyBase.SIGNBIT(ref b, j);
                j++;
                if (j == 8)
                {
                    data.Add(b);
                    b = 0;
                    j = 0;
                }
            }
            if (j != 0)
                data.Add(b);

            Transaction t = new Transaction();
            t.tranid = GetFreeTranID();
            t.AsyncDelegate = null;
            t.AsyncState = null;

            t.ParsePacket = new Action<Pack_Proto>(delegate(Pack_Proto p)
            {
                if (p.func != 0x10)
                {
                    //有问题!!!触发异常事件
                    return;
                }
                Transactions.Remove(t);
            });
            Transactions.Add(t);

            mTCPCConn.SendPacket(
                new Pack_Proto()
                {
                    tranid = t.tranid,
                    unitid = UnitID,
                    func = 0x0F,
                    buf = data.ToArray()
                }.ToBytes());
            return ModbusClient_Errno.OK;
        }


        /// <summary>
        /// 写多个coil,带返回
        /// </summary>
        /// <param name="addr"></param>
        /// <param name="datas"></param>
        /// <param name="AsyncDelegate"></param>
        /// <param name="AsyncState"></param>
        /// <returns></returns>
        public ModbusClient_Errno Do_0F(UInt16 addr, bool[] datas, Action<object> AsyncDelegate, object AsyncState)
        {
            if (Transactions.Count() >= MaxConCurrTrans) return ModbusClient_Errno.TOO_MUCH_TRANS;
            List<byte> data = new List<byte>();
            data.AddRange(addr.GetBytes_Big_endian());
            data.AddRange(((UInt16)datas.Count()).GetBytes_Big_endian());
            data.Add((byte)(Math.Ceiling(datas.Count() / 8.0)));

            byte b = 0;
            int j = 0;
            for (int i = 0; i < datas.Count(); i++)
            {
                if (datas[i])
                    Misc.MyBase.SIGNBIT(ref b, j);
                j++;
                if (j == 8)
                {
                    data.Add(b);
                    b = 0;
                    j = 0;
                }
            }
            if (j != 0)
                data.Add(b);

            Transaction t = new Transaction();
            t.tranid = GetFreeTranID();
            t.AsyncDelegate = AsyncDelegate;
            t.AsyncState = AsyncState;

            t.ParsePacket = new Action<Pack_Proto>(delegate(Pack_Proto p)
            {
                if (p.func != 0x0f)
                {
                    //有问题!!!触发异常事件
                    return;
                }
                Transactions.Remove(t);
                ((Action<object>)t.AsyncDelegate)(t.AsyncState);
            });
            Transactions.Add(t);

            mTCPCConn.SendPacket(
                new Pack_Proto()
                {
                    tranid = t.tranid,
                    unitid = UnitID,
                    func = 0x0F,
                    buf = data.ToArray()
                }.ToBytes());
            return ModbusClient_Errno.OK;
        }

        /// <summary>
        /// 写多个REG
        /// </summary>
        /// <param name="addr"></param>
        /// <param name="datas"></param>
        /// <returns></returns>
        public ModbusClient_Errno Do_10(UInt16 addr, UInt16[] datas) 
        {
            if (Transactions.Count() >= MaxConCurrTrans) return ModbusClient_Errno.TOO_MUCH_TRANS;
            List<byte> data = new List<byte>();
            data.AddRange(addr.GetBytes_Big_endian());
            data.AddRange(((UInt16)datas.Count()).GetBytes_Big_endian());
            data.Add((byte)(datas.Count() * 2));
            for (int i = 0; i < datas.Count(); i++)
            {
                data.AddRange(datas[i].GetBytes_Big_endian());
            }

            Transaction t = new Transaction();
            t.tranid = GetFreeTranID();
            t.AsyncDelegate = null;
            t.AsyncState = null;

            t.ParsePacket = new Action<Pack_Proto>(delegate(Pack_Proto p)
            {
                if (p.func != 0x10)
                {
                    //有问题!!!触发异常事件
                    return;
                }
                Transactions.Remove(t);
            });
            Transactions.Add(t);

            mTCPCConn.SendPacket(
                new Pack_Proto()
                {
                    tranid = t.tranid,
                    unitid = UnitID,
                    func = 0x10,
                    buf = data.ToArray()
                }.ToBytes());
            return ModbusClient_Errno.OK;
        }


        /// <summary>
        /// 写多个REG,带返回
        /// </summary>
        /// <param name="addr"></param>
        /// <param name="datas"></param>
        /// <param name="AsyncDelegate"></param>
        /// <param name="AsyncState"></param>
        /// <returns></returns>
        public ModbusClient_Errno Do_10(UInt16 addr, UInt16[] datas, Action<object> AsyncDelegate, object AsyncState)
        {
            if (Transactions.Count() >= MaxConCurrTrans) return ModbusClient_Errno.TOO_MUCH_TRANS;
            List<byte> data = new List<byte>();
            data.AddRange(addr.GetBytes_Big_endian());
            data.AddRange(((UInt16)datas.Count()).GetBytes_Big_endian());
            data.Add((byte)(datas.Count() * 2));
            for (int i = 0; i < datas.Count(); i++)
            {
                data.AddRange(datas[i].GetBytes_Big_endian());
            }

            Transaction t = new Transaction();
            t.tranid = GetFreeTranID();
            t.AsyncDelegate = AsyncDelegate;
            t.AsyncState = AsyncState;

            t.ParsePacket = new Action<Pack_Proto>(delegate(Pack_Proto p)
            {
                if (p.func != 0x10)
                {
                    //有问题!!!触发异常事件
                    return;
                }
                Transactions.Remove(t);
                ((Action<object>)t.AsyncDelegate)( t.AsyncState);
            });
            Transactions.Add(t);
            
            mTCPCConn.SendPacket(
                new Pack_Proto()
                {
                    tranid = t.tranid,
                    unitid = UnitID,
                    func = 0x10,
                    buf = data.ToArray()
                }.ToBytes());
            return ModbusClient_Errno.OK;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="propertyname"></param>
        protected void NotifyPropertyChanged(string propertyname)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyname));
            }
        }
        /// <summary>
        /// 
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;
    }
}