/// <summary>
 /// AckFlag must start at SeqLastNotified + 1
 /// All seqs in flag will be notified
 /// </summary>
 /// <param name="flag"></param>
 private void GenerateNotificationsFromAckFlagAndUpdateSeqLastNotified(SeqAckFlag flag)
 {
     // Notify based on the flag
     for (int seqBitPos = 0; seqBitPos < flag.SeqCount; seqBitPos++)
     {
         PacketTransmissionRecord record = _transmissionRecords.Dequeue();
         if (flag.IsAck(seqBitPos))
         {
             OnAckedTransmission(record);
         }
         _transmissionNotifications.Add(flag.IsAck(seqBitPos));
         _seqLastNotified = ++_seqLastNotified <= byte.MaxValue ? _seqLastNotified : 0;
     }
 }
        public void UpdateOutgoing(bool host = false)
        {
            _log.Debug($"UpdateOutgoing() - Frame: {Time.frameCount} Seq: {_seq}\n");

            // flow control
            if (_transmissionRecords.Count >= 32)
            {
                _log.Debug("Skipped sending outgoing data. Max in un-ACKed transmissions reached");
                return;
            }

            // fill in our reusable packet struct with the data for this sequence
            _header.Seq     = _seq;
            _header.AckFlag = _remoteSeqAckFlag;

            _log.Debug($"Generated Packet Seq: {_header.Seq}");
            _log.Debug($"RemoteAckFlag: {_remoteSeqAckFlag}");

            // Create a transmission record for the packet
            PacketTransmissionRecord record = new PacketTransmissionRecord
            {
                Seq     = _header.Seq,
                AckFlag = _remoteSeqAckFlag
            };

            // Write the packet header to the stream
            _header.Serialize(_netWriter);

            // let each stream manager write until the packet is full
            foreach (IPacketStreamWriter streamWriter in _streamWriters)
            {
                streamWriter.WriteToPacketStream(_netWriter);
            }

            // create output events
            _transmissionRecords.Enqueue(record);

            _packetSender.Send(_netWriter.Data, 0, _netWriter.Length);

            _log.Debug($"Sent Bytes: {_netWriter.Length}");

#if SQUIGGLE
            DebugGraph.Log("Sent Bytes", _netWriter.Length, Color.green);
#endif
            _netWriter.Reset();

            // Only increment our seq on successful send
            // IE if waiting for acks then seq doesn't increase
            _seq++;
        }
        private void OnAckedTransmission(PacketTransmissionRecord record)
        {
            // Drop the start of our current ack flag since we know that the remote stream
            // knows about the sequence that it represents
            // I think this covers all edge cases?
            if (record.AckFlag.SeqCount <= 0)
            {
                return;
            }

            if (_remoteSeqAckFlag.StartSeq == record.AckFlag.EndSeq)
            {
                _remoteSeqAckFlag.DropStartSequence();
            }
            else if (SequenceHelper.SeqIsAheadButInsideWindow32(_remoteSeqAckFlag.StartSeq, record.AckFlag.EndSeq))
            {
                _remoteSeqAckFlag.DropStartSequenceUntilItEquals((byte)(record.AckFlag.EndSeq + 1));
            }
        }
        public void UpdateIncoming(bool host = false)
        {
            if (_dataReceivedEvents.Count == 0)
            {
                return;
            }

            _log.Debug($"UPDATE INCOMING - Frame: {Time.frameCount} Seq: {_seq}\n");

            DebugGraph.Log("Packets Received", _dataReceivedEvents.Count);

            try
            {
                // Process data received events
                _log.Debug($"Received {_dataReceivedEvents.Count} packets");
                for (int i = 0; i < _dataReceivedEvents.Count; i++)
                {
                    // Get data reader from evt which contains the binary data for the packet
                    NetPacketReader reader = _dataReceivedEvents[i].DataReader;

                    // Deserialize packet (including header)
                    _header.Deserialize(reader);

                    _log.Debug($"Received Packet Sequence: {_header.Seq}");
                    _log.Debug($"Packet AckFlag: {_header.AckFlag}");
                    _log.Debug($"Local AckedFlag- before: {_remoteSeqAckFlag}");

                    if (_remoteSeqAckFlag.SeqCount == 0)
                    {
                        // No sequences in the flag so just initialize it with this sequence being ACKed
                        _remoteSeqAckFlag.InitWithAckedSequence(_header.Seq);
                    }
                    else if (SequenceHelper.SeqIsAheadButInsideWindow32(_remoteSeqAckFlag.EndSeq, _header.Seq))
                    {
                        _log.Debug($"Received sequence {_header.Seq} is ahead of the last sequence in our ack flag: {_remoteSeqAckFlag.EndSeq}");

                        // The seq is ahead of the range of our flag (ie a new seq) but we want to NACK any that
                        // sequences that are in between the last sequence we ACKed, and this sequence we are now receiving
                        // since they must have been dropped (or delivered out of order)
                        while (_remoteSeqAckFlag.EndSeq != (byte)(_header.Seq - 1))
                        {
                            _remoteSeqAckFlag.NackNextSequence();
                            _log.Debug($"NACKed sequence {_remoteSeqAckFlag.EndSeq}");
                        }

                        // Ack this sequence in our flag
                        _remoteSeqAckFlag.AckNextSequence();
                    }
                    else
                    {
                        // This packet was delivered out of order
                        // or is outside the expected window so don't process it further
                        _log.Debug($"{_header.Seq} - SKIPPED UNEXPECTED");
                        continue;
                    }

                    // Generate notifications based on ACKs received from the remote stream
                    if (_header.AckFlag.SeqCount > 0)
                    {
                        if (_seqLastNotified == -1)
                        {
                            _log.Debug("Initializing SeqLastNotified");
                            // This is the start of us notifying packets.. if any packets were sent but aren't
                            // included in this ack flag then they must have been dropped
                            while (_transmissionRecords.Peek().Seq != _header.AckFlag.StartSeq)
                            {
                                PacketTransmissionRecord record = _transmissionRecords.Dequeue();
                                _seqLastNotified = record.Seq;
                                _transmissionNotifications.Add(false);
                                _log.Debug($"Seq {record.Seq} was dropped");
                            }
                            // Notify based on sequences in flag
                            GenerateNotificationsFromAckFlagAndUpdateSeqLastNotified(_header.AckFlag);
                        }
                        else if (SequenceHelper.SeqIsAheadButInsideWindow32((byte)_seqLastNotified, _header.AckFlag.StartSeq))
                        {
                            // NACK all packets up until the start of this ACK flag because they must have been lost or delivered out of order
                            while (_seqLastNotified != (byte)(_header.AckFlag.StartSeq - 1))
                            {
                                _transmissionRecords.Dequeue();
                                _transmissionNotifications.Add(false);
                                _seqLastNotified = ++_seqLastNotified <= byte.MaxValue ? _seqLastNotified : 0;
                                _log.Debug($"Sequence: {_seqLastNotified} was dropped");
                            }
                            // Notify based on sequences in flag
                            GenerateNotificationsFromAckFlagAndUpdateSeqLastNotified(_header.AckFlag);
                        }
                        else if (SequenceHelper.SeqIsInsideRangeInclusive(_header.AckFlag.StartSeq, _header.AckFlag.EndSeq, (byte)_seqLastNotified))
                        {
                            _log.Debug($"{_seqLastNotified} is inside ack flag range");
                            // Drop sequences we have already notified
                            _header.AckFlag.DropStartSequenceUntilItEquals((byte)(_seqLastNotified + 1));

                            // Notify based on sequences remaining in flag
                            GenerateNotificationsFromAckFlagAndUpdateSeqLastNotified(_header.AckFlag);
                        }
                    }

                    _log.Debug("Finished generating notifications");

                    // Give stream to each system to processors in the order they were added
                    foreach (IPacketStreamReader streamProcessor in _streamProcessors)
                    {
                        streamProcessor.ReadPacketStream(reader);
                    }
                }
                _log.Debug($"SeqLastNotified - After: {_seqLastNotified}");
                _log.Debug($"Generated {_transmissionNotifications.Count} transmission notifications");
                _log.Debug($"There are now { _transmissionRecords.Count} remaining transmission records in the queue");

                // Give notifications to any stream writers that are interested in whether or not their transmissions made it
                foreach (IPacketTransmissionNotificationReceiver notificationReceiver in _notificationReceivers)
                {
                    notificationReceiver.ReceiveNotifications(_transmissionNotifications);
                }
            }
            catch (Exception e)
            {
                _log.Debug(e.Message);
                throw;
            }
            finally
            {
                _dataReceivedEvents.Clear();
                _transmissionNotifications.Clear();
            }
        }