/*
 * Copyright (c) 2010 by Cristian Maglie <c.maglie@arduino.cc>
 * Copyright (c) 2014 by Paul Stoffregen <paul@pjrc.com> (Transaction API)
 * Copyright (c) 2014 by Matthijs Kooijman <matthijs@stdin.nl> (SPISettings AVR)
 * Copyright (c) 2014 by Andrew J. Kroll <xxxajk@gmail.com> (atomicity fixes)
 * SPI Master library for arduino.
 *
 * This file is free software; you can redistribute it and/or modify
 * it under the terms of either the GNU General Public License version 2
 * or the GNU Lesser General Public License version 2.1, both as
 * published by the Free Software Foundation.
 */

#ifndef _SPI_H_INCLUDED
#define _SPI_H_INCLUDED

#include <Arduino.h>
#include "mik32_hal_spi.h"

// SPI_HAS_TRANSACTION means SPI has beginTransaction(), endTransaction(),
// usingInterrupt(), and SPISetting(clock, bitOrder, dataMode)
#define SPI_HAS_TRANSACTION         1

// SPI_HAS_NOTUSINGINTERRUPT means that SPI has notUsingInterrupt() method
#define SPI_HAS_NOTUSINGINTERRUPT   1

// SPI_ATOMIC_VERSION means that SPI has atomicity fixes and what version.
// This way when there is a bug fix you can check this define to alert users
// of your code if it uses better version of this library.
// This also implies everything that SPI_HAS_TRANSACTION as documented above is
// available too.
#define SPI_ATOMIC_VERSION          1

#define SPI_DEFAULT_SPEED   4000000

// dividers for setClockDivider()
#define SPI_CLOCK_DIV2    0x00    // 16 MHz
#define SPI_CLOCK_DIV4    0x01    // 8 MHz
#define SPI_CLOCK_DIV8    0x02    // 4 MHz
#define SPI_CLOCK_DIV16   0x03    // 2 MHz
#define SPI_CLOCK_DIV32   0x04    // 1 MHz
#define SPI_CLOCK_DIV64   0x05    // 500 kHz
#define SPI_CLOCK_DIV128  0x06    // 250 kHz
#define SPI_CLOCK_DIV256  0x07    // 125 kHz

// mode[1] - polarity, mode[0] - phase
#define SPI_MODE0         0b00
#define SPI_MODE1         0b01
#define SPI_MODE2         0b10
#define SPI_MODE3         0b11

class SPISettings {
public:
    uint32_t speedMaximum;
    uint8_t newDataOrder;
    uint8_t newDataMode;
    // save values from arguments
    SPISettings(uint32_t clock, uint8_t bitOrder, uint8_t mode)
        : speedMaximum(clock), newDataOrder(bitOrder), newDataMode(mode) {}
    SPISettings() 
        : speedMaximum(4000000), newDataOrder(MSBFIRST), newDataMode(SPI_MODE0){}  
};


class SPIClass 
{
private:
  uint8_t _spiNum;
  SPI_HandleTypeDef _hspi = {0};
  uint32_t _speed     = 0;
  uint8_t _dataOrder  = MSBFIRST;
  uint8_t _dataMode   = -1;
  bool _isInited  = false;
  bool _newConfig = false;
  bool _spiInUse  = false;
  uint8_t _interruptMode = 0; // 0=none, 1=mask
  uint8_t _interruptMask = 0; // which interrupts to mask

  void updateSettings(uint32_t speedMaximum, uint8_t dataOrder, uint8_t dataMode);

public:
  inline SPIClass(uint8_t num) 
  {
    // Set the SPI to be used
    _spiNum = (num < SPI_COMMON_QTY) ? num : 1; // SPI1 by default
  }
  // Initialize the SPI library
  void begin();

  // If SPI is used from within an interrupt, this function registers
  // that interrupt with the SPI library, so beginTransaction() can
  // prevent conflicts.  The input interruptNumber is the number used
  // with attachInterrupt.
  void usingInterrupt(uint8_t interruptNumber);
  // And this does the opposite.
  void notUsingInterrupt(uint8_t interruptNumber);
  // Note: the usingInterrupt and notUsingInterrupt functions should
  // not to be called from ISR context or inside a transaction.
  // For details see:
  // https://github.com/arduino/Arduino/pull/2381
  // https://github.com/arduino/Arduino/pull/2449

  // Before using SPI.transfer() or asserting chip select pins,
  // this function is used to gain exclusive access to the SPI bus
  // and configure the correct settings.
  void beginTransaction(SPISettings settings);

  // Write to the SPI bus (MOSI pin) and also receive (MISO pin)
  uint8_t   transfer(uint8_t data);
  uint16_t  transfer16(uint16_t data);
  void      transfer(void *buf, size_t count);
  
  // After performing a group of transfers and releasing the chip select
  // signal, this function allows others to access the SPI bus
  void endTransaction(void);

  // Disable the SPI bus
  void end();

  // This function is deprecated.  New applications should use
  // beginTransaction() to configure SPI settings.
  void setBitOrder(uint8_t bitOrder);

  // This function is deprecated.  New applications should use
  // beginTransaction() to configure SPI settings.
  void setDataMode(uint8_t dataMode);
  // This function is deprecated.  New applications should use
  // beginTransaction() to configure SPI settings.
  void setClockDivider(uint8_t clockDiv);
};

extern SPIClass SPI1;
#if SPI_COMMON_QTY > 1
extern SPIClass SPI0;
#endif
// default interface
extern SPIClass& SPI;

#endif
