#include "spi.h"
#include "registers.h"

void spi_init(spi_t* spi, spi_peripheral_t* peripheral) {
  spi->periph = peripheral;
}

void spi_master_configure_speed(spi_t *spi,
                                uint8_t divider) {
  reg_write_bits(&spi->periph->CTRL, ((uint32_t)divider) << SPI_CTRL_DIV_SEL_POS, SPI_CTRL_DIV_SEL_MSK);
}

void spi_master_configure(spi_t *spi, bool clock_polarity,
                          bool clock_phase, spi_frame_orientation_t orientation, spi_frame_format_t format,
                          bool csn_pulse) {
  reg_write_bits(&spi->periph->CTRL,
                 (format << SPI_CTRL_SIZE_SEL_POS) |
                     (orientation << SPI_CTRL_LSBFIRST_POS) |
                     (1 << SPI_CTRL_RX_EN_POS) | (1 << SPI_CTRL_TX_EN_POS) | (1 << SPI_CTRL_MASTER_POS) |
                 (clock_polarity << SPI_CTRL_CLOCK_POLARITY_POS) | (clock_phase << SPI_CTRL_CLOCK_PHASE_POS)
                 | (csn_pulse << SPI_CTRL_PULSE_CSN_POS),
				 SPI_CTRL_SIZE_SEL_MSK | SPI_CTRL_PULSE_CSN_POS | SPI_CTRL_LSBFIRST_MSK | SPI_CTRL_RX_EN_MSK | SPI_CTRL_TX_EN_MSK | SPI_CTRL_MASTER_MSK | SPI_CTRL_CLOCK_POLARITY_MSK | SPI_CTRL_CLOCK_PHASE_MSK);
}
void spi_master_enable(spi_t *spi, bool enable) {
  reg_write_bits(&spi->periph->CTRL, enable << SPI_CTRL_EN_POS, SPI_CTRL_EN_MSK);
}

uint16_t spi_transmit(spi_t *spi, uint16_t *data, uint16_t size) {
  for (int16_t i = 0; i < size; i++) {
    while (!(spi->periph->STATUS & SPI_STATUS_TX_EMPTY_MSK));
    uint16_t word = *(data++);
    spi->periph->DATA = word;
  }

  return size;
}

uint16_t spi_receive(spi_t *spi, uint16_t *buffer, uint16_t max_size) {
  uint16_t size = 0;

  while (size < max_size && (spi->periph->STATUS & SPI_STATUS_RX_FULL_MSK)) {
    *(buffer + (size++)) = (uint16_t)(spi->periph->DATA & SPI_DATA_MSK);
  }

  return size;

}

bool spi_can_transmit(spi_t *spi) {
  return (spi->periph->STATUS & SPI_STATUS_TX_EMPTY_MSK) != 0;
}
uint16_t spi_can_receive(spi_t *spi) {
  return ((spi->periph->STATUS & SPI_STATUS_RX_FULL_MSK) != 0) ? 1 : 0;
}

bool spi_enable_interrupt(spi_t *spi, bool tx, bool rx) {
  reg_write_bits(&spi->periph->INTMASK,
                 (tx << SPI_INTMASK_TX_EMPTY_POS) | (rx << SPI_INTMASK_RX_FULL_POS),
                 SPI_INTMASK_RX_FULL_MSK | SPI_INTMASK_TX_EMPTY_MSK);
  return true;
}
