#include <assert.h>

#include "platform/nrf52/spi.h"

extern "C" {
    #include "nrf52.h"
    #include "nrf52_bitfields.h"

    NRF_SPI_Type *SPI_REGS = reinterpret_cast<NRF_SPI_Type *>(NRF_SPI0_BASE);
}

using namespace pinetime::platform::nrf52;

Spi::Spi(uint32_t instance, uint32_t sck, uint32_t mosi, uint32_t miso, pinetime::interfaces::GpioInterface & cs)
    : chip_select(cs)
{
    assert(instance < 3);

    if(instance == 0) {
        SPI_REGS = reinterpret_cast<NRF_SPI_Type *>(NRF_SPI0_BASE);
    } else if(instance == 1) {
        SPI_REGS = reinterpret_cast<NRF_SPI_Type *>(NRF_SPI1_BASE);
    } else {
        SPI_REGS = reinterpret_cast<NRF_SPI_Type *>(NRF_SPI2_BASE);
    }

    this->chip_select.set();

    SPI_REGS->ENABLE = 0;
    SPI_REGS->PSEL.SCK = sck;
    SPI_REGS->PSEL.MOSI = mosi;
    SPI_REGS->PSEL.MISO = miso;
    SPI_REGS->FREQUENCY = SPI_FREQUENCY_FREQUENCY_M8;

    SPI_REGS->CONFIG = (0x03 << 1); //Sample on trailing edge of clock, shift serial data on leading edge, SCK polarity Active low
    SPI_REGS->EVENTS_READY = 0;
    SPI_REGS->ENABLE = (SPI_ENABLE_ENABLE_Enabled << SPI_ENABLE_ENABLE_Pos);
}
Spi::~Spi()
{
    SPI_REGS->ENABLE = 0;
}

void Spi::send(const uint8_t * buffer, uint32_t len)
{
    this->chip_select.clear();
    for(unsigned int i = 0; i < len; i++) {
        this->transfer(buffer[i]);
    }
    this->chip_select.set();
}

void Spi::recv(uint8_t * buffer, uint32_t len)
{
    this->chip_select.clear();
    for(unsigned int i = 0; i < len; i++) {
        buffer[i] = this->transfer(buffer[i]);
    }
    this->chip_select.set();
}

uint32_t Spi::transfer(uint32_t data)
{
    volatile uint32_t ret;
    SPI_REGS->EVENTS_READY = 0;
    SPI_REGS->TXD = data;
    while(SPI_REGS->EVENTS_READY == 0) {;}
    ret = SPI_REGS->RXD;
    return ret;
}