#pragma once

#include <thread>
#include <semaphore.h>
#include <atomic>
#include <stdint.h>
#include <complex>
#include <string>
#include <queue>

// #define UTIL_REG_BASE 0x100000
// #define TIMING_REG_BASE 0x110000
// #define DATA_GEN_REG_BASE 0x120000

#define UTIL_REG_BASE 0x10000
#define TIMING_REG_BASE 0x20000
#define DATA_GEN_REG_BASE 0x30000

#define DREXDMA_DMA_INIT             0
#define DREXDMA_DMA_CLEAR            1
#define DREXDMA_DMA_START            2
#define DREXDMA_DMA_STOP             3
#define DREXDMA_SET_NUM_BUFS         4
#define DREXDMA_SET_NUM_BYTES        5
#define DREXDMA_GET_NUM_BUFS         6
#define DREXDMA_GET_NUM_BYTES        7
#define DREXDMA_GET_FREE_BUFS        8

typedef struct {
   unsigned int cmd;
   unsigned int offset;
   unsigned int value;
} drexpcie_ioctl_t;

#define NUM_DMA_CH      4
#define BUFFER_SIZE     (4 * 1024 * 1024)
// #define BUFFER_SIZE     (8192)
#define NUM_BUFFERS     128
#define OVERALL_BUFFER_SIZE (BUFFER_SIZE * NUM_BUFFERS)

#define CLK_RATE 250e6

class DataRecorder {
    public:
        DataRecorder();
        ~DataRecorder();
        
        int start_recording(const char* filename, int save_to_disk);
        int stop_recording();
        std::vector<float> get_recording_rate();
        std::vector<uint64_t> get_filesize();
        void set_validate_cnt_data(bool enable, uint32_t pri, uint32_t inter, uint32_t count);
        void write_reg(uint32_t addr, uint32_t data);
        uint32_t read_reg(uint32_t addr);        

        bool recording_active;

    private:
        void allocate_memory();
        void get_data(int ch_ind, int save_to_disk);
        void write_data(int ch_ind);

        int plfd;
        volatile uint32_t * plmmap;

        std::thread recorder[NUM_DMA_CH];
        std::thread writer[NUM_DMA_CH];
        std::queue<int> buffer_queue[NUM_DMA_CH];
        sem_t buffer_ready_sem[NUM_DMA_CH];
        int num_bytes_to_write[NUM_DMA_CH][NUM_BUFFERS];
        int out_fd[NUM_DMA_CH];
        char * data_buffer[NUM_DMA_CH];
        std::atomic<bool> exit_thread;

        float recording_rate[NUM_DMA_CH];
        uint64_t total_bytes[NUM_DMA_CH];

        bool validate_cnt_data;
        uint32_t cnt_pri;
        uint32_t cnt_inter_pri;
        uint32_t cnt_num_pulse;

};