#include "adc.h"
#include "registers.h"
#include "stm32f401xe.h"

void adc_init(adc_t* adc, ADC_TypeDef* periph) {
  adc->periph = periph;
}

void adc_configure(adc_t *adc, adc_resolution_t resolution,
                   adc_alignment_t alignment, bool continuous) {
  reg_write_bits(&adc->periph->CR1, resolution << ADC_CR1_RES_Pos, ADC_CR1_RES_Msk);
  reg_write_bits(&adc->periph->CR2, ((continuous == true) << ADC_CR2_CONT_Pos) | (alignment << ADC_CR2_ALIGN_Pos), ADC_CR2_CONT_Msk | ADC_CR2_ALIGN_Msk);
}

void adc_enable(adc_t *adc, bool enable) {
  reg_set_bits(&adc->periph->CR2, ADC_CR2_ADON);
}

void adc_sequence(adc_t *adc, uint8_t count) {
  reg_write_bits(&adc->periph->SQR1, count << ADC_SQR1_L_Pos, ADC_SQR1_L_Msk);
}

void adc_select(adc_t *adc, uint8_t conversion_idx, uint8_t channel) {
  uint8_t reg = conversion_idx / 6;
  uint8_t pos = (conversion_idx % 6) * 4;
  volatile uint32_t* reg_ptr = &adc->periph->SQR3 - reg;

  reg_write_bits_pos(reg_ptr, channel & 0xF, pos, 0xF);
}

void adc_external_trigger(adc_t* adc, adc_external_trigger_regular_t config, adc_external_trigger_source_regular_t source) {
  reg_write_bits(&adc->periph->CR2,
                 (source << ADC_CR2_EXTSEL_Pos) | (config << ADC_CR2_EXTEN_Pos),
                 ADC_CR2_EXTSEL_Msk | ADC_CR2_EXTEN_Msk);
}

/* void adc_start_capture(adc_t* adc); */

bool adc_is_captured(adc_t *adc) {
  return adc->periph->SR & ADC_SR_EOC;
}

uint32_t adc_captured_value(adc_t *adc) {
  // TODO: different resolutions, right or left aligned
  return adc->periph->DR & 0xFFF;
}

uint32_t adc_convert_voltage(uint32_t capture, uint32_t min, uint32_t max) {
  return 0;
}
