/// <summary>
        /// returns true if the TcpStreamMonitor timed out
        /// </summary>
        /// <returns>
        /// A <see cref="System.Boolean"/>
        /// </returns>
        public bool CheckForTimeout()
        {
            TimeSpan ts = DateTime.Now - last_continuous_packet_received;

            if (ts.TotalSeconds > timeout.TotalSeconds)
            {
                // Monitor has timed out, notify the user of the monitor via the callback
                lock (this)
                {
                    condition = TcpStreamGenerator.CallbackCondition.ConnectionTimeout;
                }

                return(true);
            }

            return(false);
        }
        /// <summary>
        /// Create a TcpStreamGenerator given a flow
        /// </summary>
        /// <param name="f">
        /// A <see cref="TcpFlow"/>
        /// </param>
        /// <param name="timeout">
        /// A <see cref="TimeSpan"/>
        /// </param>
        /// <param name="sizeLimitInBytes">
        /// A <see cref="System.Nullable<System.Int64>"/>
        /// </param>
        public TcpStreamGenerator(TcpFlow f,
                                  TimeSpan timeout,
                                  long?sizeLimitInBytes)
        {
            this.flow = f;
            this.flow.OnPacketReceived += HandleFlowOnPacketReceived;
            this.timeout          = timeout;
            this.sizeLimitInBytes = sizeLimitInBytes;

            packets = new LinkedList <PacketDotNet.TcpPacket>();

            tcpStream              = new TcpStream();
            condition              = TcpStreamGenerator.CallbackCondition.NextInSequence;
            waiting_for_start_seq  = true;
            LastContinuousSequence = 0;
            last_overall_seq       = 0;

            // use now as our last packet received time to avoid
            // timing this monitor out immediately
            last_continuous_packet_received = DateTime.Now;
        }
        //FIXME: we don't handle rolling sequence numbers properly in here
        // so after 4GB of packets we will have issues
        private void InternalAddPacket(PacketDotNet.TcpPacket newPacket)
        {
#if PERFORMANCE_CRITICAL_LOGGING
            log.Debug("");
#endif

            UInt32 new_packet_seq = newPacket.SequenceNumber;

            // if we haven't set the value of start_seq we should do so now
            if (waiting_for_start_seq)
            {
#if PERFORMANCE_CRITICAL_LOGGING
                log.Debug("waiting_for_start_seq");
#endif
                // NOTE: we always use the sequence value because we won't have a new packet
                // added unless it matches the direction we are monitoring, see tcp_stream_manager::handle()
                start_seq              = new_packet_seq;
                waiting_for_start_seq  = false;
                LastContinuousSequence = last_overall_seq = new_packet_seq;
            }

            // Adjust sequence number against the start sequence number
#if PERFORMANCE_CRITICAL_LOGGING
            log.DebugFormat("new_packet.ip.tcp.get_seq() is {0}, start_seq is {1}",
                            newPacket.SequenceNumber, start_seq);
#endif

            UInt32 new_packet_size = (UInt32)newPacket.PayloadData.Length;

#if PERFORMANCE_CRITICAL_LOGGING
            log.DebugFormat("new_packet_seq: {0}, new_packet_size: {1}," +
                            " lastContinuousSequence: {2}, last_overall_seq: {3}",
                            new_packet_seq, new_packet_size,
                            LastContinuousSequence, last_overall_seq);
#endif

            // if the packet came before the last continuous sequence
            // number we've already seen the packet
            if (new_packet_seq < LastContinuousSequence)
            {
                log.DebugFormat("new_packet_seq({0}) < lastContinuousSequence({1}), rejecting packet",
                                new_packet_seq, LastContinuousSequence);
                return;
            }

            // Verify that the packet within our allowed sequence range
            if (SizeLimit.HasValue)
            {
                if ((new_packet_seq - start_seq) > SizeLimit)
                {
                    log.DebugFormat("OutOfRange, new_packet_seq({0}) > SizeLimit({1}), deleting packet",
                                    new_packet_seq, SizeLimit);
                    condition = CallbackCondition.OutOfRange;
                    return;
                }
            }

            bool packet_added = false;
            LinkedListNode <PacketDotNet.TcpPacket> pNode;

            // try to place this packet in our pending packets list
            if (packets.Count != 0)
            {
                pNode = packets.Last;
                do
                {
                    if (pNode == null)
                    {
                        break;
                    }

                    var    p = pNode.Value;
                    UInt32 current_packet_seq  = (UInt32)p.SequenceNumber;
                    UInt32 current_packet_size = (UInt32)p.PayloadData.Length;

#if PERFORMANCE_CRITICAL_LOGGING
                    log.DebugFormat("current_packet_seq: {0}, current_packet_size: {1}",
                                    current_packet_seq, current_packet_size);
#endif

                    // Does this packet belong after, but not *immediately* after current?
                    if (new_packet_seq > (current_packet_seq + current_packet_size))
                    {
#if PERFORMANCE_CRITICAL_LOGGING
                        log.Debug("OutOfSequence, adding packet");
#endif

                        // Sanity check passed, insert into vector
                        condition = CallbackCondition.OutOfSequence;
                        if (last_overall_seq < (new_packet_seq + new_packet_size))
                        {
                            last_overall_seq = new_packet_seq + new_packet_size;
                        }

                        packets.AddAfter(pNode, newPacket);
                        return; // adding an out-of-sequence packet won't put any other
                                // packets back in sequence, so we are done
                    }

                    // Does this packet fit exactly in sequence after current?
                    if (new_packet_seq == (current_packet_seq + current_packet_size))
                    {
#if PERFORMANCE_CRITICAL_LOGGING
                        log.Debug("packet fits into sequence, adding it");
#endif

                        // Verify highest sequence number
                        if ((new_packet_seq + new_packet_size) > last_overall_seq)
                        {
                            last_overall_seq = new_packet_seq + new_packet_size;
                        }

                        packets.AddAfter(pNode, newPacket);
                        packet_added = true;

                        break;
                    }

                    // if the sequence numbers match there is a packet overlap
                    if (new_packet_seq == current_packet_seq)
                    {
                        if (new_packet_size == current_packet_size)
                        {
#if PERFORMANCE_CRITICAL_LOGGING
                            log.Debug("DuplicateDropped");
#endif
                            condition = CallbackCondition.DuplicateDropped; // Duplicate packet, drop it
                            return;
                        }
                        else
                        {
#if PERFORMANCE_CRITICAL_LOGGING
                            log.Debug("StreamError, packet would overlap previous packet and is different size");
#endif
                            condition = CallbackCondition.StreamError; // Error: Packet would overlap
                            return;
                        }
                    }

                    // move to the previous node
                    pNode = pNode.Previous;
                } while(packets.Count != 0);
            }

            // Packet did not fall between the existing packets or after all of them,
            // so it should belong before them all.
            if (!packet_added)
            {
#if PERFORMANCE_CRITICAL_LOGGING
                log.Debug("!packet_added, packet did not fall between existing packets or after them, adding packet at front");
#endif
                packets.AddFirst(newPacket);
                packet_added = true;
            }


            //
            // process our pending packets list
            // pull all sequential packets out of the packets list and
            // push them into the TcpStream
            //
            // retrieve the last packet
            // if there is no last packet then break out of the do {} while()
            pNode = packets.First;
            do
            {
                if (pNode == null)
                {
#if PERFORMANCE_CRITICAL_LOGGING
                    log.Debug("pNode == null");
#endif
                    break;
                }

                var p    = pNode.Value;
                var seq  = p.SequenceNumber;
                var size = (UInt32)p.PayloadData.Length;

                // if the size is zero and this is a syn packet then
                // we need to adjust the size to be 1 according to the tcp protocol
                if (newPacket.Synchronize)
                {
                    size = 1;
                    LastContinuousSequence = seq;

                    log.DebugFormat("syn packet, lastContinousSequence updated to {0}",
                                    LastContinuousSequence);
                }

#if PERFORMANCE_CRITICAL_LOGGING
                log.DebugFormat("seq: {0}, size: {1}", seq, size);
#endif

                if (seq == LastContinuousSequence)
                {
                    if (SizeLimit.HasValue)
                    {
                        // is this packet beyond our range?
                        if ((seq - start_seq) >= SizeLimit + 1)
                        {
                            log.Debug("dropping beyond range packet");

                            // just drop this packet and loop
                            packets.Remove(pNode);
                            pNode = pNode.Next;
                            continue; // skip back around to the top of the do {} while()
                        }
                    }

                    condition = CallbackCondition.NextInSequence;

                    last_continuous_packet_received = DateTime.Now;

                    // store the packet and remove the node from the packets linked list
#if PERFORMANCE_CRITICAL_LOGGING
                    log.Debug("AppendPacket() to tcpStream");
#endif
                    tcpStream.AppendPacket(p);
                    packets.Remove(pNode);

                    // is this packet the next in line? if so update our
                    // last continuous sequence value
                    if (seq == LastContinuousSequence)
                    {
                        LastContinuousSequence += size;
                    }

                    // is the last continuous sequence after the current value?
                    // if our last overall sequence is behind, update it as well
                    if (last_overall_seq < LastContinuousSequence)
                    {
                        last_overall_seq = LastContinuousSequence;
                    }
                }
                else
                {
#if PERFORMANCE_CRITICAL_LOGGING
                    log.DebugFormat("seq {0}  != LastContinuousSequence() {1}",
                                    seq, LastContinuousSequence);
#endif
                    break;
                }

                pNode = pNode.Next;
            } while(packets.Count != 0);
        }