#include <Arduino.h>
#include <SPI.h>
#include <math.h>
#include "AD9102.h"
#include "DAC.h"

#define PIN_DAC_CS 15
#define PIN_DDS_CS 17
#define PIN_SPI_SCK 18
#define PIN_SPI_TX 19
#define PIN_SPI_RX 16

/*
  https://docs.arduino.cc/learn/communication/spi

  Mode        Clock polarity  Clock phase     Output edge     Data capture
  SPI_MODE0   0               0               Falling         Rising
  SPI_MODE1   0               1               Rising          Falling
  SPI_MODE2   1               0               Rising          Falling
  SPI_MODE3   1               1               Falling         Rising
*/
SPISettings dds_spi_settings = SPISettings(10000000, MSBFIRST, SPI_MODE0);
SPISettings dac_spi_settings = SPISettings(14000000, MSBFIRST, SPI_MODE1);

AD9102 dds = AD9102(PIN_DDS_CS, 10000000);

uint16_t val = 0x800;

void setup_SPI()
{
  Serial.println("Initializing DAC and DDS...");
  pinMode(PIN_DDS_CS, OUTPUT_4MA);
  pinMode(PIN_DAC_CS, OUTPUT_4MA);

  digitalWrite(PIN_DDS_CS, HIGH);
  digitalWrite(PIN_DAC_CS, HIGH);

  SPI.setTX(PIN_SPI_TX);
  SPI.setRX(PIN_SPI_RX);
  SPI.setSCK(PIN_SPI_SCK);
  SPI.begin(false);

  // initialize DAC
  delay(10);
  init_DAC_SPI(dds_spi_settings, PIN_DAC_CS, 0.59); //0.8 1V //0.985 15V  0.725 722
  delay(10);

  // initialize DDS
  dds.spi_debug(false);
  dds.initialize();
  delay(10);
  Serial.println("Done.");
}

void fill_square(uint32_t length)
{
  uint32_t cnt = length / 2;
  for (int i = 0; i < cnt; i++) {
    dds.spi_write(0x6000 + i, 0x7FFF);
  }
  for (int i = 0; i < cnt; i++) {
    dds.spi_write(0x6000 + cnt + i, 0);
  }
}

void fill_triangle_pattern(size_t length) {
    size_t mid = length / 2;
    for (size_t i = 0; i < length; i++) {
        if (i <= mid) {
            dds.spi_write(0x6000 + i, (uint16_t)((32767.0 / mid) * i));
        } else {
            dds.spi_write(0x6000 + i, (uint16_t)(32767 - (32767.0 / mid) * (i - mid)));
        }
    }
}

void fill_ramp_up_pattern(size_t length) {
    for (size_t i = 0; i < length; i++) {
        dds.spi_write(0x6000 + i, (uint16_t)((32767.0 / (length - 1)) * i));
    }
}

void fill_ramp_down_pattern(size_t length) {
    for (size_t i = 0; i < length; i++) {
        dds.spi_write(0x6000 + i, (uint16_t)(32767 - (32767.0 / (length - 1)) * i));
    }
}

void setup()
{
  Serial.begin(115200);
  delay(3000);
  setup_SPI();
  delay(10);

  // dds.set_dac_dgain(-1.59);
  dds.spi_write(REG_DAC_DGAIN, 0xD6E << 4); // 1.79 gain

  // dds.spi_write(REG_PAT_TYPE, 0x0);
  // dds.spi_write(REG_PATTERN_DLY, 0xe);
  // dds.spi_write(REG_WAV_CONFIG, 0x0);
  // dds.spi_write(REG_SAW_CONFIG, 0x6);
  // dds.spi_write(REG_PAT_STATUS, 0x1);
#if 0
  // dds.spi_write(REG_DDS_CONFIG, 0x4);
  dds.reg_pat_status(0, 1, 0); // enable memory access via SPI

  // write sinewave
  // dds.spi_write(0x6000, 0xF);
  // dds.spi_write(0x6001, 0xF0F0);

  // dds.spi_write(REG_START_ADDR, 0x0);
  // dds.spi_write(REG_STOP_ADDR, 0xFFF << 4);

  // int16_t data_shifted = 0;
  // for (int i = 0; i < 4096; i++)
  // {
  //   int16_t a = (int16_t)(sinf(2.0f * PI * ((float)i / 4096.0f)) * 8000.0f);
  //   data_shifted = a << 2;
  //   dds.spi_write(0x6000 + i, (uint16_t)data_shifted);
  // }

  float frequency = 2.5; // kHz
  uint32_t samples_length = 10000 / frequency; //100;

    fill_square(samples_length); // square (samples_length / 2) hore a dole
 // fill_triangle_pattern(samples_length);
 // fill_ramp_up_pattern(samples_length);
 // fill_ramp_down_pattern(samples_length);

  dds.spi_write(REG_WAV_CONFIG, 0x0 | (2 << 4)); // RAM output from START_ADDR to STOP_ADDR


  // testy start
  // dds.spi_write(REG_PATTERN_DLY, 0x0);       
  // dds.spi_write(REG_DAC_PAT, 0x0);      
  // dds.spi_write(REG_DOUT_START, 0x03);      
  // dds.spi_write(REG_DACRSET, 0x05);      
  // testy end

  { // pokus o TW + MSB EN
    // dds.spi_write(REG_DDS_CONFIG, (1 << 0) | (1 << 2));
    // dds.spi_write(REG_TW_RAM_CONFIG, 0x10);
  }

  dds.spi_write(REG_START_ADDR, 0x0);       // start from 0x6000
  dds.spi_write(REG_STOP_ADDR, (samples_length) << 4); // stop at 0x6FFF

  dds.spi_write(REG_DAC_DGAIN, 0xD6E << 4); // 1.79 gain

  dds.spi_write(REG_PAT_TIMEBASE, 0 << 4);  // set time base to 1 cycle
  dds.spi_write(REG_PAT_PERIOD,samples_length);    // set pattern samplerate to 10k == 1kHz @ 10MHz 0xFA00 pri 2000 , 0x7D00 pri 1000

  dds.spi_write(REG_DDS_TW32, 0x5000);
  // dds.spi_write(REG_DDS_TW1, 0x0);

  // dds.spi_write(REG_TRIG_TW_SEL, 1 << 1); // delay only on pattern start
  dds.spi_write(REG_RAMUPDATE, 1);        // update all SPI settings
  dds.reg_pat_status(0, 1, 1); // enable memory access via SPI and RUN!


  // dds.spi_write(REG_WAV_CONFIG, 0x11); // saw tooth + prestored with start delay and period
  // dds.spi_write(REG_SAW_CONFIG, 0x6);  // set saw step to 1
  // dds.spi_write(REG_PAT_TIMEBASE, 2 << 4);  // set time base to 1 cycle
  // dds.spi_write(REG_PAT_PERIOD, 0x2710);    // set pattern samplerate to 10k == 1kHz @ 10MHz
  // dds.spi_write(REG_DAC_DGAIN, 0xD6E << 4); // 1.79 gain

  // dds.spi_write(REG_WAV_CONFIG, 0);       // waveform from RAM
  // dds.spi_write(REG_WAV_CONFIG, 0b010010); // pre-stored delay/pat period + sawtooth
  // dds.spi_write(REG_SAW_CONFIG, 0b100);
  // dds.spi_write(REG_TRIG_TW_SEL, 1 << 1); // delay only on pattern start
  // dds.spi_write(REG_DDS_CONFIG, 0b0100);  // enable TW + PW + set DDS MSB as clock

  // dds.spi_write(REG_DDS_TW32, 0x0);
  // dds.spi_write(REG_DDS_TW1, 0x1 << 8);



  // dds.mode_sine();
  // dds.mode_sine_ram();
  // dds.mode_sqaure(10);
#else

  dds.spi_write(REG_WAV_CONFIG, 0x31); // DDS output + prestored waveform

  // // 1Hz @ 16.666 MHz CLOCK {100MHz / (divider setting: S5 - 1/3, S6 - 1/3, S7 - 0)}
  // dds.spi_write(REG_DDS_TW32, 0x0000); // DDS tuning word MSB [15:0]
  // dds.spi_write(REG_DDS_TW1, 0x0100); // DDS tuning word LSB [15:8]

  // // 2Hz @ 16.666 MHz CLOCK {100MHz / (divider setting: S5 - 1/3, S6 - 1/3, S7 - 0)}
  // dds.spi_write(REG_DDS_TW32, 0x0000); // DDS tuning word MSB [15:0]
  // dds.spi_write(REG_DDS_TW1, 0x0200); // DDS tuning word LSB [15:8]

  // // 1MHz @ 160MHz CLOCK
  // dds.spi_write(REG_DDS_TW32, 0x0199); // DDS tuning word MSB [15:0]
  // dds.spi_write(REG_DDS_TW1, 0x9A00); // DDS tuning word LSB [15:8]

  // // 2MHz @ 160MHz CLOCK
  // dds.spi_write(REG_DDS_TW32, 0x0333); // DDS tuning word MSB [15:0]
  // dds.spi_write(REG_DDS_TW1, 0x3300); // DDS tuning word LSB [15:8]

  // 500kHz @ 160 MHz
  // dds.spi_write(REG_DDS_TW32, 0x00CC); // DDS tuning word MSB [15:0]
  // dds.spi_write(REG_DDS_TW1, 0xCD00); // DDS tuning word LSB [15:8]

  // Dynamic TW configuration
  uint32_t tuning_word = 0;
  uint32_t fout = 500000; // Desired output frequency 50 kHz
  uint32_t fclock = 160000000; // Input DDS clock 160 MHz

  tuning_word = (fout * pow(2, 24)) / fclock;

  dds.spi_write(REG_DDS_TW32, (tuning_word >> 8) & 0xFFFF); // DDS tuning word MSB [15:0]
  dds.spi_write(REG_DDS_TW1, (tuning_word & 0xFF) << 8); // DDS tuning word LSB [15:8]

  dds.print_status();
  dds.print_ram();

  dds.run();
#endif
}

void loop()
{
  delay(100);
  // dds.print_status();
  // dds.print_ram();
  // for (uint32_t i = 1; i < pow(2, 22); i++)
  // {
  //   dds.set_tw(i);
  // }
}