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

package report

import (
	
	

	
	
	
)

// ReceiverInterceptorFactory is a interceptor.Factory for a ReceiverInterceptor.
type ReceiverInterceptorFactory struct {
	opts []ReceiverOption
}

// NewInterceptor constructs a new ReceiverInterceptor.
func ( *ReceiverInterceptorFactory) ( string) (interceptor.Interceptor, error) {
	 := &ReceiverInterceptor{
		interval: 1 * time.Second,
		now:      time.Now,
		log:      logging.NewDefaultLoggerFactory().NewLogger("receiver_interceptor"),
		close:    make(chan struct{}),
	}

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

	return , nil
}

// NewReceiverInterceptor returns a new ReceiverInterceptorFactory.
func ( ...ReceiverOption) (*ReceiverInterceptorFactory, error) {
	return &ReceiverInterceptorFactory{}, nil
}

// ReceiverInterceptor interceptor generates receiver reports.
type ReceiverInterceptor struct {
	interceptor.NoOp
	interval time.Duration
	now      func() time.Time
	streams  sync.Map
	log      logging.LeveledLogger
	m        sync.Mutex
	wg       sync.WaitGroup
	close    chan struct{}
}

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

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

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

	return 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 ( *ReceiverInterceptor) ( interceptor.RTCPWriter) interceptor.RTCPWriter {
	.m.Lock()
	defer .m.Unlock()

	if .isClosed() {
		return 
	}

	.wg.Add(1)

	go .loop()

	return 
}

func ( *ReceiverInterceptor) ( interceptor.RTCPWriter) {
	defer .wg.Done()

	 := time.NewTicker(.interval)
	defer .Stop()
	for {
		select {
		case <-.C:
			 := .now()
			.streams.Range(func(,  interface{}) bool {
				if ,  := .(*receiverStream); ! {
					.log.Warnf("failed to cast ReceiverInterceptor stream")
				} else if ,  := .Write(
					[]rtcp.Packet{.generateReport()}, interceptor.Attributes{},
				);  != nil {
					.log.Warnf("failed sending: %+v", )
				}

				return true
			})

		case <-.close:
			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 ( *ReceiverInterceptor) (
	 *interceptor.StreamInfo,  interceptor.RTPReader,
) interceptor.RTPReader {
	 := newReceiverStream(.SSRC, .ClockRate)
	.streams.Store(.SSRC, )

	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, 
		}

		.processRTP(.now(), )

		return , , nil
	})
}

// UnbindRemoteStream is called when the Stream is removed. It can be used to clean up any data related to that track.
func ( *ReceiverInterceptor) ( *interceptor.StreamInfo) {
	.streams.Delete(.SSRC)
}

// BindRTCPReader lets you modify any incoming RTCP packets. It is called once per sender/receiver, however this might
// change in the future. The returned method will be called once per packet batch.
func ( *ReceiverInterceptor) ( interceptor.RTCPReader) interceptor.RTCPReader {
	return interceptor.RTCPReaderFunc(func( []byte,  interceptor.Attributes) (int, interceptor.Attributes, error) {
		, ,  := .Read(, )
		if  != nil {
			return 0, nil, 
		}

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

		for ,  := range  {
			if ,  := ().(*rtcp.SenderReport);  {
				,  := .streams.Load(.SSRC)
				if ! {
					continue
				}

				if ,  := .(*receiverStream); ! {
					.log.Warnf("failed to cast ReceiverInterceptor stream")
				} else {
					.processSenderReport(.now(), )
				}
			}
		}

		return , , nil
	})
}