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

package dtls

import (
	

	
	
)

type handshakeCacheItem struct {
	typ             handshake.Type
	isClient        bool
	epoch           uint16
	messageSequence uint16
	data            []byte
}

type handshakeCachePullRule struct {
	typ      handshake.Type
	epoch    uint16
	isClient bool
	optional bool
}

type handshakeCache struct {
	cache []*handshakeCacheItem
	mu    sync.Mutex
}

func newHandshakeCache() *handshakeCache {
	return &handshakeCache{}
}

func ( *handshakeCache) ( []byte, ,  uint16,  handshake.Type,  bool) {
	.mu.Lock()
	defer .mu.Unlock()

	.cache = append(.cache, &handshakeCacheItem{
		data:            append([]byte{}, ...),
		epoch:           ,
		messageSequence: ,
		typ:             ,
		isClient:        ,
	})
}

// returns a list handshakes that match the requested rules
// the list will contain null entries for rules that can't be satisfied
// multiple entries may match a rule, but only the last match is returned (ie ClientHello with cookies)
func ( *handshakeCache) ( ...handshakeCachePullRule) []*handshakeCacheItem {
	.mu.Lock()
	defer .mu.Unlock()

	 := make([]*handshakeCacheItem, len())
	for ,  := range  {
		for ,  := range .cache {
			if .typ == .typ && .isClient == .isClient && .epoch == .epoch {
				switch {
				case [] == nil:
					[] = 
				case [].messageSequence < .messageSequence:
					[] = 
				}
			}
		}
	}

	return 
}

// fullPullMap pulls all handshakes between rules[0] to rules[len(rules)-1] as map.
func ( *handshakeCache) ( int,  CipherSuite,  ...handshakeCachePullRule) (int, map[handshake.Type]handshake.Message, bool) {
	.mu.Lock()
	defer .mu.Unlock()

	 := make(map[handshake.Type]*handshakeCacheItem)
	for ,  := range  {
		var  *handshakeCacheItem
		for ,  := range .cache {
			if .typ == .typ && .isClient == .isClient && .epoch == .epoch {
				switch {
				case  == nil:
					 = 
				case .messageSequence < .messageSequence:
					 = 
				}
			}
		}
		if !.optional &&  == nil {
			// Missing mandatory message.
			return , nil, false
		}
		[.typ] = 
	}
	 := make(map[handshake.Type]handshake.Message)
	 := 
	for ,  := range  {
		 := .typ
		 := []
		if  == nil {
			continue
		}
		var  CipherSuiteKeyExchangeAlgorithm
		if  != nil {
			 = .KeyExchangeAlgorithm()
		}
		 := &handshake.Handshake{
			KeyExchangeAlgorithm: ,
		}
		if  := .Unmarshal(.data);  != nil {
			return , nil, false
		}
		if uint16() != .Header.MessageSequence {
			// There is a gap. Some messages are not arrived.
			return , nil, false
		}
		++
		[] = .Message
	}
	return , , true
}

// pullAndMerge calls pull and then merges the results, ignoring any null entries
func ( *handshakeCache) ( ...handshakeCachePullRule) []byte {
	 := []byte{}

	for ,  := range .pull(...) {
		if  != nil {
			 = append(, .data...)
		}
	}
	return 
}

// sessionHash returns the session hash for Extended Master Secret support
// https://tools.ietf.org/html/draft-ietf-tls-session-hash-06#section-4
func ( *handshakeCache) ( prf.HashFunc,  uint16,  ...[]byte) ([]byte, error) {
	 := []byte{}

	// Order defined by https://tools.ietf.org/html/rfc5246#section-7.3
	 := .pull(
		handshakeCachePullRule{handshake.TypeClientHello, , true, false},
		handshakeCachePullRule{handshake.TypeServerHello, , false, false},
		handshakeCachePullRule{handshake.TypeCertificate, , false, false},
		handshakeCachePullRule{handshake.TypeServerKeyExchange, , false, false},
		handshakeCachePullRule{handshake.TypeCertificateRequest, , false, false},
		handshakeCachePullRule{handshake.TypeServerHelloDone, , false, false},
		handshakeCachePullRule{handshake.TypeCertificate, , true, false},
		handshakeCachePullRule{handshake.TypeClientKeyExchange, , true, false},
	)

	for ,  := range  {
		if  == nil {
			continue
		}

		 = append(, .data...)
	}
	for ,  := range  {
		 = append(, ...)
	}

	 := ()
	if ,  := .Write();  != nil {
		return []byte{}, 
	}

	return .Sum(nil), nil
}