/*
 * SPI Master library for nRF5x.
 * Copyright (c) 2015 Arduino LLC
 * Copyright (c) 2016 Sandeep Mistry All right reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "SPI.h"
#include <Arduino.h>
#include <wiring_private.h>
#include <assert.h>

#define SPI_IMODE_NONE   0
#define SPI_IMODE_EXTINT 1
#define SPI_IMODE_GLOBAL 2


const SPISettings DEFAULT_SPI_SETTINGS = SPISettings();

SPIClass::SPIClass(uint8_t uc_pinMISO, uint8_t uc_pinSCK, uint8_t uc_pinMOSI)
{
  initialized = false;

  // pins
  _uc_pinMISO = uc_pinMISO;
  _uc_pinSCK = uc_pinSCK;
  _uc_pinMOSI = uc_pinMOSI;

  _dataMode = SPI_MODE0;
  _bitOrder = SPI_CONFIG_ORDER_MsbFirst;
}

#ifdef ARDUINO_GENERIC
void SPIClass::setPins(uint8_t uc_pinMISO, uint8_t uc_pinSCK, uint8_t uc_pinMOSI)
{
  _uc_pinMISO = uc_pinMISO;
  _uc_pinSCK = uc_pinSCK;
  _uc_pinMOSI = uc_pinMOSI;
}
#endif // ARDUINO_GENERIC

void SPIClass::begin()
{ 
  init();
  config(DEFAULT_SPI_SETTINGS);
}

void SPIClass::init()
{
  if (initialized)
    return;
  spi = NULL;
  interruptMode = SPI_IMODE_NONE;
  interruptSave = 0;
  interruptMask = 0;
  initialized = true;
}

void SPIClass::config(SPISettings settings)
{
  spi = new SPI_dal((PinName)_uc_pinMOSI, (PinName)_uc_pinMISO, (PinName)_uc_pinSCK, (PinName)NC);
  spi->format(8, settings.clockFreq, settings.bitOrder, settings.dataMode);
  delay(1);
}

void SPIClass::end()
{
  spi->free();
  delete spi;
  spi = NULL;
  initialized = false;
}

void SPIClass::usingInterrupt(int /*interruptNumber*/)
{
}

void SPIClass::beginTransaction(SPISettings settings)
{
  config(settings);
}

void SPIClass::endTransaction(void)
{
}

void SPIClass::setBitOrder(BitOrder order)
{
  this->_bitOrder = (order == MSBFIRST ? SPI_CONFIG_ORDER_MsbFirst : SPI_CONFIG_ORDER_LsbFirst);
  spi->bitOrder(this->_bitOrder);
}

void SPIClass::setDataMode(uint8_t mode)
{
  if(mode>3 || mode <0) return;
  this->_dataMode = mode;
  spi->mode(this->_dataMode);
}

void SPIClass::setClockDivider(uint8_t div)
{
  uint32_t clockFreq;

  if (div >= SPI_CLOCK_DIV128) {
    clockFreq = SPI_FREQUENCY_FREQUENCY_K125;
  } else if (div >= SPI_CLOCK_DIV64) {
    clockFreq = SPI_FREQUENCY_FREQUENCY_K250;
  } else if (div >= SPI_CLOCK_DIV32) {
    clockFreq = SPI_FREQUENCY_FREQUENCY_K500;
  } else if (div >= SPI_CLOCK_DIV16) {
    clockFreq = SPI_FREQUENCY_FREQUENCY_M1;
  } else if (div >= SPI_CLOCK_DIV8) {
    clockFreq = SPI_FREQUENCY_FREQUENCY_M2;
  } else if (div >= SPI_CLOCK_DIV4) {
    clockFreq = SPI_FREQUENCY_FREQUENCY_M4;
  } else {
    clockFreq = SPI_FREQUENCY_FREQUENCY_M8;
  }

  spi->frequency(clockFreq);
}

byte SPIClass::transfer(uint8_t data)
{
  if (!initialized)
    return 0;
  return spi->write(data);
}

uint16_t SPIClass::transfer16(uint16_t data) {
  union { uint16_t val; struct { uint8_t lsb; uint8_t msb; }; } t;

  t.val = data;

  if (_bitOrder == SPI_CONFIG_ORDER_LsbFirst) {
    t.lsb = transfer(t.lsb);
    t.msb = transfer(t.msb);
  } else {
    t.msb = transfer(t.msb);
    t.lsb = transfer(t.lsb);
  }

  return t.val;
}

void SPIClass::attachInterrupt() {
  // Should be enableInterrupt()
}

void SPIClass::detachInterrupt() {
  // Should be disableInterrupt()
}

SPIClass SPI (MISO, SCK , MOSI);