internal override void HandleReceivedFragment(FragmentHeader header, ref Frame payload)
        {
            if (header.FragmentSeqNumber == _lastFragmentSeqNumberReceived)
            {
#if PRINTALL
                Trace.Print("Already received this fragment. Drop.");
#endif
                Frame.Release(ref payload);
                return;
            }

            _lastFragmentSeqNumberReceived = header.FragmentSeqNumber;

            if ((header.Type != FragmentType.ACK) || (header.MessageSeqNumber != _messageSeqNumber))
            {
                Frame.Release(ref payload);
                return;
            }

            FragmentationMessageTerminatedEventArgs messageTerminatedHandlerArgs = null;
            bool sendData = false;

            lock (_lock)
            {
                if (_currentState == FragmentationMessageState.Disposed)
                {
                    Frame.Release(ref payload);
                    return;
                }

                bool timerWasEnabled = _timerEnabled;
                if (_timerEnabled)
                {
                    _timer.UnregisterItem(this);
                    _timerEnabled = false;
                }

                _nbTimeoutWithoutAck = 0;
                if (header.Type == FragmentType.ACK)
                {
#if PRINTALL
                    Trace.Print("Received ACK for message " + header.MessageSeqNumber + " sduHandle=" + _sduHandle);
#endif
                    _nbFragmentsSentWithoutAck = 0;
                    BitArray fragmentMissingTable;
                    byte     lastFragmentSeqNumberReceivedByReceived = 255;
                    try
                    {
                        Fragment.DecodeAckPayload(ref payload, out lastFragmentSeqNumberReceivedByReceived, out fragmentMissingTable);
                        Frame.Release(ref payload);
                        if (fragmentMissingTable.Length == _successfullyTransmittedFragments.Length)
                        {
                            BitArray fragmentReceived = fragmentMissingTable.Clone();
                            fragmentReceived.Not();
#if PRINTALL
                            string msg = "";
                            for (int i = 0; i < fragmentReceived.Length; i++)
                            {
                                if (fragmentReceived.Get(i))
                                {
                                    msg += i + ",";
                                }
                            }

                            Trace.Print("received so far: " + msg);
#endif
                            _successfullyTransmittedFragments.Or(fragmentReceived);
                            if (_successfullyTransmittedFragments.CheckAllSet(true))
                            {
                                _allFragmentTransmitted = true;
                                _currentState           = FragmentationMessageState.Final;
                            }

                            // now check if we received  ACK for the last fragment that we sent.
                            if (lastFragmentSeqNumberReceivedByReceived == _lastFragmentSeqNumberSent)
                            {
                                long roundTripTime = (DateTime.Now.Ticks - _timeLastFragmentSent.Ticks) / TimeSpan.TicksPerMillisecond;
                                if (2 * roundTripTime <= (long)uint.MaxValue)
                                {
                                    _timeoutForResending = (uint)(2 * roundTripTime);
                                }

                                _maxFragmentsBeforeAck++;
                            }

                            _lastFragmentSent.SetAll(false);
                        }
                    }
                    catch (ArgumentException)
                    {
                        // could not decode payload.
                        if (timerWasEnabled)
                        {
                            _timer.RegisterItem(_timeoutTicks, this);
                            _timerEnabled = true;
                        }

                        Frame.Release(ref payload);
                        return;
                    }

                    if (_allFragmentTransmitted)
                    {
                        _currentState = FragmentationMessageState.Final;
                        messageTerminatedHandlerArgs = new FragmentationMessageTerminatedEventArgs(_currentState, Status.Success);
                    }
                    else
                    {
                        sendData = true;
                    }
                }
                else
                {
#if DEBUG
                    Trace.Print("Received bad fragment type for message " + header.MessageSeqNumber + " sdhHandle=" + _sduHandle);
#endif
                    Frame.Release(ref payload);
                    if (timerWasEnabled)
                    {
                        _timer.RegisterItem(_timeoutTicks, this);
                        _timerEnabled = true;
                    }
                }
            }

            // release the lock before calling OnFragmentationMessageFinalState since upper layer handler is called.
            if (messageTerminatedHandlerArgs != null)
            {
                OnFragmentationMessageTerminated(messageTerminatedHandlerArgs);
            }
            else if (sendData)
            {
                SendNextDataFragment(false);
            }
        }
        /// <summary>
        /// Start sending segments.
        /// <param name="force">send a fragment in all case if true, do not send if no fragment pending</param>
        /// </summary>
        override internal void SendNextDataFragment(bool force)
        {
            Frame  fragment;
            ushort target;
            byte   pduHandle;

            lock (_lock)
            {
                if (_timerEnabled)
                {
                    _timer.UnregisterItem(this);
                    _timerEnabled = false;
                }

                if ((_currentState == FragmentationMessageState.Disposed) || (_allFragmentTransmitted))
                {
                    return;
                }

                // for broadcast message, we send fragment one by one to make sure that they are properly sent.
                byte indexOfFragmentToSend = 0;
                bool fragmentFound         = false;
                bool lastFragmentToBeSent  = true;
                for (int i = 0; i < _endingIndices.Length; i++)
                {
                    if (!_successfullyTransmittedFragments.Get(i) && !_lastFragmentSent.Get(i))
                    {
                        if (fragmentFound)
                        {
                            lastFragmentToBeSent = false;
                            break;
                        }

                        indexOfFragmentToSend = (byte)i;
                        fragmentFound         = true;
                    }
                }

                bool alreadySentAll = false;
                if (!fragmentFound)
                {
                    if (force)
                    {
                        // no segment to send. Resent last one.
                        alreadySentAll        = true;
                        indexOfFragmentToSend = (byte)(_endingIndices.Length - 1);
                    }
                    else
                    {
                        // no fragment to send. Wait for ack.
                        _timeoutTicks = DateTime.Now.Ticks + _timeoutForResending * TimeSpan.TicksPerMillisecond;
                        _timerId      = _timer.RegisterItem(_timeoutTicks, this);
                        _timerEnabled = true;
                        _currentState = FragmentationMessageState.WaitingAck;
                        return;
                    }
                }

                bool requireAck        = false;
                byte fragmentSeqNumber = ++_lastFragmentSeqNumberSent;

                if ((!_isBroadcast) && (lastFragmentToBeSent || (_nbFragmentsSentWithoutAck + 1 >= _maxFragmentsBeforeAck) || alreadySentAll))
                {
                    requireAck = true;
                }

                int startIndexInSdu = (indexOfFragmentToSend == 0) ? 0 : _endingIndices[indexOfFragmentToSend - 1] + 1;
                int endIndexInSdu   = _endingIndices[indexOfFragmentToSend];

                fragment = Fragment.CreateDataFrame(_messageSeqNumber, fragmentSeqNumber, _isBroadcast, requireAck,
                                                    _maxPduSize, _reservedTailSize, _reservedHeaderSize,
                                                    _nbFragments, indexOfFragmentToSend, _sdu, startIndexInSdu, endIndexInSdu - startIndexInSdu + 1);

                _nbFragmentsSentWithoutAck++;
                _nbFragmentsTransmittedLastSendTransition = 0;
                _lastFragmentSent.Set(indexOfFragmentToSend, true);
#if PRINTALL
                Trace.Print("Sending part  " + indexOfFragmentToSend + "/" + _endingIndices.Length.ToString() + " of packet " + _messageSeqNumber);
#endif
                _currentState = FragmentationMessageState.WaitingSentStatus;

                pduHandle = _pduHandle++;
                target    = _destination;
                _nbTransmissions++;
                _nbFragmentsTransmittedLastSendTransition++;

                // already set time of last fragment sent, but update when receive sent confirmation.
                // this prevent the situation where the ACK is actually handled before the send confirmation
                _timeLastFragmentSent = DateTime.Now;
            }

            // release lock before calling lower layer handler.
            _lowerLayerRequestHandler(target, ref fragment, pduHandle, this.HandleSendConfirmation);
            Frame.Release(ref fragment);
            return;
        }