//go:build nrf && softdevice

package machine

import (
	"device/arm"
	"device/nrf"

	"errors"
)

// avoid a heap allocation in GetRNG.
var (
	softdeviceEnabled uint8
	bytesAvailable    uint8
	buf               [4]uint8

	errNoSoftDeviceSupport = errors.New("rng: softdevice not supported on this device")
	errNotEnoughRandomData = errors.New("rng: not enough random data available")
)

// GetRNG returns 32 bits of non-deterministic random data based on internal thermal noise.
// According to Nordic's documentation, the random output is suitable for cryptographic purposes.
func GetRNG() (ret uint32, err error) {
	// First check whether the SoftDevice is enabled.
	// sd_rand_application_bytes_available_get cannot be called when the SoftDevice is not enabled.
	arm.SVCall1(0x12, &softdeviceEnabled) // sd_softdevice_is_enabled

	if softdeviceEnabled == 0 {
		return getRNG()
	}

	// call into the SoftDevice to get random data bytes available
	switch nrf.Device {
	case "nrf51":
		// sd_rand_application_bytes_available_get: SOC_SVC_BASE_NOT_AVAILABLE + 4
		arm.SVCall1(0x2B+4, &bytesAvailable)
	case "nrf52", "nrf52840", "nrf52833":
		// sd_rand_application_bytes_available_get: SOC_SVC_BASE_NOT_AVAILABLE + 4
		arm.SVCall1(0x2C+4, &bytesAvailable)
	default:
		return 0, errNoSoftDeviceSupport
	}

	if bytesAvailable < 4 {
		return 0, errNotEnoughRandomData
	}

	switch nrf.Device {
	case "nrf51":
		// sd_rand_application_vector_get: SOC_SVC_BASE_NOT_AVAILABLE + 5
		arm.SVCall2(0x2B+5, &buf, 4)
	case "nrf52", "nrf52840", "nrf52833":
		// sd_rand_application_vector_get: SOC_SVC_BASE_NOT_AVAILABLE + 5
		arm.SVCall2(0x2C+5, &buf, 4)
	}

	return uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24, nil
}
