// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT

package nack

import (
	
	
	

	
	
	
)

// GeneratorInterceptorFactory is a interceptor.Factory for a GeneratorInterceptor.
type GeneratorInterceptorFactory struct {
	opts []GeneratorOption
}

// NewInterceptor constructs a new ReceiverInterceptor.
func ( *GeneratorInterceptorFactory) ( string) (interceptor.Interceptor, error) {
	 := &GeneratorInterceptor{
		streamsFilter:     streamSupportNack,
		size:              512,
		skipLastN:         0,
		maxNacksPerPacket: 0,
		interval:          time.Millisecond * 100,
		receiveLogs:       map[uint32]*receiveLog{},
		nackCountLogs:     map[uint32]map[uint16]uint16{},
		close:             make(chan struct{}),
		log:               logging.NewDefaultLoggerFactory().NewLogger("nack_generator"),
	}

	for ,  := range .opts {
		if  := ();  != nil {
			return nil, 
		}
	}

	if ,  := newReceiveLog(.size);  != nil {
		return nil, 
	}

	return , nil
}

// GeneratorInterceptor interceptor generates nack feedback messages.
type GeneratorInterceptor struct {
	interceptor.NoOp
	streamsFilter     func(info *interceptor.StreamInfo) bool
	size              uint16
	skipLastN         uint16
	maxNacksPerPacket uint16
	interval          time.Duration
	m                 sync.Mutex
	wg                sync.WaitGroup
	close             chan struct{}
	log               logging.LeveledLogger
	nackCountLogs     map[uint32]map[uint16]uint16

	receiveLogs   map[uint32]*receiveLog
	receiveLogsMu sync.Mutex
}

// NewGeneratorInterceptor returns a new GeneratorInterceptorFactory.
func ( ...GeneratorOption) (*GeneratorInterceptorFactory, error) {
	return &GeneratorInterceptorFactory{}, nil
}

// BindRTCPWriter lets you modify any outgoing RTCP packets. It is called once per PeerConnection.
// The returned method will be called once per packet batch.
func ( *GeneratorInterceptor) ( interceptor.RTCPWriter) interceptor.RTCPWriter {
	.m.Lock()
	defer .m.Unlock()

	if .isClosed() {
		return 
	}

	.wg.Add(1)

	go .loop()

	return 
}

// BindRemoteStream lets you modify any incoming RTP packets. It is called once for per RemoteStream.
// The returned method will be called once per rtp packet.
func ( *GeneratorInterceptor) (
	 *interceptor.StreamInfo,  interceptor.RTPReader,
) interceptor.RTPReader {
	if !.streamsFilter() {
		return 
	}

	// error is already checked in NewGeneratorInterceptor
	,  := newReceiveLog(.size)
	.receiveLogsMu.Lock()
	.receiveLogs[.SSRC] = 
	.receiveLogsMu.Unlock()

	return interceptor.RTPReaderFunc(func( []byte,  interceptor.Attributes) (int, interceptor.Attributes, error) {
		, ,  := .Read(, )
		if  != nil {
			return 0, nil, 
		}

		if  == nil {
			 = make(interceptor.Attributes)
		}
		,  := .GetRTPHeader([:])
		if  != nil {
			return 0, nil, 
		}
		.add(.SequenceNumber)

		return , , nil
	})
}

// UnbindRemoteStream is called when the Stream is removed. It can be used to clean up any data related to that track.
func ( *GeneratorInterceptor) ( *interceptor.StreamInfo) {
	.receiveLogsMu.Lock()
	delete(.receiveLogs, .SSRC)
	.receiveLogsMu.Unlock()
}

// Close closes the interceptor.
func ( *GeneratorInterceptor) () error {
	defer .wg.Wait()
	.m.Lock()
	defer .m.Unlock()

	if !.isClosed() {
		close(.close)
	}

	return nil
}

// nolint:gocognit,cyclop
func ( *GeneratorInterceptor) ( interceptor.RTCPWriter) {
	defer .wg.Done()

	 := rand.Uint32() // #nosec

	 := make([]uint16, .size)
	 := make([]uint16, .size)

	 := time.NewTicker(.interval)
	defer .Stop()
	for {
		select {
		case <-.C:
			func() {
				.receiveLogsMu.Lock()
				defer .receiveLogsMu.Unlock()

				for ,  := range .receiveLogs {
					 := .missingSeqNumbers(.skipLastN, )

					if len() == 0 || .nackCountLogs[] == nil {
						.nackCountLogs[] = map[uint16]uint16{}
					}
					if len() == 0 {
						continue
					}

					 := &rtcp.TransportLayerNack{} // nolint:ineffassign,wastedassign

					 := 0 // nolint:varnamelen,
					if .maxNacksPerPacket > 0 {
						for ,  := range  {
							if .nackCountLogs[][] < .maxNacksPerPacket {
								[] = 
								++
							}
							.nackCountLogs[][]++
						}

						if  == 0 {
							continue
						}

						 = &rtcp.TransportLayerNack{
							SenderSSRC: ,
							MediaSSRC:  ,
							Nacks:      rtcp.NackPairsFromSequenceNumbers([:]),
						}
					} else {
						 = &rtcp.TransportLayerNack{
							SenderSSRC: ,
							MediaSSRC:  ,
							Nacks:      rtcp.NackPairsFromSequenceNumbers(),
						}
					}

					for  := range .nackCountLogs[] {
						 := false
						for ,  := range  {
							if  ==  {
								 = true

								break
							}
						}
						if ! {
							delete(.nackCountLogs[], )
						}
					}

					if ,  := .Write([]rtcp.Packet{}, interceptor.Attributes{});  != nil {
						.log.Warnf("failed sending nack: %+v", )
					}
				}
			}()
		case <-.close:
			return
		}
	}
}

func ( *GeneratorInterceptor) () bool {
	select {
	case <-.close:
		return true
	default:
		return false
	}
}