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

package sctp

import (
	
	
)

type receivePayloadQueue struct {
	tailTSN      uint32
	chunkSize    int
	tsnBitmask   []uint64
	dupTSN       []uint32
	maxTSNOffset uint32

	cumulativeTSN uint32
}

func newReceivePayloadQueue( uint32) *receivePayloadQueue {
	 = (( + 63) / 64) * 64

	return &receivePayloadQueue{
		tsnBitmask:   make([]uint64, /64),
		maxTSNOffset: ,
	}
}

func ( *receivePayloadQueue) ( uint32) {
	.cumulativeTSN = 
	.tailTSN = 
	.chunkSize = 0
	for  := range .tsnBitmask {
		.tsnBitmask[] = 0
	}
	.dupTSN = .dupTSN[:0]
}

func ( *receivePayloadQueue) ( uint32) bool {
	if .chunkSize == 0 || sna32LTE(, .cumulativeTSN) || sna32GT(, .tailTSN) {
		return false
	}

	,  := int(/64)%len(.tsnBitmask), %64

	return .tsnBitmask[]&(1<<) != 0
}

func ( *receivePayloadQueue) ( uint32) bool {
	 := .hasChunk()
	if  || sna32LTE(, .cumulativeTSN) || sna32GT(, .cumulativeTSN+.maxTSNOffset) {
		return false
	}

	return true
}

// push pushes a payload data. If the payload data is already in our queue or
// older than our cumulativeTSN marker, it will be recored as duplications,
// which can later be retrieved using popDuplicates.
func ( *receivePayloadQueue) ( uint32) bool {
	if sna32GT(, .cumulativeTSN+.maxTSNOffset) {
		return false
	}

	if sna32LTE(, .cumulativeTSN) || .hasChunk() {
		// Found the packet, log in dups
		.dupTSN = append(.dupTSN, )

		return false
	}

	,  := int(/64)%len(.tsnBitmask), %64
	.tsnBitmask[] |= (1 << )
	.chunkSize++
	if sna32GT(, .tailTSN) {
		.tailTSN = 
	}

	return true
}

// pop advances cumulativeTSN and pops the oldest chunk's TSN if it matches the given TSN or force is true.
func ( *receivePayloadQueue) ( bool) bool {
	 := .cumulativeTSN + 1
	if .hasChunk() {
		,  := int(/64)%len(.tsnBitmask), int(%64)
		.tsnBitmask[] &= ^uint64(1 << ())
		.chunkSize--
		.cumulativeTSN++

		return true
	}
	if  {
		.cumulativeTSN++
		if .chunkSize == 0 {
			.tailTSN = .cumulativeTSN
		}
	}

	return false
}

// popDuplicates returns an array of TSN values that were found duplicate.
func ( *receivePayloadQueue) () []uint32 {
	 := .dupTSN
	.dupTSN = []uint32{}

	return 
}

func ( *receivePayloadQueue) () ( []gapAckBlock) {
	var  gapAckBlock

	if .chunkSize == 0 {
		return nil
	}

	,  := .cumulativeTSN+1, .tailTSN
	var  bool
	for  := ; sna32LTE(, ); {
		,  := int(/64)%len(.tsnBitmask), int(%64)
		if ! { //nolint:nestif
			// find first received tsn as start
			if ,  := getFirstNonZeroBit(.tsnBitmask[], , 64);  {
				//nolint:gosec // G115
				.start = uint16( + uint32(-) - .cumulativeTSN)
				 += uint32( - ) //nolint:gosec // G115
				 = true
			} else {
				// no result, find start bits in next uint64 bitmask
				 += uint32(64 - ) //nolint:gosec // G115
			}
		} else {
			if ,  := getFirstZeroBit(.tsnBitmask[], , 64);  {
				//nolint:gosec // G115
				.end = uint16( + uint32(-) - 1 - .cumulativeTSN)
				 += uint32( - ) //nolint:gosec // G115
				if sna32LTE(, ) {
					 = append(, gapAckBlock{
						start: .start,
						end:   .end,
					})
				}
				 = false
			} else {
				 += uint32(64 - ) //nolint:gosec // G115
			}

			// no zero bit at the end, close and append the last gap
			if sna32GT(, ) {
				.end = uint16( - .cumulativeTSN) //nolint:gosec // G115
				 = append(, gapAckBlock{
					start: .start,
					end:   .end,
				})

				break
			}
		}
	}

	return 
}

func ( *receivePayloadQueue) () string {
	 := .getGapAckBlocks()
	 := fmt.Sprintf("cumTSN=%d", .cumulativeTSN)
	for ,  := range  {
		 += fmt.Sprintf(",%d-%d", .start, .end)
	}

	return 
}

func ( *receivePayloadQueue) () (uint32, bool) {
	if .chunkSize == 0 {
		return 0, false
	}

	return .tailTSN, true
}

func ( *receivePayloadQueue) () uint32 {
	return .cumulativeTSN
}

func ( *receivePayloadQueue) () int {
	return .chunkSize
}

func getFirstNonZeroBit( uint64, ,  int) (int, bool) {
	 := bits.TrailingZeros64( >> uint64()) //nolint:gosec // G115

	return  + , + < 
}

func getFirstZeroBit( uint64, ,  int) (int, bool) {
	return getFirstNonZeroBit(^, , )
}