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

package sctp

import (
	
	
	
)

/*
chunkInitCommon represents an SCTP Chunk body of type INIT and INIT ACK

 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 = 1    |  Chunk Flags  |      Chunk Length             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                         Initiate Tag                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           Advertised Receiver Window Credit (a_rwnd)          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Number of Outbound Streams   |  Number of Inbound Streams    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                          Initial TSN                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
|              Optional/Variable-Length Parameters              |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

The INIT chunk contains the following parameters.  Unless otherwise
noted, each parameter MUST only be included once in the INIT chunk.

Fixed Parameters                     Status
----------------------------------------------
Initiate Tag                        Mandatory
Advertised Receiver Window Credit   Mandatory
Number of Outbound Streams          Mandatory
Number of Inbound Streams           Mandatory
Initial TSN                         Mandatory
*/

type chunkInitCommon struct {
	initiateTag                    uint32
	advertisedReceiverWindowCredit uint32
	numOutboundStreams             uint16
	numInboundStreams              uint16
	initialTSN                     uint32
	params                         []param
	unrecognizedParams             []paramHeader
}

const (
	initChunkMinLength          = 16
	initOptionalVarHeaderLength = 4
)

// Init chunk errors.
var (
	ErrInitChunkParseParamTypeFailed = errors.New("failed to parse param type")
	ErrInitAckMarshalParam           = errors.New("unable to marshal parameter for INIT/INITACK")
)

func ( *chunkInitCommon) ( []byte) error {
	.initiateTag = binary.BigEndian.Uint32([0:])
	.advertisedReceiverWindowCredit = binary.BigEndian.Uint32([4:])
	.numOutboundStreams = binary.BigEndian.Uint16([8:])
	.numInboundStreams = binary.BigEndian.Uint16([10:])
	.initialTSN = binary.BigEndian.Uint32([12:])

	// https://tools.ietf.org/html/rfc4960#section-3.2.1
	//
	// Chunk values of SCTP control chunks consist of a chunk-type-specific
	// header of required fields, followed by zero or more parameters.  The
	// optional and variable-length parameters contained in a chunk are
	// defined in a Type-Length-Value format as shown below.
	//
	// 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
	// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	// |          Parameter Type       |       Parameter Length        |
	// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	// |                                                               |
	// |                       Parameter Value                         |
	// |                                                               |
	// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

	 := initChunkMinLength
	 := len() - 
	for  > 0 {
		if  > initOptionalVarHeaderLength {
			var  paramHeader
			if  := .unmarshal([:]);  != nil {
				return fmt.Errorf("%w: %v", ErrInitChunkParseParamTypeFailed, ) //nolint:errorlint
			}

			,  := buildParam(.typ, [:])
			if  != nil {
				.unrecognizedParams = append(.unrecognizedParams, )
			} else {
				.params = append(.params, )
			}

			 := getPadding(.length())
			 += .length() + 
			 -= .length() + 
		} else {
			break
		}
	}

	return nil
}

func ( *chunkInitCommon) () ([]byte, error) {
	 := make([]byte, initChunkMinLength)
	binary.BigEndian.PutUint32([0:], .initiateTag)
	binary.BigEndian.PutUint32([4:], .advertisedReceiverWindowCredit)
	binary.BigEndian.PutUint16([8:], .numOutboundStreams)
	binary.BigEndian.PutUint16([10:], .numInboundStreams)
	binary.BigEndian.PutUint32([12:], .initialTSN)
	for ,  := range .params {
		,  := .marshal()
		if  != nil {
			return nil, fmt.Errorf("%w: %v", ErrInitAckMarshalParam, ) //nolint:errorlint
		}

		 = append(, ...) //nolint:makezero // TODO: fix

		// Chunks (including Type, Length, and Value fields) are padded out
		// by the sender with all zero bytes to be a multiple of 4 bytes
		// long.  This padding MUST NOT be more than 3 bytes in total.  The
		// Chunk Length value does not include terminating padding of the
		// chunk.  *However, it does include padding of any variable-length
		// parameter except the last parameter in the chunk.*  The receiver
		// MUST ignore the padding.
		if  != len(.params)-1 {
			 = padByte(, getPadding(len()))
		}
	}

	return , nil
}

// String makes chunkInitCommon printable.
func ( chunkInitCommon) () string {
	 := `initiateTag: %d
	advertisedReceiverWindowCredit: %d
	numOutboundStreams: %d
	numInboundStreams: %d
	initialTSN: %d`

	 := fmt.Sprintf(,
		.initiateTag,
		.advertisedReceiverWindowCredit,
		.numOutboundStreams,
		.numInboundStreams,
		.initialTSN,
	)

	for ,  := range .params {
		 += fmt.Sprintf("Param %d:\n %s", , )
	}

	return 
}