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

namespace FObjBase
{
    public enum SENSE_CONFIG
    {
        ADD, REMOVE
    }
    /// <summary>
    /// 建议使用方法：
    /// 做为客户端，都使用 FObjSys.Current 连接不同的服务器
    /// 做为服务器，当要提供2个Obj服务, 应该要使用 Currents[index]
    /// 这套逻辑已经潜入到 每个 FObj 中， FObj.CurrObjSys, 就是对应的 Obj服务
    /// </summary>
    public class FObjSys : IFObjSysAsClient, IFObjSysAsServer
    {
        /// <summary>
        /// nlog调试信息
        /// </summary>
        static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
        /// <summary>
        /// 全局 当前正在操作的 fobj系统
        /// </summary>
        public static FObjSys Current;
        /// <summary>
        /// fobj系统数组
        /// </summary>
        public static List<FObjSys> Currents = new List<FObjSys>();


        /// <summary>
        /// 作为服务器  监听客户端到本地的连接请求
        /// </summary>
        TCPListen m_tcplisten;

        /// <summary>
        /// 作为客户端  管理连接到异地服务器的连接
        /// </summary>
        Dictionary<TCPCConn, List<UInt32>> m_cconns = new Dictionary<TCPCConn, List<UInt32>>();

        /// <summary>
        /// 作为服务器，当异地客户端断开，发送通知
        /// </summary>
        List<UInt32> m_serverConnectedNotifyObjID = new List<UInt32>();

        /// <summary>
        /// 交易列表
        /// </summary>
        List<Transaction> Transactions = new List<Transaction>();

        /// <summary>
        /// 消息推送的敏感对象
        /// </summary>
        class Sense
        {
            public IFConn conn;
            public UInt32 objid;
            public UInt32 sense_mask;
        }
        /// <summary>
        /// 加入到 fobj系统的 obj
        /// </summary>
        Dictionary<IFObj, List<Sense>> Objs = new Dictionary<IFObj, List<Sense>>();

        static FObjSys()
        {
            Currents.Add(new FObjBase.FObjSys());
            Current = Currents[0];
        }
        public FObjSys() {


        }
        #region IFObjSys
        /// <summary>
        /// 添加OBJ到系统
        /// </summary>
        /// <param name="obj"></param>
        public void ObjAdd(IFObj obj)
        {
            if (obj.ID == 0)
                obj.ID = GetFreeObjID();

            Objs.Add(obj, new List<Sense>());
        }
        /// <summary>
        /// 对外发送消息
        /// </summary>
        /// <param name="s1">远端对应的连接代理 </param>
        /// <param name="destid">远端的ID</param>
        /// <param name="srcid">本地的ID</param>
        /// <param name="magic">交易码</param>
        /// <param name="info_no">信息码</param>
        /// <param name="info_data">信息内容</param>
        public void SendMessageEx(IFConn s1, UInt32 destid, UInt32 srcid, UInt32 magic, UInt16 info_no, byte[] info_data)
        {
            if (s1 == null)
                return;
            if (s1 is FConnLocal)
            {
                return;
            }
            else
            {
                var p = new Pack_Proto()
                {
                    destid = destid,
                    srcid = srcid,
                    magic = magic,
                    info = info_no,
                    buf = info_data
                };

                ((TCPConn)s1).SendPacket(p.ToBytes());

                if (!(s1 is TCPCConn))
                {
                    if (logger.IsDebugEnabled)
                    {
                        logger.Debug($"(S) SendMessageEx{Environment.NewLine}{Pack_ProtoToString(p)}");
                    }
                }
                return;
            }
        }
        static string BytesToHexString(byte[] buf)
        {
            int row = 0;
            StringBuilder sb = new StringBuilder();
            StringBuilder sb_hex = new StringBuilder();
            StringBuilder sb_str = new StringBuilder();

            sb.Append($"        | 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F |                ");
            sb.AppendLine();
            sb.Append($"--------| -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- |----------------");
            sb.AppendLine();
            for (int i = 0; i < buf.Count(); i++)
            {
                sb_hex.Append($"{buf[i]:X2} ");
                char c = (char)buf[i];
                if (c > 0x20 && c < 0x7e)
                {
                    sb_str.Append(c);
                }
                else
                {
                    sb_str.Append(" ");
                }
                if ((i + 1) % 16 == 0)
                {
                    sb.Append($"{row * 16:X08}| {sb_hex}|{sb_str}");
                    row++;
                    sb.AppendLine();
                    sb_hex.Clear();
                    sb_str.Clear();
                }
            }
            int remaining = buf.Count() % 16;
            if (remaining > 0)
            {
                for (int i = 0; i < (16 - remaining); i++)
                {
                    sb_hex.Append("   ");
                    sb_str.Append(" ");
                }
                sb.Append($"{row * 16:X08}| {sb_hex}|{sb_str}");
                row++;
                sb.AppendLine();
                sb_hex.Clear();
                sb_str.Clear();
            }
            return sb.ToString();
        }
        static string Pack_ProtoToString(Pack_Proto p)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine($"destid=0x{p.destid:x8}, srcid=0x{p.srcid:x8}, magic={p.magic}, info=0x{p.info:x4}, buf=");
            sb.Append(BytesToHexString(p.buf));
            return sb.ToString();
        }
        #region 服务端

        /// <summary>
        /// 启动本地OBJSys网络
        /// </summary>
        /// <param name="ep"></param>
        /// <param name="objids"></param>
        public void Start_Conn_Server(IPEndPoint ep, params UInt32[] objids)
        {
            m_serverConnectedNotifyObjID.AddRange(objids);
            if (m_tcplisten == null)
            {
                m_tcplisten = new TCPListen(ep)
                {
                    ParsePacket = new ParsePacketHandler(ParsePacketInServer),
                    ConnectAction = new ConnectHandler(SConnConnectAction),
                };
            }
            else
            {
                m_tcplisten.Enable = false;
                m_tcplisten.LocalEP = ep;
            }
            m_tcplisten.Enable = true;
        }

        /// <summary>
        /// 指定推送到某个远端
        /// </summary>
        /// <param name="srcobj"></param>
        /// <param name="infoid"></param>
        /// <param name="infodata"></param>
        /// <param name="s1"></param>
        /// <param name="destid"></param>
        public void PushObjInfoEx(IFObj srcobj, UInt16 infoid, byte[] infodata, IFConn s1, UInt32 destid)
        {
            if (s1 is FConnLocal)
            {
                IFObj obj = Find(destid);
                if (obj != null)
                    obj.PushInfo(s1, srcobj.ID, infoid, infodata);
                return;
            }
            PushAny(s1, destid, srcobj.ID, GetFreeMagic(), Proto.INFO_PUSH_EVENT, infoid, infodata);
        }
        /// <summary>
        /// 推送到远端,系统判定根据敏感对象推送
        /// </summary>
        /// <param name="srcobj"></param>
        /// <param name="infoid"></param>
        /// <param name="infodata"></param>
        public void PushObjInfoEx(IFObj srcobj, UInt16 infoid, byte[] infodata)
        {
            if (!Objs.Keys.Contains(srcobj))
                return;
            List<Sense> senses = Objs[srcobj];

            //foreach (Sense sense in senses)
            //    sense.hasPush = false;

            int cnt = senses.Count();

            for (int i = 0; i < cnt; i++)
            {
                Sense sense = senses[i];

                if ((sense.sense_mask & (1 << infoid)) == 0)
                    continue;
                if (sense.conn is FConnLocal)
                {
                    IFObj obj = Find(sense.objid);
                    if (obj != null)
                        obj.PushInfo(sense.conn, srcobj.ID, infoid, infodata);
                    continue;
                }

                PushAny(sense.conn, sense.objid, srcobj.ID, GetFreeMagic(), Proto.INFO_PUSH_EVENT, infoid, infodata);
            }
        }
        /// <summary>
        /// 推送到远端,系统判定根据敏感对象推送,但不推送给特定某对象
        /// </summary>
        /// <param name="srcobj"></param>
        /// <param name="infoid"></param>
        /// <param name="infodata"></param>
        /// <param name="except_s1"></param>
        /// <param name="except_destid"></param>
        public void PushObjInfoEx_except(IFObj srcobj, UInt16 infoid, byte[] infodata, IFConn except_s1, UInt32 except_destid)
        {

            if (!Objs.Keys.Contains(srcobj))
                return;
            List<Sense> senses = Objs[srcobj];

            //foreach (Sense sense in senses)
            //    sense.hasPush = false;

            int cnt = senses.Count();

            for (int i = 0; i < cnt; i++)
            {
                Sense sense = senses[i];

                if ((sense.sense_mask & (1 << infoid)) == 0)
                    continue;
                if ((sense.conn == except_s1) && (sense.objid == except_destid))
                    continue;
                if (sense.conn is FConnLocal)
                {
                    IFObj obj = Find(sense.objid);
                    if (obj != null)
                        obj.PushInfo(sense.conn, srcobj.ID, infoid, infodata);
                    continue;
                }

                PushAny(sense.conn, sense.objid, srcobj.ID, GetFreeMagic(), Proto.INFO_PUSH_EVENT, infoid, infodata);
            }
        }

        /// <summary>
        /// 回复 客户端的CallFunction
        /// </summary>
        /// <param name="s1">客户端对应的连接代理 </param>
        /// <param name="destid">客户端的对象ID</param>
        /// <param name="srcid">本地服务端的对象ID</param>
        /// <param name="magic">交易码</param>
        /// <param name="funcid">函数ID</param>
        /// <param name="retdata">函数返回</param>
        public void PushCallFunctionEx(IFConn s1, UInt32 destid, UInt32 srcid, UInt32 magic, UInt16 funcid, byte[] retdata)
        {
            if (s1 == null)
                return;

            if (s1 is FConnLocal)
            {
                var ts = from t in Transactions where t.Magic == magic && t.Conn == s1 select t;
                if (ts.Count() > 0)
                {
                    Transaction tran = ts.First();
                    Transactions.Remove(tran);
                    PushCallFunction(s1, srcid, destid, magic, funcid, retdata, tran.AsyncDelegate, tran.AsyncContext);
                }
                else
                {
                    PushCallFunction(s1, srcid, destid, magic, funcid, retdata, null, null);
                }
                return;
            }

            PushAny(s1, destid, srcid, magic, Proto.INFO_PUSH_CALL_FUNCTION, funcid, retdata);
        }
        void PushAny(IFConn s1, UInt32 destid, UInt32 srcid, UInt32 magic, UInt16 infoid, UInt16 funcid, byte[] retdata)
        {
            PushAny(s1, destid, srcid, magic, infoid, funcid, retdata, null, null);
        }
        void PushAny(IFConn s1, UInt32 destid, UInt32 srcid, UInt32 magic, UInt16 infoid, UInt16 funcid, byte[] retdata, AsyncCBHandler asyncDelegate, object asyncContext)
        {
            if ((retdata != null) && (retdata.Length > Transaction.PackSize)) //参数太大，需要分包多次发送
            {
                //安全检测
                if (Transactions.Any(tran => tran.Magic == magic && tran.Conn == s1))
                {
                    //不应该有
                    throw new Exception($"推送大包出错，在 交易队列已经 magic={magic}");
                }

                Transaction t = new Transaction()
                {
                    Conn = s1,
                    SrcObjID = destid,
                    Magic = magic,
                    Size = retdata.Length,
                    Buf = (byte[])retdata.Clone(),
                    InfoId = infoid,
                    FuncID = funcid,
                    AsyncDelegate = asyncDelegate,
                    AsyncContext = asyncContext
                };
                Transactions.Add(t);
                SendBigSize(s1, destid, srcid, magic);
            }
            else
            {
                if (asyncDelegate != null)
                {
                    Transaction t = new Transaction()
                    {
                        Conn = s1,
                        SrcObjID = destid,
                        Magic = magic,
                        InfoId = infoid,
                        FuncID = funcid,
                        AsyncDelegate = asyncDelegate,
                        AsyncContext = asyncContext
                    };
                    Transactions.Add(t);
                }

                Pack_GetSetPushCall p = new Pack_GetSetPushCall
                {
                    infoid = funcid,
                    infodata = retdata
                };
                SendMessageEx(s1, destid, srcid, magic, infoid, p.ToBytes());
            }
        }


        #endregion
        #region 客户端

        /// <summary>
        /// 获取没用使用的OBJID
        /// </summary>
        /// <returns></returns>
        public UInt32 GetFreeObjID()
        {
            if (Objs.Count > 0)
                return Objs.Max(obj => obj.Key.ID) + 1;
            else
                return 1;
        }

        /// <summary>
        /// 从系统删除OBJ
        /// </summary>
        /// <param name="obj"></param>
        public void ObjRemove(IFObj obj)
        {
            Objs.Remove(obj);//作为服务端，关闭推送服务
            foreach (var kv in m_cconns)
            {
                kv.Value.Remove(obj.ID);
            }
        }
        /// <summary>
        /// 作为客户端，从系统删除OBJ;
        /// 还会通知服务器，断开与这个 OBJ 的所有连接
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="conn"></param>
        public void ObjRemove(IFObj obj, IFConn conn)
        {
            ObjRemove(obj);

            Transactions.RemoveAll(t => t.SrcObjID == obj.ID);

            if (conn != null)
            {
                SendMessageEx(conn, 0, obj.ID, GetFreeMagic(),
                    Proto.INFO_OBJ_DISPOSE, null);
            }
        }

        /// <summary>
        /// 连接到远端的OBJSys连接
        /// </summary>
        /// <param name="ep">远端地址</param>
        /// <param name="objids">连接成功后，通知这些obj</param>
        /// <returns></returns>
        public TCPCConn Connect_to_Another_OBJSys(IPEndPoint ep, params UInt32[] objids)
        {
            try
            {
                KeyValuePair<TCPCConn, List<UInt32>> cc_kv;
                cc_kv = m_cconns.First(_cc => _cc.Key.RemoteEP.Equals(ep));
                cc_kv.Value.AddRange(objids);
                //已经存在此连接,通知
                if (cc_kv.Key.IsConnected)
                {
                    foreach (UInt32 objid in objids)
                    {
                        IFObj obj = Find(objid);
                        if (obj != null)
                            obj.ConnectNotify(cc_kv.Key);
                    }
                }
                return cc_kv.Key;
            }
            catch
            {
                TCPCConn conn = new TCPCConn(ep)
                {
                    ParsePacket = new ParsePacketHandler(ParsePacketInClient),
                    ConnectAction = new ConnectHandler(CConnConnectAction),
                    Enable = true
                };

                m_cconns.Add(conn, new List<uint>(objids));
                return conn;
            }
        }
        /// <summary>
        /// 断开到远端OBJSys的连接
        /// </summary>
        /// <param name="ep">远端地址</param>
        /// <param name="objids">连接成功后，通知这些obj</param>
        /// <returns></returns>
        public void Disconnect_to_Another_OBJSys(IPEndPoint ep, params UInt32[] objids)
        {
            try
            {
                KeyValuePair<TCPCConn, List<UInt32>> cc_kv;
                cc_kv = m_cconns.First(_cc => _cc.Key.RemoteEP.Equals(ep));

                cc_kv.Value.RemoveAll(objid => objids.Contains(objid));
                //删除大通讯的trans
                Transactions.RemoveAll(t => objids.Contains(t.SrcObjID));



                if (cc_kv.Key.IsConnected)//它的连接是正常的，通知objids，说连接断开了
                {
                    foreach (UInt32 objid in objids)
                    {
                        IFObj obj = Find(objid);
                        if (obj != null)
                            obj.ConnectNotify(
                                new TCPCConn(ep)
                                {
                                    Enable = false
                                });
                    }
                }
                if (cc_kv.Value.Count == 0)//已经没有obj需要连接了，删除conn
                {
                    cc_kv.Key.Enable = false;
                    m_cconns.Remove(cc_kv.Key);
                }
            }
            catch
            {

            }
        }



        /// <summary>
        /// 注册本地obj 为远端obj的敏感对象，得到远端obj的推送消息
        /// </summary>
        /// <param name="s1">远端对应的连接代理 </param>
        /// <param name="objid">远端的ID</param>
        /// <param name="senobjid">本地obj</param>
        /// <param name="sense_mask">注册的敏感源</param>
        /// <param name="action">动作，添加?删除?</param>
        public void SenseConfigEx(IFConn s1, UInt32 objid, UInt32 senobjid, UInt32 sense_mask, SENSE_CONFIG action)
        {
            if (s1 == null)
                return;

            List<byte> buf = new List<byte>();
            if (action == SENSE_CONFIG.ADD)
                buf.Add(0);
            else
                buf.Add(1);

            buf.AddRange(BitConverter.GetBytes(sense_mask));

            SendMessageEx(s1, objid, senobjid, GetFreeMagic(), Proto.INFO_CONFIG_SENSE_OBJ, buf.ToArray());
        }

        /// <summary>
        /// 向远端GetValue
        /// </summary>
        /// <param name="s1">远端对应的连接代理</param>
        /// <param name="destid">远端的ID</param>
        /// <param name="srcid">本地的ID</param>
        /// <param name="memid">成员ID</param>
        public void GetValueEx(IFConn s1, UInt32 destid, UInt32 srcid, UInt16 memid)
        {
            if (s1 == null)
                return;

            if (s1 is FConnLocal)
            {
                byte[] value;
                GetValue(s1, srcid, destid, memid, out value);
                if (value != null)
                    PushGetValue(s1, destid, srcid, memid, value);
                return;
            }

            PushAny(s1, destid, srcid, GetFreeMagic(), Proto.INFO_GET_VALUE, memid, null);
        }

        /// <summary>
        /// 向远端SetValue
        /// </summary>
        /// <param name="s1">远端对应的连接代理</param>
        /// <param name="destid">远端的ID</param>
        /// <param name="srcid">本地的ID</param>
        /// <param name="memid">成员ID</param>
        /// <param name="value">成员内容</param>
        public void SetValueEx(IFConn s1, UInt32 destid, UInt32 srcid, UInt16 memid, byte[] value)
        {
            if (s1 == null)
                return;

            if (s1 is FConnLocal)
            {
                SetValue(s1, srcid, destid, memid, value);
                return;
            }

            PushAny(s1, destid, srcid, GetFreeMagic(), Proto.INFO_SET_VALUE, memid, value);
        }

        /// <summary>
        /// 向远端CallFunction
        /// </summary>
        /// <param name="s1">远端对应的连接代理 </param>
        /// <param name="destid">远端对象ID</param>
        /// <param name="srcid">本地对象ID</param>
        /// <param name="funcid">函数ID</param>
        /// <param name="param">函数参数</param>
        /// <param name="asyncDelegate">在其上调用异步调用的委托对象</param>
        /// <param name="asyncContext">作为 BeginInvoke 方法调用的最后一个参数而提供的对象</param>
        public UInt32 CallFunctionEx(IFConn s1, UInt32 destid, UInt32 srcid, UInt16 funcid, byte[] param, AsyncCBHandler asyncDelegate, object asyncContext)
        {
            if (s1 == null)
                return 0;

            UInt32 magic = GetFreeMagic();
            if (s1 is FConnLocal)
            {
                if ((asyncDelegate != null) || (asyncContext != null)) //需要返回参数！！！
                {
                    Transaction t = new Transaction()
                    {
                        Conn = s1,
                        SrcObjID = destid,
                        Magic = magic,
                        AsyncDelegate = asyncDelegate,
                        AsyncContext = asyncContext
                    };
                    Transactions.Add(t);
                }
                CallFunction(s1, srcid, destid, magic, funcid, param);
                return magic;
            }

            PushAny(s1, destid, srcid, magic, Proto.INFO_CALL_FUNCTION, funcid, param, asyncDelegate, asyncContext);

            return magic;
        }

        /// <summary>
        /// 向远端CallFunction
        /// </summary>
        /// <param name="s1">远端对应的连接代理 </param>
        /// <param name="destid">远端对象ID</param>
        /// <param name="srcid">本地对象ID</param>
        /// <param name="funcid">函数ID</param>
        /// <param name="param">函数参数</param>
        public UInt32 CallFunctionEx(IFConn s1, UInt32 destid, UInt32 srcid, UInt16 funcid, byte[] param)
        {
            return CallFunctionEx(s1, destid, srcid, funcid, param, null, null);
        }

        /// <summary>
        /// 发送大包数据
        /// </summary>
        /// <param name="conn"></param>
        /// <param name="destid"></param>
        /// <param name="srcid"></param>
        /// <param name="magic"></param>
        void SendBigSize(
            IFConn conn, UInt32 destid, UInt32 srcid, UInt32 magic)
        {
            var ts = from t in Transactions where t.Magic == magic && t.Conn == conn select t;
            if (ts.Count() == 0)
                return;
            Transaction tran = ts.First();
            if (tran.GetBigSize(out Pack_BigSize pack))
            {
                //缓冲区的数据都发送完了！！！！！
                if (tran.Reset())//复位缓冲区
                {
                    //不需要回调，直接删除 交易
                    Transactions.Remove(tran);
                }
                //需要回调的交易留着，通过magic匹配
            }

            SendMessageEx(conn, destid, srcid, magic,
                Proto.INFO_PUSH_BIGSIZE, pack.ToBytes());
        }

        /// <summary>
        /// 客户端接收大包数据
        /// </summary>
        void ReciveBigSizeInClient(IFConn conn, Pack_Proto p)
        {
            Pack_BigSize pack = new Pack_BigSize();
            if (!pack.TryParse(p.buf))
                throw new Exception("解析大包出错");
            
            //找到交易， 没有就创建
            var ts = from t in Transactions where t.Conn == conn && t.Magic == p.magic select t;
            Transaction tran;
            if (ts.Count() == 0)
            {
                tran = new Transaction()
                {
                    Conn = conn,
                    SrcObjID = p.destid,
                    Magic = p.magic
                };
                Transactions.Add(tran);
            }
            else
            {
                if (ts.Count() > 1)
                {
                    throw new Exception("接收大包时，出错，交易列表中 有2条或以上的交易号一样");
                }

                tran = ts.First();
            }




            if (!tran.ToBuf(pack))//把大包放入 交易的缓冲
            {
                //还没获取完大包，继续问服务器获取
                if (tran.InfoId == Proto.INFO_PUSH_CALL_FUNCTION)
                {
                    //如果是callfunction， 引发进度事件
                    PushCallFunctionBigSizeStatus(conn, p.srcid, p.destid, p.magic, tran.FuncID, tran.Size, tran.Position, tran.AsyncContext);
                }


                //继续获取参数！！！！！！！！！
                SendMessageEx(conn, p.srcid, p.destid, p.magic,
                    Proto.INFO_REQUEST_BIGSIZE, null);
            }
            else
            {
                //大包接收完了
                //交易完成，删除
                Transactions.Remove(tran);

                if (tran.InfoId == Proto.INFO_PUSH_CALL_FUNCTION)
                {
                    //如果是callfunction， 引发进度事件
                    PushCallFunctionBigSizeStatus(conn, p.srcid, p.destid, p.magic, tran.FuncID, tran.Size, tran.Position, tran.AsyncContext);
                }

                switch (tran.InfoId)
                {
                    case Proto.INFO_PUSH_CALL_FUNCTION:
                        PushCallFunction(conn, p.srcid, p.destid, p.magic, tran.FuncID, tran.Buf, tran.AsyncDelegate, tran.AsyncContext);
                        break;
                    case Proto.INFO_PUSH_GET_VALUE:
                        PushGetValue(conn, p.srcid, p.destid, tran.FuncID, tran.Buf);
                        break;
                    case Proto.INFO_PUSH_EVENT:
                        PushObjInfo(conn, p.srcid, p.destid, tran.FuncID, tran.Buf);
                        break;
                }

            }
        }

        /// <summary>
        /// 服务器接收大包数据
        /// </summary>
        void ReciveBigSizeInServer(IFConn conn, Pack_Proto p)
        {
            Pack_BigSize pack = new Pack_BigSize();
            if (!pack.TryParse(p.buf))
                throw new Exception("解析大包出错");

            //找到交易， 没有就创建
            var ts = from t in Transactions where t.Conn == conn && t.Magic == p.magic select t;
            Transaction tran;
            if (ts.Count() == 0)
            {
                tran = new Transaction()
                {
                    Conn = conn,
                    SrcObjID = p.destid,
                    Magic = p.magic
                };
                Transactions.Add(tran);
            }
            else
            {
                if (ts.Count() > 1)
                {
                    throw new Exception("接收大包时，出错，交易列表中 有2条或以上的交易号一样");
                }


                tran = ts.First();
            }
            

            if (!tran.ToBuf(pack))//把大包放入 交易的缓冲
            {
                //继续获取参数！！！！！！！！！
                SendMessageEx(conn, p.srcid, p.destid, p.magic,
                    Proto.INFO_REQUEST_BIGSIZE, null);
            }
            else
            {
                //大包接收完了

                Transactions.Remove(tran);//用完了，删除！！！

                switch (tran.InfoId)
                {
                    case Proto.INFO_CALL_FUNCTION:
                        CallFunction(conn, p.srcid, p.destid, p.magic, tran.FuncID, tran.Buf);
                        break;
                    case Proto.INFO_SET_VALUE:
                        SetValue(conn, p.srcid, p.destid, tran.FuncID, tran.Buf);
                        break;
                }

            }
        }
        #endregion

        #endregion
        /// <summary>
        /// 通过objid 查找 obj
        /// </summary>
        /// <param name="objid"></param>
        /// <returns></returns>
        IFObj Find(UInt32 objid) 
        {
            try
            {
                KeyValuePair<IFObj, List<Sense>> obj_kv = Objs.First(obj => obj.Key.ID == objid);
                return obj_kv.Key;
            }
            catch
            {
                return null;
            }
        }
        UInt32 free_magic = 1;
        /// <summary>
        /// 获取一个没有被分配的 交易码
        /// </summary>
        /// <returns></returns>
        UInt32 GetFreeMagic()
        {
            UInt32 f = free_magic;
            free_magic++;
            if (free_magic == 0)
                free_magic++;

            //if (free_magic > UInt32.MaxValue)
            //    freetranno = 1;
            return f;
        }

        /// <summary>
        /// 作为客户端，与服务器连续状态改变
        /// </summary>
        /// <param name="conn"></param>
        void CConnConnectAction(IFConn conn)
        {
            TCPCConn cc = (TCPCConn)conn;
            if (logger.IsDebugEnabled)
            {
                logger.Debug($"(C) CConnConnectAction {cc.RemoteEP} conn.IsConnected={conn.IsConnected}");
            }

            KeyValuePair<TCPCConn, List<UInt32>> cc_kv;
            try
            {
                cc_kv = m_cconns.First(_cc => _cc.Key == cc);
                foreach (UInt32 objid in cc_kv.Value) 
                {
                    IFObj obj = Find(objid);
                    if (obj != null)
                        obj.ConnectNotify(cc);
                }    
            }
            catch(Exception e)
            {
                throw e;
            }

        }
        /// <summary>
        /// 作为服务器端，客户的登陆与退出
        /// </summary>
        /// <param name="conn"></param>
        void SConnConnectAction(IFConn conn)
        {
            if (logger.IsDebugEnabled)
            {
                if (conn is TCPConn)
                {
                    logger.Debug($"(S) SConnConnectAction IsConnected={conn.IsConnected}");
                }
            }
            
            //通知obj 连接状态
            foreach (UInt32 objid in m_serverConnectedNotifyObjID)
            {
                IFObj obj = Find(objid);
                if (obj != null)
                    obj.ConnectNotify(conn);
            }

            //当通讯断开时，还要把关注的推送删除掉
            if(conn.IsConnected==false)
            {
                for (int i = 0; i < Objs.Keys.Count(); i++)
                {
                    List<Sense> senses = Objs.ElementAt(i).Value;
                    senses.RemoveAll(s => s.conn == conn);
                }
                //删除所有与它有关的 tran
                Transactions.RemoveAll(t => t.Conn == conn);
            }
        }
        /// <summary>
        /// 作为服务器端，客户端 的 某obj 注销了，把它相关的都删除
        /// </summary>
        /// <param name="conn"></param>
        /// <param name="srcid"></param>
        void ClearRemoteObjConn(IFConn conn, UInt32 srcid) 
        {
            //删除推送
            for (int i = 0; i < Objs.Keys.Count(); i++)
            {
                List<Sense> senses = Objs.ElementAt(i).Value;
                senses.RemoveAll(s => (s.conn == conn) && (s.objid == srcid));
            }
            //删除所有与它有关的 tran
            Transactions.RemoveAll(t => t.Conn == conn && t.SrcObjID == srcid);
        }


        #region 本地，给Protocol的函数指针

        /// <summary>
        /// 本地，给Protocol的函数指针;
        /// GetValue 
        /// </summary>
        /// <param name="from">远端对应的连接代理</param>
        /// <param name="srcid">远端对象ID</param>
        /// <param name="destid">本地对象ID</param>
        /// <param name="memid"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        void GetValue(IFConn from,UInt32 srcid,UInt32 destid, UInt16 memid,out byte[] value)
        {
            value = null;

            IFObj destobj = Find(destid);
            if (destobj == null)
                return;

            destobj.GetValue(from, srcid,memid,out value);
        }
        /// <summary>
        /// 本地，给Protocol的函数指针;
        /// SetValue
        /// </summary>
        /// <param name="from">远端对应的连接代理</param>
        /// <param name="srcid">远端对象ID</param>
        /// <param name="destid">本地对象ID</param>
        /// <param name="memid"></param>
        /// <param name="value"></param>
        void SetValue(IFConn from, UInt32 srcid, UInt32 destid, UInt16 memid, byte[] value)
        {
            IFObj destobj = Find(destid);
            if (destobj == null)
                return;

           destobj.SetValue(from, srcid, memid, value);
        }
        /// <summary>
        /// 本地，给Protocol的函数指针;
        /// CallFunction
        /// </summary>
        /// <param name="from">远端对应的连接代理</param>
        /// <param name="srcid">远端对象ID</param>
        /// <param name="destid">本地对象ID</param>
        /// <param name="magic">交易号</param>
        /// <param name="funcid">功能号</param>
        /// <param name="param">参数</param>     
        void CallFunction(IFConn from, UInt32 srcid, UInt32 destid, UInt32 magic, UInt16 funcid, byte[] param)
        {
            IFObj destobj = Find(destid);
            if (destobj == null)
                return;

            destobj.CallFunction(from, srcid, magic, funcid, param);
        }
        /// <summary>
        /// 本地，给Protocol的函数指针;
        /// Sense_Config
        /// </summary>
        /// <param name="from">远端对应的连接代理</param>
        /// <param name="srcid">远端对象ID</param>
        /// <param name="destid">本地对象ID</param>
        /// <param name="sense_mask">远端对象 关注 本地对象 的敏感源</param>
        /// <param name="action">动作，添加?删除?</param>
        void Sense_Config(IFConn from, UInt32 srcid, UInt32 destid, UInt32 sense_mask, SENSE_CONFIG action)
        {
            try
            {
                KeyValuePair<IFObj, List<Sense>> obj_kv = Objs.First(obj => obj.Key.ID == destid);
                
                Sense sense = obj_kv.Value.Find(s=>s.conn == from && s.objid==srcid);
                if (sense == null)
                {
                    if (action == SENSE_CONFIG.ADD)
                    {
                        Sense s = new Sense() { conn = from, objid = srcid, sense_mask = sense_mask };
                        obj_kv.Value.Add(s);
                    }
                }
                else 
                {
                    if (action == SENSE_CONFIG.ADD)
                    {
                        sense.sense_mask |= sense_mask;
                    }
                    else 
                    {
                        sense.sense_mask &= ~sense_mask;
                    }
                }
 
            }
            catch
            {
                //不能找到对应的objid
            }
        }
        
        void PushObjInfo(IFConn from, UInt32 srcid, UInt32 destid, UInt16 infoid, byte[] infodata) 
        {

            IFObj destobj = Find(destid);
            if (destobj == null)
                return;

            destobj.PushInfo(from, srcid, infoid, infodata);      
        }
        void PushGetValue(IFConn from, UInt32 srcid, UInt32 destid, UInt16 infoid, byte[] infodata)
        {
            IFObj destobj = Find(destid);
            if (destobj == null)
                return;
            destobj.PushGetValue(from, srcid, infoid, infodata);
        }
        void PushCallFunction(IFConn from, UInt32 srcid, UInt32 destid, UInt32 magic, UInt16 infoid, byte[] infodata, AsyncCBHandler asyncDelegate, object asyncContext)
        {
            IFObj destobj = Find(destid);
            if (destobj == null)
                return;
            destobj.PushCallFunction(from, srcid, magic, infoid, infodata, asyncDelegate, asyncContext);
        }

        void PushCallFunctionBigSizeStatus(IFConn from, UInt32 srcid, UInt32 destid, UInt32 magic, UInt16 infoid, int total_size, int download_size, object asyncContext)
        {
            IFObj destobj = Find(destid);
            if (destobj == null)
                return;

            destobj.PushCallFunctionBigSizeStatus(from, srcid, magic, infoid, total_size, download_size, asyncContext);
        }

        int Process(IFConn from, UInt32 srcid, UInt32 destid, UInt32 magic, UInt16 info_no, byte[] infodata, out byte[] retdata)
        {
            retdata = null;
            IFObj destobj = Find(destid);
            if (destobj == null)
                return -1;
            return destobj.ProcessEx(from, srcid, magic, info_no, infodata,out retdata);  
        }
        #endregion

        /// <summary>
        /// 从异端接收的数据，解释数据
        /// </summary>
        /// <param name="packet"></param>
        /// <param name="conn"></param>
        /// <returns></returns>
        bool ParsePacketInClient(byte[] packet, IFConn conn)
        {
            Pack_Proto p = new Pack_Proto();
            if (!p.TryParse(packet))
                return false;


            if (logger.IsDebugEnabled)
            {
                logger.Debug($"(C) ParsePacketInClient {Environment.NewLine}{Pack_ProtoToString(p)}");
            }


            conn.TranID = p.magic;
            {
                if (Process(conn, p.srcid, p.destid, p.magic, p.info, p.buf, out byte[] retdata) == 0)
                {
                    if (retdata != null)
                    {
                        SendMessageEx(conn, p.srcid, p.destid, p.magic, p.info, retdata);
                    }
                    return true;
                }
            }

            switch (p.info)
            {
                case Proto.INFO_PUSH_EVENT:
                    {
                        Pack_GetSetPushCall pack = new Pack_GetSetPushCall();
                        if (!pack.TryParse(p.buf))
                            return false;
                        PushObjInfo(conn, p.srcid, p.destid, pack.infoid, pack.infodata);
                    }break;
                case Proto.INFO_PUSH_GET_VALUE:
                    {
                        Pack_GetSetPushCall pack = new Pack_GetSetPushCall();
                        if (!pack.TryParse(p.buf))
                            return false;
                        PushGetValue(conn, p.srcid, p.destid, pack.infoid, pack.infodata);
                    }break;
                case Proto.INFO_PUSH_CALL_FUNCTION:
                    {
                        Pack_GetSetPushCall pack = new Pack_GetSetPushCall();
                        if (!pack.TryParse(p.buf))
                            return false;

                        var ts = from t in Transactions where (t.Magic == p.magic) && (t.Conn == conn) select t;
                        if (ts.Count() > 0)
                        {
                            Transaction tran = ts.First();
                            Transactions.Remove(tran);//用完，删除
                            PushCallFunction(conn, p.srcid, p.destid, p.magic, pack.infoid, pack.infodata, tran.AsyncDelegate, tran.AsyncContext);
                        }
                        else 
                        {
                            PushCallFunction(conn, p.srcid, p.destid, p.magic, pack.infoid, pack.infodata, null, null);
                        }
                    } break;
                case Proto.INFO_REQUEST_BIGSIZE:
                    {
                        SendBigSize(conn, p.srcid, p.destid, p.magic);
                    } break;
                case Proto.INFO_PUSH_BIGSIZE:
                    {
                        ReciveBigSizeInClient(conn, p);
                    } break;
            }
            
            return true;
        }
        
        /// <summary>
        /// 从异端接收的数据，解释数据
        /// </summary>
        /// <param name="packet"></param>
        /// <param name="conn"></param>
        /// <returns></returns>
        bool ParsePacketInServer(byte[] packet, IFConn conn)
        {

            Pack_Proto p = new Pack_Proto();
            if (!p.TryParse(packet))
                return false;

            conn.TranID = p.magic;
            {
                if (Process(conn, p.srcid, p.destid, p.magic, p.info, p.buf, out byte[] retdata) == 0)
                {
                    if (retdata != null)
                    {
                        SendMessageEx(conn, p.srcid, p.destid, p.magic, p.info, retdata);
                    }
                    return true;
                }
            }

            switch (p.info)
            {
                case Proto.INFO_SET_VALUE:
                    {
                        Pack_GetSetPushCall p_info = new Pack_GetSetPushCall();
                        if (!p_info.TryParse(p.buf))
                            throw new Exception("解析 SetValue 出错");
                        
                        SetValue(conn, p.srcid, p.destid, p_info.infoid, p_info.infodata);
                    } break;
                case Proto.INFO_GET_VALUE:
                    {
                        Pack_GetSetPushCall p_info = new Pack_GetSetPushCall();
                        if (!p_info.TryParse(p.buf))
                            throw new Exception("解析 GetValue 出错");
                        GetValue(conn, p.srcid, p.destid, p_info.infoid, out byte[] value);

                        if (value != null)
                        {
                            PushAny(conn, p.srcid, p.destid, p.magic,
                                Proto.INFO_PUSH_GET_VALUE, p_info.infoid, value);
                        }
                    } break;
                case Proto.INFO_CALL_FUNCTION:
                    {
                        Pack_GetSetPushCall p_info = new Pack_GetSetPushCall();
                        if (!p_info.TryParse(p.buf))
                            throw new Exception("解析 CallFunction 出错");

                        CallFunction(conn, p.srcid, p.destid, p.magic, p_info.infoid, p_info.infodata);
                    } break;
                case Proto.INFO_CONFIG_SENSE_OBJ:
                    {
                        SENSE_CONFIG action;

                        if (p.buf[0] == 0)
                            action = SENSE_CONFIG.ADD;
                        else
                            action = SENSE_CONFIG.REMOVE;

                        UInt32 mask = BitConverter.ToUInt32(p.buf, 1);
                        Sense_Config(conn, p.srcid, p.destid, mask, action);
                    }
                    break;
                case Proto.INFO_OBJ_DISPOSE:
                    {
                        ClearRemoteObjConn(conn, p.srcid);
                    }
                    break;
                case Proto.INFO_PUSH_BIGSIZE:
                    {
                        //服务器端，收到客户端 的 大包
                        ReciveBigSizeInServer(conn, p);
                    }break;
                case Proto.INFO_REQUEST_BIGSIZE:
                    {
                        //服务器端，收到客户端 “请继续发送回复大包” 请求
                        SendBigSize(conn, p.srcid, p.destid, p.magic);
                    }break;

            }
            //DateTime dt1 = DateTime.Now;
            //Console.WriteLine("FObjSys ParsePacketInServer " + "p.info=" + p.info.ToString() + " " + (dt1 - dt2).TotalMilliseconds.ToString() + " " + (dt1 - dtlast).TotalMilliseconds.ToString());
            //dtlast = dt1;
            return true;
        }

    }


}
