//go:build stm32f4

package machine

import (
	"device/stm32"
	"unsafe"
)

// InitADC initializes the registers needed for ADC1.
func InitADC() {
	// Enable ADC clock
	enableAltFuncClock(unsafe.Pointer(stm32.ADC1))

	// stop scan, and clear scan resolution
	stm32.ADC1.CR1.ClearBits(stm32.ADC_CR1_SCAN | stm32.ADC_CR1_RES_Msk)

	// set conversion mode and resolution
	stm32.ADC1.CR1.SetBits(stm32.ADC_CR1_RES_TwelveBit)

	// clear CONT, ALIGN, EXTEN and EXTSEL bits from CR2
	stm32.ADC1.CR2.ClearBits(stm32.ADC_CR2_CONT | stm32.ADC_CR2_ALIGN | stm32.ADC_CR2_EXTEN_Msk | stm32.ADC_CR2_EXTSEL_Msk)

	// set CONT, ALIGN, EXTEN and EXTSEL bits from CR2
	stm32.ADC1.CR2.SetBits(stm32.ADC_CR2_CONT_Single | stm32.ADC_CR2_ALIGN_Right)

	stm32.ADC1.SQR1.ClearBits(stm32.ADC_SQR1_L_Msk)
	stm32.ADC1.SQR1.SetBits(2 << stm32.ADC_SQR1_L_Pos) // 2 means 3 conversions

	// enable
	stm32.ADC1.CR2.SetBits(stm32.ADC_CR2_ADON)

	return
}

// Configure configures an ADC pin to be able to read analog data.
func (a ADC) Configure(ADCConfig) {
	a.Pin.ConfigureAltFunc(PinConfig{Mode: PinInputAnalog}, 0)

	// set sample time
	ch := a.getChannel()
	if ch > 9 {
		stm32.ADC1.SMPR1.SetBits(stm32.ADC_SMPR1_SMP11_Cycles84 << (ch - 10) * stm32.ADC_SMPR1_SMP11_Pos)
	} else {
		stm32.ADC1.SMPR2.SetBits(stm32.ADC_SMPR2_SMP1_Cycles84 << (ch * stm32.ADC_SMPR2_SMP1_Pos))
	}

	return
}

// Get returns the current value of a ADC pin in the range 0..0xffff.
// TODO: DMA based implementation.
func (a ADC) Get() uint16 {
	// set rank
	ch := uint32(a.getChannel())
	stm32.ADC1.SQR3.SetBits(ch)

	// start conversion
	stm32.ADC1.CR2.SetBits(stm32.ADC_CR2_SWSTART)

	// wait for conversion to complete
	for !stm32.ADC1.SR.HasBits(stm32.ADC_SR_EOC) {
	}

	// read 12-bit result as 16 bit value
	result := uint16(stm32.ADC1.DR.Get()) << 4

	// clear flag
	stm32.ADC1.SR.ClearBits(stm32.ADC_SR_EOC)

	// clear rank
	stm32.ADC1.SQR3.ClearBits(ch)

	return result
}

func (a ADC) getChannel() uint8 {
	switch a.Pin {
	case PA0:
		return 0
	case PA1:
		return 1
	case PA2:
		return 2
	case PA3:
		return 3
	case PA4:
		return 4
	case PA5:
		return 5
	case PA6:
		return 6
	case PA7:
		return 7
	case PB0:
		return 8
	case PB1:
		return 9
	case PC0:
		return 10
	case PC1:
		return 11
	case PC2:
		return 12
	case PC3:
		return 13
	case PC4:
		return 14
	case PC5:
		return 15
	}

	return 0
}
