#include <Arduino.h>

#ifndef AD9102_h
#define AD9102_h

// SPI RW command bits
#define DDS_WRITE 0x7FFF
#define DDS_READ (1 << 15)

// SPI DDS registers
#define REG_SPICONFIG 0x0000
#define REG_POWERCONFIG 0x0001
#define REG_CLOCKCONFIG 0x0002
#define REG_REFADJ 0x0003
#define REG_DACAGAIN 0x0007
#define REG_DACRANGE 0x0008
#define REG_DACRSET 0x000C
#define REG_CALCONFIG 0x000D
#define REG_COMPOFFSET 0x000E
#define REG_RAMUPDATE 0x001D
#define REG_PAT_STATUS 0x001E
#define REG_PAT_TYPE 0x001F
#define REG_PATTERN_DLY 0x0020
#define REG_DACDOF 0x0025
#define REG_WAV_CONFIG 0x0027
#define REG_PAT_TIMEBASE 0x0028
#define REG_PAT_PERIOD 0x0029
#define REG_DAC_PAT 0x002B
#define REG_DOUT_START 0x002C
#define REG_DOUT_CONFIG 0x002D
#define REG_DAC_CST 0x0031
#define REG_DAC_DGAIN 0x0035
#define REG_SAW_CONFIG 0x0037
#define REG_DDS_TW32 0x003E
#define REG_DDS_TW1 0x003F
#define REG_DDS_PW 0x0043
#define REG_TRIG_TW_SEL 0x0044
#define REG_DDS_CONFIG 0x0045
#define REG_TW_RAM_CONFIG 0x0047
#define REG_START_DELAY 0x005C
#define REG_START_ADDR 0x005D
#define REG_STOP_ADDR 0x005E
#define REG_DDS_CYC 0x005F
#define REG_CFG_ERROR 0x0060
#define REG_SRAM_DATA 0x6000

// stuff
#define WAVE_SEL_RAM 0
#define WAVE_SEL_PRES 1
#define WAVE_SEL_PRES_S 2
#define WAVE_SEL_PRES_RAM 3

#define PRESTORE_SEL_CONST 0
#define PRESTORE_SEL_SAW 1
#define PRESTORE_SEL_PSEU 2
#define PRESTORE_SEL_DDS 3

#include "Arduino.h"
#include "SPI.h"

class AD9102_status
{
public:
    bool SPICONFIG_LSBFIRST;
    bool SPICONFIG_SPI3WIRE;
    bool SPICONFIG_RESET;
    bool SPICONFIG_DOUBLESPI;
    bool SPICONFIG_SPI_DRV;
    bool SPICONFIG_DOUT_EN;

    bool POWERCONFIG_CLK_LDO_STAT;
    bool POWERCONFIG_DIG1_LDO_STAT;
    bool POWERCONFIG_DIG2_LDO_STAT;
    bool POWERCONFIG_PDN_LDO_CLK;
    bool POWERCONFIG_PDN_LDO_DIG1;
    bool POWERCONFIG_PDN_LDO_DIG2;
    bool POWERCONFIG_REF_PDN;
    bool POWERCONFIG_REF_EXT;
    bool POWERCONFIG_DAC_SLEEP;

    bool CLOCKCONFIG_CLK;
    bool CLOCKCONFIG_DCLK;
    bool CLOCKCONFIG_CLK_SLEEP;
    bool CLOCKCONFIG_CLK_PDN;
    bool CLOCKCONFIG_EPS;
    bool CLOCKCONFIG_DAC_CLK;

    double BGDR;
};

class AD9102
{
public:
    // Constructor
    AD9102(pin_size_t CS, uint32_t clock_speed);
    // Destructor
    ~AD9102(){};

    AD9102_status status;

    void spi_debug(bool enable);
    uint16_t spi_write(uint16_t address, uint16_t value);
    uint32_t spi_write_32(uint16_t address, uint32_t value);
    uint16_t spi_read(uint16_t address);

    void initialize();
    void print_status();
    void print_ram();

    void reg_spicfg(bool lsb, bool mode_3_wire, bool reset, bool double_spi, bool drive, bool dout);
    void reg_calconfig(bool cal_reset, bool cal_mode_enable, uint8_t comp_cal_range, bool cal_clk_enable, uint8_t cal_clk_div);
    void reg_compoffset(bool start);
    void reg_pat_status(bool buff_read, bool mem_access, bool run);
    void reg_wav_config(byte wav_type, byte prestored_type);
    void reg_dds_config(bool tw_ram, bool pw_ram, bool dds_clock = 0, bool dds_cos = 0);
    void set_tw(uint32_t tw);
    void set_dac_dgain(double gain);
    void update();
    void run();
    void reset();

    // generator modes
    void mode_sine();
    void mode_sine_ram();
    void mode_sqaure(uint16_t freq);

private:
    pin_size_t CS_;
    SPISettings spi_settings;
    bool spi_debug_enabled;
    bool calibration_running;

    void calibrate();
};

#endif