#ifndef MISSION_DEVICES_MAX1227_H_
#define MISSION_DEVICES_MAX1227_H_

#include <cstddef>
#include <cstdint>

namespace max1227 {

enum ScanModes : uint8_t {
  CHANNELS_0_TO_N = 0b00,
  CHANNEL_N_TO_HIGHEST = 0b01,
  N_REPEATEDLY = 0b10,
  N_ONCE = 0b11
};

enum ClkSel : uint8_t {
  INT_CONV_INT_TIMED_CNVST_AS_CNVST = 0b00,
  INT_CONV_EXT_TIMED_CNVST = 0b01,
  // Default mode upon power-up
  INT_CONV_INT_TIMED_CNVST_AS_AIN = 0b10,
  // Use external SPI clock for conversion and timing
  EXT_CONV_EXT_TIMED = 0b11
};

enum RefSel : uint8_t {
  INT_REF_WITH_WAKEUP = 0b00,
  // No wakeup delay needed
  EXT_REF_SINGLE_ENDED = 0b01,
  INT_REF_NO_WAKEUP = 0b10,
  // No wakeup delay needed
  EXT_REF_DIFFERENTIAL = 0b11
};

enum DiffSel : uint8_t {
  NONE_0 = 0b00,
  NONE_1 = 0b01,
  // One unipolar config byte follows the setup byte
  UNIPOLAR_CFG = 0b10,
  // One bipolar config byte follows the setup byte
  BIPOLAR_CFG = 0b11
};

constexpr uint8_t buildResetByte(bool fifoOnly) { return (1 << 4) | (fifoOnly << 3); }

constexpr uint8_t buildConvByte(ScanModes scanMode, uint8_t channel, bool readTemp) {
  return (1 << 7) | (channel << 3) | (scanMode << 1) | readTemp;
}

constexpr uint8_t buildSetupByte(ClkSel clkSel, RefSel refSel, DiffSel diffSel) {
  return (1 << 6) | (clkSel << 4) | (refSel << 2) | diffSel;
}

/**
 * If there is a wakeup delay, there needs to be a 65 us delay between sending
 * the first byte (conversion byte) and the the rest of the SPI buffer.
 * The raw ADC value will be located in the first and second reply byte.
 * @param spiBuf
 * @param n
 * @param sz
 */
void prepareExternallyClockedSingleChannelRead(uint8_t* spiBuf, uint8_t channel, size_t& sz);

/**
 * If there is a wakeup delay, there needs to be a 65 us delay between sending
 * the first byte (first conversion byte) the the rest of the SPI buffer.
 * @param spiBuf
 * @param n Channel number. Example: If the ADC has 6 channels, n will be 5
 * @param sz Will be incremented by amount which should be sent
 */
void prepareExternallyClockedRead0ToN(uint8_t* spiBuf, uint8_t n, size_t& sz);

/**
 * Prepare an externally clocked temperature read. 25 bytes have to be sent
 * and the raw temperature value will appear on the last 2 bytes of the reply.
 * @param spiBuf
 * @param sz Will be incremented by amount which should be sent
 */
void prepareExternallyClockedTemperatureRead(uint8_t* spiBuf, size_t& sz);

float getTemperature(int16_t temp);

}  // namespace max1227

#endif /* MISSION_DEVICES_MAX1227_H_ */