// Decompiled with JetBrains decompiler
// Type: Microsoft.Iris.Render.Protocol.RenderPort
// Assembly: UIX.RenderApi, Version=4.8.0.0, Culture=neutral, PublicKeyToken=ddd0da4d3e678217
// MVID: D47658B8-A8EA-43D6-8837-ECE823BFFFC1
// Assembly location: C:\Program Files\Zune\UIX.RenderApi.dll

using Microsoft.Iris.Render.Common;
using Microsoft.Iris.Render.Internal;
using Microsoft.Iris.Render.Protocols.Splash.Messaging;
using System;
using System.Collections;

namespace Microsoft.Iris.Render.Protocol
{
    internal class RenderPort : IRenderPortCallback, IRenderHandleOwner
    {
        private const uint c_maxCachedBatchSendInfos = 25;
        private MessagingSession _session;
        private IChannel _channel;
        private ContextID _idRemoteContext;
        private ProtocolSplashMessaging _protocolMessaging;
        private ArrayList _protocols = new ArrayList();
        private HandleTable _handleTable;
        private Map<RENDERGROUP, WeakReference> _groupProxies;
        private MessageCookieLayout _layoutOfRenderHandles;
        private RENDERGROUP _defaultGroupId;
        private RENDERHANDLE _rootHandle;
        private ArrayList _callbackHandlers = new ArrayList();
        private MessageHeap _currentMessageHeap;
        private ObjectCache _spareHeapCache;
        private Map<MessageHeap, MessageHeap> _activeHeaps;
        private uint _nextBatchId;
        private RenderPort.BatchSendInfo _sentBatchesHead;
        private RenderPort.BatchSendInfo _sentBatchesTail;
        private RenderPort.BatchSendInfo _freeBatchSendInfos;
        private uint _numFreeBatchSendInfos;
        private unsafe MessageBatchEntry* _lastBatchEntry;
        private uint _lastBatchBlockId;
        private RENDERHANDLE[] _pendingBatchDeletes;
        private uint _pendingBatchDeleteSlot;
        private RENDERGROUP[] _pendingBatchGroupDeletes;
        private uint _pendingBatchGroupDeleteSlot;
        private BatchCallback _batchPopulateCallback;
        private bool _individualMessageMode;
        private uint _uBatchedMessages;
        private DeferredHandler _deferredObjectReleaseWorker;
        private DeferredHandler _deferredGroupReleaseWorker;
        private DeferredHandler _deferredHeapReleaseWorker;
        private DeferredHandler _deferredTrafficUpdate;
        private bool _foreignByteOrder;
        private static object s_SyncLock = new object();
        private static ushort[] s_ByteOrder_CallbackMessage;
        private static ushort[] s_ByteOrder_MessageBatchHeader;
        private static ushort[] s_ByteOrder_MessageBatchEntry;
        private ulong _ulOverallControlTraffic;
        private uint _uRecentControlTraffic;
        private ulong _ulOverallDataTraffic;
        private uint _uRecentDataTraffic;
        private ulong _ulOverallTotalTraffic;
        private DateTime _dtRecentTrafficTime;
        private bool _fPendingTrafficSummary;
        private static TimeSpan s_timeTrafficWindow = TimeSpan.FromMilliseconds(500.0);

        internal RenderPort(
          MessagingSession session,
          IChannel channel,
          ContextID idRemoteContext,
          RENDERGROUP idGroup,
          MessageCookieLayout layout,
          bool foreignByteOrder)
        {
            Debug2.Validate(session != null, typeof(ArgumentNullException), nameof(session));
            Debug2.Validate(channel != null, typeof(ArgumentNullException), nameof(channel));
            Debug2.Validate(idRemoteContext != ContextID.NULL, typeof(ArgumentNullException), nameof(idRemoteContext));
            this._deferredObjectReleaseWorker = new DeferredHandler(this.DeferredObjectReleaseWorker);
            this._deferredGroupReleaseWorker = new DeferredHandler(this.DeferredGroupReleaseWorker);
            this._deferredHeapReleaseWorker = new DeferredHandler(this.DeferredHeapReleaseWorker);
            this._deferredTrafficUpdate = new DeferredHandler(this.DeferredTrafficUpdate);
            this._activeHeaps = new Map<MessageHeap, MessageHeap>();
            this._spareHeapCache = new ObjectCache(new ObjectCache.Callback(this.HeapCacheCallback), 10, false);
            this._individualMessageMode = true;
            this._layoutOfRenderHandles = layout;
            this._handleTable = new HandleTable(this, this._layoutOfRenderHandles);
            this._defaultGroupId = this.AllocHandleGroup(false);
            this._rootHandle = this.AllocHandle(this);
            if (idGroup != RENDERGROUP.NULL)
            {
                this.AllocHandleGroupWithId(idGroup);
                this._defaultGroupId = idGroup;
            }
            this._channel = channel;
            this._session = session;
            this._foreignByteOrder = foreignByteOrder;
            this._idRemoteContext = idRemoteContext;
            this._protocolMessaging = ProtocolSplashMessaging.Bind(this);
        }

        public void Connect()
        {
            Debug2.Throw(this._handleTable != null && this._channel != null, "must be valid to connect");
            Debug2.Throw(!this._channel.IsConnected, "already connected");
            this._channel.Connect(this._idRemoteContext, this._rootHandle, this._layoutOfRenderHandles);
            foreach (ProtocolInstance protocol in this._protocols)
                protocol.OnConnect();
        }

        internal void PingConnection() => LocalRenderPortCallback.AutoSendOnPingReply(this._protocolMessaging, RENDERHANDLE.NULL);

        internal event EventHandler PingReply;

        public void Dispose()
        {
            this.PingReply = null;
            this.DisposeObjectMaps();
            if (this._channel != null)
            {
                this._channel.Dispose();
                this._channel = null;
            }
            if (this._spareHeapCache == null)
                return;
            if (this._currentMessageHeap != null)
                this.ReleaseHeap(this._currentMessageHeap);
            this._currentMessageHeap = null;
            this._spareHeapCache.Dispose();
            this._spareHeapCache = null;
        }

        public void DisposeObjectMaps()
        {
            if (this._handleTable == null)
                return;
            this._handleTable.Dispose();
            this._handleTable = null;
        }

        public MessagingSession Session => this._session;

        public ContextID RemoteContext => this._idRemoteContext;

        public bool ForeignByteOrder => this._foreignByteOrder;

        public bool AlwaysBigEndian => this._foreignByteOrder;

        internal uint DefaultGroupID => RENDERGROUP.ToUInt32(this._defaultGroupId);

        RENDERHANDLE IRenderHandleOwner.RenderHandle => RENDERHANDLE.NULL;

        void IRenderHandleOwner.OnDisconnect()
        {
        }

        internal ProtocolSplashMessaging MessagingProtocol => this._protocolMessaging;

        public ProtocolInstance LookUpProtocol(string name)
        {
            ProtocolInstance protocolInstance = null;
            foreach (ProtocolInstance protocol in this._protocols)
            {
                if (protocol.Name == name)
                    protocolInstance = protocol;
            }
            return protocolInstance;
        }

        internal void BindProtocol(ProtocolInstance protocol)
        {
            Debug2.Validate(this.LookUpProtocol(protocol.Name) == null, typeof(InvalidOperationException), nameof(protocol), "protocol already bound");
            this._protocols.Add(protocol);
            if (this._channel == null || !this._channel.IsConnected)
                return;
            protocol.OnConnect();
        }

        internal RENDERHANDLE InitRemoteClass(string className)
        {
            if (className.Equals("Splash::Messaging::Broker"))
                return this._rootHandle;
            RENDERHANDLE idObjectClass = this.AllocHandle(this);
            RemoteBroker.SendCreateClass(this._protocolMessaging, className, idObjectClass);
            return idObjectClass;
        }

        internal unsafe void CreateRemoteObject(
          RENDERHANDLE classCreate,
          RENDERHANDLE hNewObject,
          Message* pmsgConstructor)
        {
            RemoteBroker.SendCreateObject(this._protocolMessaging, classCreate, hNewObject, pmsgConstructor);
        }

        internal unsafe void SendLocalCallbackMessage(Message* pmsgCallback) => Protocols.Splash.Messaging.RemoteContext.SendForwardMessage(this._protocolMessaging, this._session.LocalContext, pmsgCallback);

        internal void LinkContext(ContextID idContextExisting, ContextID idContextAlias) => RemoteContextRelay.SendLinkContext(this._protocolMessaging, idContextExisting, idContextAlias);

        internal void UnlinkContext(ContextID idContextExisting, ContextID idContextAlias) => RemoteContextRelay.SendUnlinkContext(this._protocolMessaging, idContextExisting, idContextAlias);

        public void BeginMessageBatch(BatchCallback populateCallback)
        {
            this.Session.AssertOwningThread();
            Debug2.Throw(this._individualMessageMode, "Already in batch mode");
            this._batchPopulateCallback = populateCallback;
            this._individualMessageMode = false;
        }

        public void EndMessageBatch(BatchCallback deliveryCallback)
        {
            this.Session.AssertOwningThread();
            Debug2.Throw(!this._individualMessageMode, "Must be collecting a batch to end");
            this.SendCurrentBatch(deliveryCallback);
            this._individualMessageMode = true;
        }

        public unsafe void SendDataBuffer(void* pvData, uint cbSize, RENDERHANDLE idRemoteBuffer)
        {
            Debug2.Validate(idRemoteBuffer != RENDERHANDLE.NULL, typeof(ArgumentNullException), nameof(idRemoteBuffer));
            this.UpdateTraffic(0U, cbSize);
            var info = new EngineApi.BufferInfo
            {
                idContextSrc = this._session.LocalContext,
                idContextDest = this._idRemoteContext,
                idBuffer = idRemoteBuffer,
                nFlags = 0,
                cbSizeBuffer = cbSize
            };
            EngineApi.IFC(EngineApi.SpBufferOpen(&info, pvData));
        }

        public unsafe void SendBatchBuffer(void* pvData, uint cbSize, RENDERHANDLE idRemoteBuffer)
        {
            int num = idRemoteBuffer != RENDERHANDLE.NULL ? 1 : 0;
            this.UpdateTraffic(cbSize, 0U);
            var info = new EngineApi.BufferInfo()
            {
                idContextSrc = this._session.LocalContext,
                idContextDest = this._idRemoteContext,
                idBuffer = idRemoteBuffer,
                nFlags = EngineApi.BufferFlags.IsBatch,
                cbSizeBuffer = cbSize
            };
            EngineApi.IFC(EngineApi.SpBufferOpen(&info, pvData));
        }

        public unsafe void SendMessageBuffer(Message* pmsg)
        {
            this.UpdateTraffic(pmsg->cbSize, 0U);
            uint src = pmsg->cbSize;
            if (this.ForeignByteOrder)
                src = Memory.ConvertEndian(src);
            var info = new EngineApi.BufferInfo()
            {
                idContextSrc = this._session.LocalContext,
                idContextDest = this._idRemoteContext,
                idBuffer = RENDERHANDLE.NULL,
                nFlags = EngineApi.BufferFlags.CopyData,
                cbSizeBuffer = src
            };
            EngineApi.IFC(EngineApi.SpBufferOpen(&info, pmsg));
        }

        private void UpdateTraffic(uint uControl, uint uData)
        {
            this._uRecentControlTraffic += uControl;
            this._uRecentDataTraffic += uData;
            this._dtRecentTrafficTime = DateTime.UtcNow;
            if (Debug2.IsCategoryEnabled(DebugCategory.Protocol, 1))
            {
                if (this._fPendingTrafficSummary || !this.Session.IsConnected)
                    return;
                this.Session.DeferredInvoke(_deferredTrafficUpdate, null, DeferredInvokePriority.Idle, s_timeTrafficWindow);
                this._fPendingTrafficSummary = true;
            }
            else
                this.TrafficUpdateWorker();
        }

        private void DeferredTrafficUpdate(object obj)
        {
            if (!this._fPendingTrafficSummary)
                return;
            if (DateTime.UtcNow - this._dtRecentTrafficTime < s_timeTrafficWindow)
            {
                this.Session.DeferredInvoke(_deferredTrafficUpdate, null, DeferredInvokePriority.Idle, this._dtRecentTrafficTime + s_timeTrafficWindow - DateTime.UtcNow);
            }
            else
            {
                this._fPendingTrafficSummary = false;
                this.TrafficUpdateWorker();
            }
        }

        private void TrafficUpdateWorker()
        {
            this._ulOverallControlTraffic += _uRecentControlTraffic;
            this._ulOverallDataTraffic += _uRecentDataTraffic;
            this._ulOverallTotalTraffic += this._uRecentControlTraffic + this._uRecentDataTraffic;
            Debug2.IsCategoryEnabled(DebugCategory.Protocol, 1);
            this._uRecentControlTraffic = 0U;
            this._uRecentDataTraffic = 0U;
        }

        public unsafe void* AllocMessageBuffer(uint size)
        {
            this.Session.AssertOwningThread();
            if (this._currentMessageHeap == null)
                this._currentMessageHeap = this.AllocHeap();
            return this._currentMessageHeap.Alloc(size);
        }

        public unsafe void SendRemoteMessage(Message* pmsg)
        {
            this.Session.AssertOwningThread();
            Debug2.Throw(this._channel != null, "Not connected to renderer");
            if (this._individualMessageMode)
            {
                this.SendMessageBuffer(pmsg);
                if (this._currentMessageHeap == null)
                    return;
                this._currentMessageHeap.Reset();
            }
            else
                this.StoreMessageEntry(pmsg);
        }

        public RENDERHANDLE AllocHandle(IRenderHandleOwner owner)
        {
            Debug2.Throw(this._handleTable != null, "Render port has already shut down");
            Debug2.Validate(owner != null, typeof(ArgumentNullException), nameof(owner));
            return this._handleTable.Alloc(this._defaultGroupId, owner);
        }

        public void RegisterKnownHandle(IRenderHandleOwner owner, RENDERHANDLE handle)
        {
            Debug2.Throw(this._handleTable != null, "Render port has already shut down");
            Debug2.Validate(owner != null, typeof(ArgumentNullException), nameof(owner));
            this._handleTable.AllocWithId(owner, handle, true);
        }

        public void FreeHandle(RENDERHANDLE handle)
        {
            if (this.Session.IsOwningThread())
            {
                if (this._handleTable == null)
                    return;
                if (this._channel != null && this._handleTable.GetGroup(handle) == this._defaultGroupId)
                {
                    this._handleTable.SetOwner(handle, null);
                    this.AddPendingDelete(handle);
                    RemoteBroker.SendDestroyObject(this._protocolMessaging, handle);
                }
                else
                    this._handleTable.Free(handle);
            }
            else
                this.Session.DeferredInvoke(_deferredObjectReleaseWorker, handle, DeferredInvokePriority.High);
        }

        public void CancelAllocHandle(RENDERHANDLE handle)
        {
            if (this._handleTable == null)
                return;
            this._handleTable.Free(handle);
        }

        public IRenderHandleOwner TryGetHandleOwner(RENDERHANDLE handle) => this._handleTable.GetOwner(handle, true);

        public IRenderHandleOwner TryGetProxyOwner(RENDERHANDLE handle)
        {
            IRenderHandleOwner renderHandleOwner = null;
            if (this._groupProxies != null)
            {
                RENDERGROUP group = this._handleTable.GetGroup(handle);
                if (group != RENDERGROUP.NULL)
                    renderHandleOwner = this.GetGroupProxy(group);
            }
            return renderHandleOwner;
        }

        public IRenderHandleOwner GetHandleOwner(RENDERHANDLE handle) => this._handleTable.GetOwner(handle);

        public void ValidateHandle(RENDERHANDLE handle) => this._handleTable.Validate(handle);

        public void ValidateHandleOrNull(RENDERHANDLE handle)
        {
            if (!(handle != RENDERHANDLE.NULL))
                return;
            this._handleTable.Validate(handle);
        }

        public RENDERGROUP AllocHandleGroup() => this.AllocHandleGroup(true);

        public void AllocHandleGroupWithId(RENDERGROUP idGroup) => this._handleTable.AllocGroupWithId(idGroup);

        private RENDERGROUP AllocHandleGroup(bool failIfNotConnected)
        {
            RENDERGROUP handle = this._handleTable.AllocGroup();
            if (this._channel != null)
                Protocols.Splash.Messaging.RemoteContext.SendCreateGroup(this._protocolMessaging, (int)RENDERGROUP.ToUInt32(handle), this._session.LocalContext);
            else if (failIfNotConnected)
                Debug2.Throw(false, "Not connected to renderer");
            return handle;
        }

        public void FreeHandleGroup(RENDERGROUP group)
        {
            if (this.Session.IsOwningThread())
            {
                if (this._handleTable == null)
                    return;
                if (this._channel != null)
                {
                    this.AddPendingGroupDelete(group);
                    Protocols.Splash.Messaging.RemoteContext.SendDestroyGroup(this._protocolMessaging, (int)RENDERGROUP.ToUInt32(group));
                }
                else
                    this._handleTable.FreeGroup(group);
            }
            else
                this.Session.DeferredInvoke(_deferredGroupReleaseWorker, group, DeferredInvokePriority.High);
        }

        public void RegisterGroupProxy(RENDERGROUP group, IRenderHandleOwner proxy)
        {
            if (this._groupProxies == null)
                this._groupProxies = new Map<RENDERGROUP, WeakReference>();
            this._groupProxies[group] = new WeakReference(proxy);
        }

        public void RevokeGroupProxy(RENDERGROUP group)
        {
            if (this._groupProxies == null)
                return;
            this._groupProxies[group] = null;
        }

        public IRenderHandleOwner GetGroupProxy(RENDERGROUP group)
        {
            IRenderHandleOwner renderHandleOwner = null;
            if (this._groupProxies != null)
            {
                WeakReference groupProxy = this._groupProxies[group];
                if (groupProxy != null)
                    renderHandleOwner = groupProxy.Target as IRenderHandleOwner;
            }
            return renderHandleOwner;
        }

        public MessageHeap AllocHeap()
        {
            this.Session.AssertOwningThread();
            return (MessageHeap)this._spareHeapCache.Pop();
        }

        public void ReleaseHeap(MessageHeap heap)
        {
            if (this.Session.IsOwningThread())
                this.DeferredHeapReleaseWorker(heap);
            else if (this.Session.OwningThread != null)
                this.Session.DeferredInvoke(_deferredHeapReleaseWorker, heap, DeferredInvokePriority.High);
            else
                heap.Dispose();
        }

        private unsafe void StoreMessageEntry(Message* pMethodMsg)
        {
            MessageHeap.Block block = this._currentMessageHeap.LookupBlock(pMethodMsg);
            Debug2.Throw((IntPtr)block.data != IntPtr.Zero, "message should be in current message heap");
            uint num = (uint)((ulong)((sbyte*)pMethodMsg - (sbyte*)block.data) - (ulong)sizeof(MessageBatchEntry));
            if ((IntPtr)_lastBatchEntry != IntPtr.Zero && _lastBatchBlockId == block.id)
                this._lastBatchEntry->uOffsetNextEntry = num;
            else
                ((MessageBatchHeader*)block.data)->uOffsetFirstEntry = num;
            this._lastBatchEntry = (MessageBatchEntry*)((nint)block.data + num);
            this._lastBatchBlockId = block.id;
            ++this._uBatchedMessages;
            BatchCallback populateCallback = this._batchPopulateCallback;
            if (populateCallback == null)
                return;
            this._batchPopulateCallback = null;
            populateCallback();
        }

        private unsafe void SendCurrentBatch(BatchCallback deliveryCallback)
        {
            try
            {
                if ((IntPtr)this._lastBatchEntry != IntPtr.Zero)
                {
                    uint nextBatchId = this._nextBatchId;
                    ++this._nextBatchId;
                    try
                    {
                        LocalRenderPortCallback.AutoSendOnBatchProcessed(this._protocolMessaging, RENDERHANDLE.NULL, nextBatchId);
                        RenderPort.BatchSendInfo info = this.AllocBatchSendInfo();
                        info.batchID = nextBatchId;
                        info.heap = this._currentMessageHeap;
                        info.pendingDeletes = this._pendingBatchDeletes;
                        info.pendingGroupDeletes = this._pendingBatchGroupDeletes;
                        info.deliveryCallback = deliveryCallback;
                        this.EnqueuePendingBatch(info);
                        RENDERHANDLE renderhandle = RENDERHANDLE.NULL;
                        int blockCount = this._currentMessageHeap.BlockCount;
                        uint num = 0;
                        MessageHeap.Enumerator enumerator = this._currentMessageHeap.GetEnumerator();
                        while (enumerator.MoveNext())
                        {
                            MessageHeap.Block current = enumerator.Current;
                            MessageBatchHeader* data = (MessageBatchHeader*)current.data;
                            RENDERHANDLE idRemoteBuffer = RENDERHANDLE.NULL;
                            --blockCount;
                            if (blockCount > 0)
                                idRemoteBuffer = this.AllocHandle(this);
                            data->idPredicateBuffer = renderhandle;
                            if (this.ForeignByteOrder)
                                SwapBatchListByteOrder(data);
                            num += current.size;
                            this.SendBatchBuffer(current.data, current.size, idRemoteBuffer);
                            renderhandle = idRemoteBuffer;
                        }
                        this._currentMessageHeap = null;
                        this._pendingBatchDeletes = null;
                        this._pendingBatchDeleteSlot = 0U;
                        this._pendingBatchGroupDeletes = null;
                        this._pendingBatchGroupDeleteSlot = 0U;
                    }
                    finally
                    {
                        if (this._currentMessageHeap != null)
                        {
                            this.DequeuePendingBatch(nextBatchId, true);
                            this.ReleaseHeap(this._currentMessageHeap);
                            this._currentMessageHeap = null;
                        }
                    }
                }
                else
                {
                    if (this._currentMessageHeap != null)
                    {
                        this.ReleaseHeap(this._currentMessageHeap);
                        this._currentMessageHeap = null;
                    }
                    if (deliveryCallback == null)
                        return;
                    deliveryCallback();
                }
            }
            finally
            {
                this._currentMessageHeap = null;
                this._batchPopulateCallback = null;
                this._lastBatchEntry = null;
                this._lastBatchBlockId = 0U;
                this._uBatchedMessages = 0U;
            }
        }

        private static unsafe void SwapBatchListByteOrder(MessageBatchHeader* pHeader)
        {
            byte* pMem1 = (byte*)pHeader;
            uint num = pHeader->uOffsetFirstEntry;
            MarshalHelper.SwapByteOrder(pMem1, ref s_ByteOrder_MessageBatchHeader, typeof(MessageBatchHeader), 0, 0);
            while (num != 0U)
            {
                byte* pMem2 = pMem1 + (int)num;
                num = ((MessageBatchEntry*)pMem2)->uOffsetNextEntry;
                MarshalHelper.SwapByteOrder(pMem2, ref s_ByteOrder_MessageBatchEntry, typeof(MessageBatchEntry), 0, 0);
            }
        }

        private void DeferredObjectReleaseWorker(object args) => this.FreeHandle((RENDERHANDLE)args);

        private void DeferredGroupReleaseWorker(object args) => this.FreeHandleGroup((RENDERGROUP)args);

        private void DeferredHeapReleaseWorker(object args) => this._spareHeapCache.Push((MessageHeap)args);

        private unsafe object HeapCacheCallback(ObjectCache.Operation operation, object data)
        {
            this.Session.AssertOwningThread();
            MessageHeap key = data as MessageHeap;
            switch (operation)
            {
                case ObjectCache.Operation.Alloc:
                    key = new MessageHeap(8U * (uint)Environment.SystemPageSize, (uint)sizeof(MessageBatchHeader), (uint)sizeof(MessageBatchEntry));
                    this._activeHeaps[key] = key;
                    break;
                case ObjectCache.Operation.Free:
                    this._activeHeaps.Remove(key);
                    key.Dispose();
                    key = null;
                    break;
                case ObjectCache.Operation.Freeze:
                    Debug2.Validate(key != null, typeof(ArgumentNullException), nameof(data));
                    key.Reset();
                    break;
            }
            return key;
        }

        internal unsafe void ProcessMessageBuffer(EngineApi.BufferInfo* pBufferInfo, void* pvBufferData)
        {
            if (this.ForeignByteOrder)
                MarshalHelper.SwapByteOrder((byte*)pvBufferData, ref s_ByteOrder_CallbackMessage, typeof(CallbackMessage), 0, 0);
            CallbackMessage* message = (CallbackMessage*)pvBufferData;
            this.DispatchCallbackMessage(RENDERHANDLE.ToUInt32(message->idObjectSubject), message);
        }

        internal RENDERHANDLE RegisterCallback(PortCallback callback, out uint idCallback)
        {
            idCallback = (uint)this._callbackHandlers.Add(callback);
            return RENDERHANDLE.FromUInt32(idCallback + 2U);
        }

        private unsafe void DispatchCallbackMessage(uint idCallback, CallbackMessage* message)
        {
            idCallback -= 2U;
            if (idCallback >= (uint)this._callbackHandlers.Count)
                return;
            PortCallback callbackHandler = (PortCallback)this._callbackHandlers[(int)idCallback];
            if (callbackHandler == null)
                return;
            IRenderHandleOwner owner = this;
            if (message->hTarget != RENDERHANDLE.NULL)
            {
                if (this._handleTable == null)
                    return;
                owner = this.TryGetHandleOwner(message->hTarget);
                if (owner == null)
                    return;
            }
            callbackHandler(this, owner, message);
        }

        void IRenderPortCallback.OnPingReply(RENDERHANDLE target)
        {
            if (this.PingReply == null)
                return;
            this.PingReply(this, EventArgs.Empty);
        }

        void IRenderPortCallback.OnBatchProcessed(
          RENDERHANDLE target,
          uint uBatchCompleted)
        {
            RenderPort.BatchSendInfo info = this.DequeuePendingBatch(uBatchCompleted, false);
            if (info == null)
                return;
            this.ProcessPendingDeletes(info.pendingDeletes);
            this.ProcessPendingGroupDeletes(info.pendingGroupDeletes);
            this.ReleaseHeap(info.heap);
            if (info.deliveryCallback != null)
                info.deliveryCallback();
            this.FreeBatchSendInfo(info);
        }

        private void AddPendingDelete(RENDERHANDLE handle)
        {
            uint num1 = 0;
            if (this._pendingBatchDeletes != null)
                num1 = (uint)this._pendingBatchDeletes.Length;
            if (this._pendingBatchDeleteSlot >= num1)
            {
                uint num2 = 2U * num1;
                if (num2 == 0U)
                    num2 = 16U;
                RENDERHANDLE[] renderhandleArray = new RENDERHANDLE[num2];
                if (num1 > 0U)
                    Array.Copy(_pendingBatchDeletes, renderhandleArray, this._pendingBatchDeletes.Length);
                this._pendingBatchDeletes = renderhandleArray;
            }
            this._pendingBatchDeletes[this._pendingBatchDeleteSlot] = handle;
            ++this._pendingBatchDeleteSlot;
        }

        private void ProcessPendingDeletes(RENDERHANDLE[] deleteList)
        {
            if (deleteList == null || this._handleTable == null)
                return;
            foreach (RENDERHANDLE delete in deleteList)
            {
                if (delete != RENDERHANDLE.NULL)
                    this._handleTable.Free(delete);
            }
        }

        private void AddPendingGroupDelete(RENDERGROUP group)
        {
            uint num1 = 0;
            if (this._pendingBatchGroupDeletes != null)
                num1 = (uint)this._pendingBatchGroupDeletes.Length;
            if (this._pendingBatchGroupDeleteSlot >= num1)
            {
                uint num2 = 2U * num1;
                if (num2 == 0U)
                    num2 = 16U;
                RENDERGROUP[] rendergroupArray = new RENDERGROUP[num2];
                if (num1 > 0U)
                    Array.Copy(_pendingBatchGroupDeletes, rendergroupArray, this._pendingBatchGroupDeletes.Length);
                this._pendingBatchGroupDeletes = rendergroupArray;
            }
            this._pendingBatchGroupDeletes[this._pendingBatchGroupDeleteSlot] = group;
            ++this._pendingBatchGroupDeleteSlot;
        }

        private void ProcessPendingGroupDeletes(RENDERGROUP[] deleteList)
        {
            if (deleteList == null || this._handleTable == null)
                return;
            foreach (RENDERGROUP delete in deleteList)
            {
                if (delete != RENDERGROUP.NULL)
                    this._handleTable.FreeGroup(delete);
            }
        }

        private RenderPort.BatchSendInfo AllocBatchSendInfo()
        {
            RenderPort.BatchSendInfo batchSendInfo;
            if (this._freeBatchSendInfos != null)
            {
                batchSendInfo = this._freeBatchSendInfos;
                this._freeBatchSendInfos = batchSendInfo.next;
                batchSendInfo.next = null;
                --this._numFreeBatchSendInfos;
            }
            else
                batchSendInfo = new RenderPort.BatchSendInfo();
            return batchSendInfo;
        }

        private void FreeBatchSendInfo(RenderPort.BatchSendInfo info)
        {
            info.Reset();
            if (this._numFreeBatchSendInfos >= 25U)
                return;
            info.next = this._freeBatchSendInfos;
            this._freeBatchSendInfos = info;
            ++this._numFreeBatchSendInfos;
        }

        private void EnqueuePendingBatch(RenderPort.BatchSendInfo info)
        {
            if (this._sentBatchesTail == null)
            {
                this._sentBatchesHead = info;
                this._sentBatchesTail = info;
            }
            else
            {
                this._sentBatchesTail.next = info;
                this._sentBatchesTail = info;
            }
        }

        private RenderPort.BatchSendInfo DequeuePendingBatch(uint batchID, bool fFreeInfo)
        {
            RenderPort.BatchSendInfo batchSendInfo = null;
            RenderPort.BatchSendInfo info;
            for (info = this._sentBatchesHead; info != null && (int)info.batchID != (int)batchID; info = info.next)
                batchSendInfo = info;
            if (info != null)
            {
                if (this._sentBatchesTail == info)
                    this._sentBatchesTail = batchSendInfo;
                if (batchSendInfo == null)
                    this._sentBatchesHead = info.next;
                else
                    batchSendInfo.next = info.next;
                info.next = null;
                if (fFreeInfo)
                {
                    this.FreeBatchSendInfo(info);
                    info = null;
                }
            }
            return info;
        }

        public static object SyncLock => s_SyncLock;

        private class BatchSendInfo
        {
            public uint batchID;
            public MessageHeap heap;
            public RENDERHANDLE[] pendingDeletes;
            public RENDERGROUP[] pendingGroupDeletes;
            public BatchCallback deliveryCallback;
            public RenderPort.BatchSendInfo next;

            public void Reset()
            {
                this.batchID = 0U;
                this.heap = null;
                this.pendingDeletes = null;
                this.pendingGroupDeletes = null;
                this.deliveryCallback = null;
                this.next = null;
            }
        }
    }
}
