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

namespace FLY.ModbusModule
{
    
    public class ClientTCP:IModbusClient, INotifyPropertyChanged
    {
        TCPManager.TCPCConn mTCPCConn;
        public event Action<Pack_Proto> ErrorEvent;
        public bool IsConnected 
        {
            get {
                return mTCPCConn.IsConnected;
            }
        }
        public ClientTCP(IPAddress ip)
        {
            mTCPCConn = new TCPManager.TCPCConn(new IPEndPoint(ip, 502), ParsePacketInClient);

            mTCPCConn.Enable = true;

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

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

        /// <summary>
        /// 从异端接收的数据，解释数据
        /// </summary>
        /// <param name="packet"></param>
        /// <param name="conn"></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, 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);
                    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>
        public void Do_01(UInt16 addr, UInt16 cnt, Action<bool[],object> AsyncDelegate, object AsyncState)
        {
            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;
                        }
                    }
                }
                ((Action<bool[], object>)t.AsyncDelegate)(blist.ToArray(), t.AsyncState);
            });
            Transactions.Add(t);

            mTCPCConn.SendPacket(
                new Pack_Proto()
                {
                    tranid = t.tranid,
                    unitid = 0,
                    func = 0x01,
                    buf = data.ToArray()
                }.ToBytes());
        }
        /// <summary>
        /// 读多个REG
        /// </summary>
        /// <param name="addr"></param>
        public void Do_03(UInt16 addr, UInt16 cnt, Action<UInt16[], object> AsyncDelegate, object AsyncState) 
        {
            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;
                }
                ((Action<UInt16[], object>)t.AsyncDelegate)(blist.ToArray(), t.AsyncState);
            });
            Transactions.Add(t);

            mTCPCConn.SendPacket(
                new Pack_Proto()
                {
                    tranid = t.tranid,
                    unitid = 0,
                    func = 0x03,
                    buf = data.ToArray()
                }.ToBytes());
        }
        /// <summary>
        /// 写多个coil
        /// </summary>
        /// <param name="addr"></param>
        public void Do_0F(UInt16 addr, bool[] datas) 
        {
            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);



            mTCPCConn.SendPacket(
                new Pack_Proto()
                {
                    tranid = GetFreeTranID(),
                    unitid = 0,
                    func = 0x0F,
                    buf = data.ToArray()
                }.ToBytes());
        }
        /// <summary>
        /// 写多个REG
        /// </summary>
        /// <param name="addr"></param>
        public void Do_10(UInt16 addr, UInt16[] datas) 
        {
            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());
            }
            mTCPCConn.SendPacket(
                new Pack_Proto()
                {
                    tranid = GetFreeTranID(),
                    unitid = 0,
                    func = 0x10,
                    buf = data.ToArray()
                }.ToBytes());
        }
        protected void NotifyPropertyChanged(string propertyname)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyname));
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;
    }
}
