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

package dtls

import (
	
	
	

	
	
	
	
)

// State holds the dtls connection state and implements both encoding.BinaryMarshaler and encoding.BinaryUnmarshaler
type State struct {
	localEpoch, remoteEpoch   atomic.Value
	localSequenceNumber       []uint64 // uint48
	localRandom, remoteRandom handshake.Random
	masterSecret              []byte
	cipherSuite               CipherSuite // nil if a cipherSuite hasn't been chosen

	srtpProtectionProfile atomic.Value // Negotiated SRTPProtectionProfile
	PeerCertificates      [][]byte
	IdentityHint          []byte
	SessionID             []byte

	isClient bool

	preMasterSecret      []byte
	extendedMasterSecret bool

	namedCurve                 elliptic.Curve
	localKeypair               *elliptic.Keypair
	cookie                     []byte
	handshakeSendSequence      int
	handshakeRecvSequence      int
	serverName                 string
	remoteRequestedCertificate bool   // Did we get a CertificateRequest
	localCertificatesVerify    []byte // cache CertificateVerify
	localVerifyData            []byte // cached VerifyData
	localKeySignature          []byte // cached keySignature
	peerCertificatesVerified   bool

	replayDetector []replaydetector.ReplayDetector

	peerSupportedProtocols []string
	NegotiatedProtocol     string
}

type serializedState struct {
	LocalEpoch            uint16
	RemoteEpoch           uint16
	LocalRandom           [handshake.RandomLength]byte
	RemoteRandom          [handshake.RandomLength]byte
	CipherSuiteID         uint16
	MasterSecret          []byte
	SequenceNumber        uint64
	SRTPProtectionProfile uint16
	PeerCertificates      [][]byte
	IdentityHint          []byte
	SessionID             []byte
	IsClient              bool
}

func ( *State) () *State {
	 := .serialize()
	 := &State{}
	.deserialize(*)

	return 
}

func ( *State) () *serializedState {
	// Marshal random values
	 := .localRandom.MarshalFixed()
	 := .remoteRandom.MarshalFixed()

	 := .getLocalEpoch()
	return &serializedState{
		LocalEpoch:            .getLocalEpoch(),
		RemoteEpoch:           .getRemoteEpoch(),
		CipherSuiteID:         uint16(.cipherSuite.ID()),
		MasterSecret:          .masterSecret,
		SequenceNumber:        atomic.LoadUint64(&.localSequenceNumber[]),
		LocalRandom:           ,
		RemoteRandom:          ,
		SRTPProtectionProfile: uint16(.getSRTPProtectionProfile()),
		PeerCertificates:      .PeerCertificates,
		IdentityHint:          .IdentityHint,
		SessionID:             .SessionID,
		IsClient:              .isClient,
	}
}

func ( *State) ( serializedState) {
	// Set epoch values
	 := .LocalEpoch
	.localEpoch.Store(.LocalEpoch)
	.remoteEpoch.Store(.RemoteEpoch)

	for len(.localSequenceNumber) <= int() {
		.localSequenceNumber = append(.localSequenceNumber, uint64(0))
	}

	// Set random values
	 := &handshake.Random{}
	.UnmarshalFixed(.LocalRandom)
	.localRandom = *

	 := &handshake.Random{}
	.UnmarshalFixed(.RemoteRandom)
	.remoteRandom = *

	.isClient = .IsClient

	// Set master secret
	.masterSecret = .MasterSecret

	// Set cipher suite
	.cipherSuite = cipherSuiteForID(CipherSuiteID(.CipherSuiteID), nil)

	atomic.StoreUint64(&.localSequenceNumber[], .SequenceNumber)
	.setSRTPProtectionProfile(SRTPProtectionProfile(.SRTPProtectionProfile))

	// Set remote certificate
	.PeerCertificates = .PeerCertificates
	.IdentityHint = .IdentityHint
	.SessionID = .SessionID
}

func ( *State) () error {
	if .cipherSuite.IsInitialized() {
		return nil
	}

	 := .localRandom.MarshalFixed()
	 := .remoteRandom.MarshalFixed()

	var  error
	if .isClient {
		 = .cipherSuite.Init(.masterSecret, [:], [:], true)
	} else {
		 = .cipherSuite.Init(.masterSecret, [:], [:], false)
	}
	if  != nil {
		return 
	}
	return nil
}

// MarshalBinary is a binary.BinaryMarshaler.MarshalBinary implementation
func ( *State) () ([]byte, error) {
	 := .serialize()

	var  bytes.Buffer
	 := gob.NewEncoder(&)
	if  := .Encode(*);  != nil {
		return nil, 
	}
	return .Bytes(), nil
}

// UnmarshalBinary is a binary.BinaryUnmarshaler.UnmarshalBinary implementation
func ( *State) ( []byte) error {
	 := gob.NewDecoder(bytes.NewBuffer())
	var  serializedState
	if  := .Decode(&);  != nil {
		return 
	}

	.deserialize()

	return .initCipherSuite()
}

// ExportKeyingMaterial returns length bytes of exported key material in a new
// slice as defined in RFC 5705.
// This allows protocols to use DTLS for key establishment, but
// then use some of the keying material for their own purposes
func ( *State) ( string,  []byte,  int) ([]byte, error) {
	if .getLocalEpoch() == 0 {
		return nil, errHandshakeInProgress
	} else if len() != 0 {
		return nil, errContextUnsupported
	} else if ,  := invalidKeyingLabels()[];  {
		return nil, errReservedExportKeyingMaterial
	}

	 := .localRandom.MarshalFixed()
	 := .remoteRandom.MarshalFixed()

	 := []byte()
	if .isClient {
		 = append(append(, [:]...), [:]...)
	} else {
		 = append(append(, [:]...), [:]...)
	}
	return prf.PHash(.masterSecret, , , .cipherSuite.HashFunc())
}

func ( *State) () uint16 {
	if ,  := .remoteEpoch.Load().(uint16);  {
		return 
	}
	return 0
}

func ( *State) () uint16 {
	if ,  := .localEpoch.Load().(uint16);  {
		return 
	}
	return 0
}

func ( *State) ( SRTPProtectionProfile) {
	.srtpProtectionProfile.Store()
}

func ( *State) () SRTPProtectionProfile {
	if ,  := .srtpProtectionProfile.Load().(SRTPProtectionProfile);  {
		return 
	}

	return 0
}