// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause

package magicsock

import (
	"sync/atomic"

	"tailscale.com/types/key"
)

type discoKeyPair struct {
	private key.DiscoPrivate
	public  key.DiscoPublic
	short   string // public.ShortString()
}

// discoAtomic is an atomic container for a disco private key, public key, and
// the public key's ShortString. The private and public keys are always kept
// synchronized.
//
// The zero value is not ready for use. Use [Set] to provide a usable value.
type discoAtomic struct {
	pair atomic.Pointer[discoKeyPair]
}

// Pair returns the private and public keys together atomically.
// Code that needs both the private and public keys synchronized should
// use Pair instead of calling Private and Public separately.
func (dk *discoAtomic) Pair() (key.DiscoPrivate, key.DiscoPublic) {
	p := dk.pair.Load()
	return p.private, p.public
}

// Private returns the private key.
func (dk *discoAtomic) Private() key.DiscoPrivate {
	return dk.pair.Load().private
}

// Public returns the public key.
func (dk *discoAtomic) Public() key.DiscoPublic {
	return dk.pair.Load().public
}

// Short returns the short string of the public key (see [DiscoPublic.ShortString]).
func (dk *discoAtomic) Short() string {
	return dk.pair.Load().short
}

// Set updates the private key (and the cached public key and short string).
func (dk *discoAtomic) Set(private key.DiscoPrivate) {
	public := private.Public()
	dk.pair.Store(&discoKeyPair{
		private: private,
		public:  public,
		short:   public.ShortString(),
	})
}
