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

package sctp

import (
	
	
	
	
)

/*
chunkPayloadData represents an SCTP Chunk of type DATA

	 0                   1                   2                   3
	 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	|   Type = 0    | Reserved|U|B|E|    Length                     |
	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	|                              TSN                              |
	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	|      Stream Identifier S      |   Stream Sequence Number n    |
	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	|                  Payload Protocol Identifier                  |
	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	|                                                               |
	|                 User Data (seq n of Stream S)                 |
	|                                                               |
	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

An unfragmented user message shall have both the B and E bits set to
'1'.  Setting both B and E bits to '0' indicates a middle fragment of
a multi-fragment user message, as summarized in the following table:

	   B E                  Description
	============================================================
	|  1 0 | First piece of a fragmented user message          |
	+----------------------------------------------------------+
	|  0 0 | Middle piece of a fragmented user message         |
	+----------------------------------------------------------+
	|  0 1 | Last piece of a fragmented user message           |
	+----------------------------------------------------------+
	|  1 1 | Unfragmented message                              |
	============================================================
	|             Table 1: Fragment Description Flags          |
	============================================================
*/
type chunkPayloadData struct {
	chunkHeader

	unordered         bool
	beginningFragment bool
	endingFragment    bool
	immediateSack     bool

	tsn                  uint32
	streamIdentifier     uint16
	streamSequenceNumber uint16
	payloadType          PayloadProtocolIdentifier
	userData             []byte

	// Whether this data chunk was acknowledged (received by peer)
	acked         bool
	missIndicator uint32

	// Partial-reliability parameters used only by sender
	since        time.Time
	nSent        uint32 // number of transmission made for this chunk
	_abandoned   bool
	_allInflight bool // valid only with the first fragment

	// Retransmission flag set when T1-RTX timeout occurred and this
	// chunk is still in the inflight queue
	retransmit bool

	head *chunkPayloadData // link to the head of the fragment
}

const (
	payloadDataEndingFragmentBitmask   = 1
	payloadDataBeginingFragmentBitmask = 2
	payloadDataUnorderedBitmask        = 4
	payloadDataImmediateSACK           = 8

	payloadDataHeaderSize = 12
)

// PayloadProtocolIdentifier is an enum for DataChannel payload types.
type PayloadProtocolIdentifier uint32

// PayloadProtocolIdentifier enums
// https://www.iana.org/assignments/sctp-parameters/sctp-parameters.xhtml#sctp-parameters-25
const (
	PayloadTypeUnknown           PayloadProtocolIdentifier = 0
	PayloadTypeWebRTCDCEP        PayloadProtocolIdentifier = 50
	PayloadTypeWebRTCString      PayloadProtocolIdentifier = 51
	PayloadTypeWebRTCBinary      PayloadProtocolIdentifier = 53
	PayloadTypeWebRTCStringEmpty PayloadProtocolIdentifier = 56
	PayloadTypeWebRTCBinaryEmpty PayloadProtocolIdentifier = 57
)

// Data chunk errors.
var (
	ErrChunkPayloadSmall = errors.New("packet is smaller than the header size")
)

func ( PayloadProtocolIdentifier) () string {
	switch  {
	case PayloadTypeWebRTCDCEP:
		return "WebRTC DCEP"
	case PayloadTypeWebRTCString:
		return "WebRTC String"
	case PayloadTypeWebRTCBinary:
		return "WebRTC Binary"
	case PayloadTypeWebRTCStringEmpty:
		return "WebRTC String (Empty)"
	case PayloadTypeWebRTCBinaryEmpty:
		return "WebRTC Binary (Empty)"
	default:
		return fmt.Sprintf("Unknown Payload Protocol Identifier: %d", )
	}
}

func ( *chunkPayloadData) ( []byte) error {
	if  := .chunkHeader.unmarshal();  != nil {
		return 
	}

	.immediateSack = .flags&payloadDataImmediateSACK != 0
	.unordered = .flags&payloadDataUnorderedBitmask != 0
	.beginningFragment = .flags&payloadDataBeginingFragmentBitmask != 0
	.endingFragment = .flags&payloadDataEndingFragmentBitmask != 0

	if len(.raw) < payloadDataHeaderSize {
		return ErrChunkPayloadSmall
	}
	.tsn = binary.BigEndian.Uint32(.raw[0:])
	.streamIdentifier = binary.BigEndian.Uint16(.raw[4:])
	.streamSequenceNumber = binary.BigEndian.Uint16(.raw[6:])
	.payloadType = PayloadProtocolIdentifier(binary.BigEndian.Uint32(.raw[8:]))
	.userData = .raw[payloadDataHeaderSize:]

	return nil
}

func ( *chunkPayloadData) () ([]byte, error) {
	 := make([]byte, payloadDataHeaderSize+len(.userData))

	binary.BigEndian.PutUint32([0:], .tsn)
	binary.BigEndian.PutUint16([4:], .streamIdentifier)
	binary.BigEndian.PutUint16([6:], .streamSequenceNumber)
	binary.BigEndian.PutUint32([8:], uint32(.payloadType))
	copy([payloadDataHeaderSize:], .userData)

	 := uint8(0)
	if .endingFragment {
		 = 1
	}
	if .beginningFragment {
		 |= 1 << 1
	}
	if .unordered {
		 |= 1 << 2
	}
	if .immediateSack {
		 |= 1 << 3
	}

	.chunkHeader.flags = 
	.chunkHeader.typ = ctPayloadData
	.chunkHeader.raw = 

	return .chunkHeader.marshal()
}

func ( *chunkPayloadData) () ( bool,  error) {
	return false, nil
}

// String makes chunkPayloadData printable.
func ( *chunkPayloadData) () string {
	return fmt.Sprintf("%s\n%d", .chunkHeader, .tsn)
}

func ( *chunkPayloadData) () bool {
	if .head != nil {
		return .head._abandoned && .head._allInflight
	}

	return ._abandoned && ._allInflight
}

func ( *chunkPayloadData) ( bool) {
	if .head != nil {
		.head._abandoned = 

		return
	}
	._abandoned = 
}

func ( *chunkPayloadData) () {
	if .endingFragment {
		if .head != nil {
			.head._allInflight = true
		} else {
			._allInflight = true
		}
	}
}

func ( *chunkPayloadData) () bool {
	return !(.head == nil && .beginningFragment && .endingFragment)
}