#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <byteswap.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <ctype.h>
#include "main.h"
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <time.h>

//
#define PORT 8888

// Define the structure to represent a packet
struct Packet {
    uint32_t header;                         // 0x48454144
    uint16_t trigger_type;                    // 0, normal trigger; 1 trigger in water
    uint16_t sn_alert;                        // 0, no alert; 1, alert
    uint64_t timestamp;                      // 8 ns timeStamp
    uint32_t trigger_counter;                // trigger count, server can check it
    uint16_t drop_trigger_count;             // drop_count cause network congestion
    uint16_t masked_trig_count;              // mask trigger caused dead time, dead time(1000 ns)
    uint32_t reserve;                        // reserve
    uint32_t tailer;                         // 0x5461696C
};

// Function declarations
uint64_t htonll(uint64_t value);
uint64_t ntohll(uint64_t value);
uint64_t gloabl_time_offset = 0x2fa600000000000;
// Global variables
struct Packet packets;

//Gl vector
int event_rd = 0;
int size = 30001 * 16;
int file_num = 0;
int event_waitflag = 1;
int triggercnt = 0;

uint32_t last_trigger_time = 0;

// int last_event_rd = 0;
// first event
// 1 = 0x0 ~ 0x20000000 
// 0 = 0x20000000 ~ 0x40000000

// Function to convert a 64-bit unsigned integer from host byte order to network byte order
uint64_t htonll(uint64_t value) {
    // Check if the system is little-endian
    const int num = 42;
    if (*(const char *)&num == 42) {
    // Convert from little-endian to big-endian manually
    return ((uint64_t)htonl(value & 0xFFFFFFFF) << 32) | htonl(value >> 32);
    } else {
    // System is big-endian, no conversion needed
    return value;
    }
}

// Function to convert a 64-bit unsigned integer from network byte order to host byte order
uint64_t ntohll(uint64_t value) {
    // Check if the system is little-endian
    const int num = 42;
    if (*(const char *)&num == 42) {
    // Convert from big-endian to little-endian manually
    return ((uint64_t)ntohl(value & 0xFFFFFFFF) << 32) | ntohl(value >> 32);
    } else {
    // System is big-endian, no conversion needed
    return value;
    }
}

uint64_t combine_uint32(uint32_t high, uint32_t low) {
    return ((uint64_t)high << 32) | low;
}


void makeFolder(char *folderName) {
    // 创建文件夹
    int status = mkdir(folderName, 0777); // 创建文件夹权限设置为777,即读、写、执行权限
    if (status == -1) {
    printf("创建文件夹失败\n");
    exit(1);
}
}

void read_data(int fd,int *c2h_align_mem,const char *file_name, int offset, int client_fd, int file_num, int delay_time)
{
    if (fd<0)
    {
        printf("open failed");
        printf("%d",fd);
    }
    else
    {
        printf("open c2h\n");
    }
    if (file_num > 0){
        FILE *record_fp = fopen(file_name, "wb");
        lseek(fd,offset,SEEK_SET);
        read(fd, c2h_align_mem, size);
        // fwrite(c2h_align_mem, size, 1, record_fp);
        // read(fd, c2h_align_mem, size);
        fwrite(c2h_align_mem + 4, size - 16, 1, record_fp);
        
        float triggerRate = 1000. * 1e9 / (8 * (c2h_align_mem[4 * 1001 + 1] - c2h_align_mem[4 * 1 + 1]));
        printf("trigger rate : %f Hz",triggerRate);

        packets.header = htonl(0x48454144);
        packets.trigger_type = htonl(0x0000);
        packets.sn_alert = htonl(0x0000);
        // packets.timestamp = htonll(125 * i * time_interval);
        // packets.trigger_counter = htonl(i);
        packets.drop_trigger_count = htonl(0x00000000);
        packets.masked_trig_count = htonl(0x0000);
        packets.reserve = htonl(0x00000000);
        packets.tailer = htonl(0x5441494c);

        int i;
        for (i = 1; i < 30001; i++)
        {   
            uint32_t lowtime = c2h_align_mem[4 * i + 1];
            if ((lowtime - last_trigger_time) < 88){
                continue;
            }else{
                uint32_t hightime = c2h_align_mem[4 * i + 2];
                // printf("%02X ",c2h_align_mem[4 * i - 1]);
                // packets.timestamp = htonll(c2h_align_mem[4 * i - 1]);
                uint64_t time = combine_uint32(hightime, lowtime) - delay_time;
                time = time + gloabl_time_offset;
                packets.timestamp = htonll(time);
                packets.trigger_counter = htonl(triggercnt);
                ssize_t bytes_sent = send(client_fd, &packets, sizeof(packets), 0);
                triggercnt = triggercnt + 1;
                last_trigger_time = lowtime;
            }
        }
    }
    else{
        lseek(fd,offset,SEEK_SET);
        read(fd, c2h_align_mem, size);
    }
    printf("\n%s\n",file_name);
}

void read_data_pure(int fd,int *c2h_align_mem,const char *file_name, int offset, int file_num)
{
    if (fd<0)
    {
        printf("open failed");
        printf("%d",fd);
    }
    else
    {
        printf("open c2h\n");
    }
    if (file_num > 0){
        FILE *record_fp = fopen(file_name, "wb");
        lseek(fd,offset,SEEK_SET);
        read(fd, c2h_align_mem, size);
        fwrite(c2h_align_mem + 4, size - 16, 1, record_fp);
        
        float triggerRate = 1000. * 1e9 / (8 * (c2h_align_mem[4 * 1001 + 1] - c2h_align_mem[4 * 1 + 1]));
        printf("trigger rate : %f Hz",triggerRate);
        // int i;
        // for (i = 1; i < 10; i++)
        // {   
        //     // uint64_t time = (uint64_t)c2h_align_mem[4 * i - 1];
        //     printf("%02X ",c2h_align_mem[4 * i + 1]);
        // }
        printf("\n%s\n",file_name);
    }
    else{
        lseek(fd,offset,SEEK_SET);
        read(fd, c2h_align_mem, size);
    }
}

void *c2h_data_process(int fd_c2h,int fd_usr, int *c2h_align_mem, int client_fd, int delay_time)
{   

    
    //读取的数据写文件
    char file[256];
    char file_path[]="data/";
    char file_pack[]=".bin";
    sprintf(file,"%.100s%d%.30s",file_path,file_num,file_pack);
    int offset;

    // read restart
    char *reg_wr_0[]={"0","/dev/xdma0_user","0x4000","w","1"};
    char *reg_wr_1[]={"0","/dev/xdma0_user","0x4000","w","0"};

    //read event

    char *reg_rd[]={"0","/dev/xdma0_user","0x4000","w"};

    event_rd = reg_rw(4,reg_rd,fd_usr);
    switch(event_rd){
        case 1: 
            if (event_waitflag == 0){
                offset = 0x0;
                read_data(fd_c2h, c2h_align_mem, file, offset, client_fd, file_num, delay_time);
                event_waitflag = 1;
                printf("read done ram1!\n");
                file_num = file_num + 1;
                break;
            }
            else{
                break;
            }
        case 0:
            if (event_waitflag == 1){
                offset = 0x800000;
                read_data(fd_c2h, c2h_align_mem, file, offset, client_fd, file_num, delay_time);
                event_waitflag = 0;
                printf("read done ram2!\n");
                file_num = file_num + 1;
                break;
            }
        default: 
            break;
    }
}

void *c2h_data_process_pure(int fd_c2h,int fd_usr, int *c2h_align_mem)
{
    //读取的数据写文件
    char file[256];
    char file_path[]="data/";
    char file_pack[]=".bin";
    sprintf(file,"%.100s%d%.30s",file_path,file_num,file_pack);
    int offset;

    // read restart
    char *reg_wr_0[]={"0","/dev/xdma0_user","0x4000","w","1"};
    char *reg_wr_1[]={"0","/dev/xdma0_user","0x4000","w","0"};

    //read event

    char *reg_rd[]={"0","/dev/xdma0_user","0x4000","w"};

    event_rd = reg_rw(4,reg_rd,fd_usr);
    switch(event_rd){
        case 1: 
            if (event_waitflag == 0){
                offset = 0x0;
                read_data_pure(fd_c2h, c2h_align_mem, file, offset, file_num);
                event_waitflag = 1;
                printf("read done ram1!\n");
                file_num = file_num + 1;
                break;
            }
            else{
                break;
            }
        case 0:
            if (event_waitflag == 1){
                offset = 0x800000;
                read_data_pure(fd_c2h, c2h_align_mem, file, offset, file_num);
                event_waitflag = 0;
                printf("read done ram2!\n");
                file_num = file_num + 1;
                break;
            }
        default: 
            break;
    }
}


int main(int argc, char *argv[])
{   
    srand(time(NULL));
    if (argc < 4)
    {
        fprintf(stderr, "Usage: %s <daq_connect> <file_num> <delaytime>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    int daq_connect = atoi(argv[1]);
    int File_NUM = atoi(argv[2]);
    int delay_time =atoi(argv[3]);

    if (daq_connect == 0){
        setbuf(stdout,NULL);
        int fd_c2h = open("/dev/xdma0_c2h_0",O_RDWR);
        int fd_usr = open("/dev/xdma0_user",O_RDWR);
        char *reg_restart0[]={"0","/dev/xdma0_user","0x4000","w","1"};
        char *reg_restart1[]={"0","/dev/xdma0_user","0x4000","w","0"};
        reg_rw(5, reg_restart0,fd_usr);
        reg_rw(5, reg_restart1,fd_usr);
        
        while(1) {
            if (file_num < File_NUM) {
                int *c2h_align_mem = (int*)malloc(size);
                c2h_data_process_pure(fd_c2h,fd_usr,c2h_align_mem);
                // printf ("c2h_align_mem[0]: %d", c2h_align_mem[0]);
                free(c2h_align_mem);
            } else {
                break;
            }
        }
        printf ("\nWrite done! %d files",file_num);
    }else if (daq_connect == 1){

        // // Instantiate the server address
        // struct sockaddr_in server_addr;
        // int client_fd;

        // // Create TCP socket
        // if ((client_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        //     perror("socket failed");
        //     exit(EXIT_FAILURE);
        // }

        // // Set the server address struct 
        // memset(&server_addr, 0, sizeof(server_addr));
        // server_addr.sin_family = AF_INET;
        // server_addr.sin_port = htons(PORT);


        // // // Set the server address
        // // if (inet_pton(AF_INET, "10.7.50.123", &server_addr.sin_addr) <= 0) {
        // //     perror("inet_pton failed");
        // // }

        // // Set the server address
        // if (inet_pton(AF_INET, "10.7.35.42", &server_addr.sin_addr) <=Quick Access 0) {
        //     perror("inet_pton failed");
        // }

        // // Connect to server
        // if (connect(client_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        //     perror("connect failed");
        //     exit(EXIT_FAILURE);
        // }

        // printf("Connect to server\n");

        // setbuf(stdout,NULL);

        // int fd_c2h = open("/dev/xdma0_c2h_0",O_RDWR);
        // int fd_usr = open("/dev/xdma0_user",O_RDWR);
        // char *reg_restart0[]={"0","/dev/xdma0_user","0x4000","w","1"};
        // char *reg_restart1[]={"0","/dev/xdma0_user","0x4000","w","0"};
        // reg_rw(5, reg_restart0,fd_usr);
        // reg_rw(5, reg_restart1,fd_usr);
        
        // while(1) {
        //     if (file_num < File_NUM) {
        //         int *c2h_align_mem = (int*)malloc(size);
        //         c2h_data_process(fd_c2h,fd_usr,c2h_align_mem, client_fd, delay_time);
        //         // printf ("c2h_align_mem[0]: %d", c2h_align_mem[0]);
        //         free(c2h_align_mem);
        //     } else {
        //         break;
        //     }
        // }
        // printf ("\nWrite done! %d files",file_num);
        // close(client_fd);


        // Instantiate the server address
        struct sockaddr_in server_addr, client_addr;
        int server_fd, client_fd;
        socklen_t client_len;

        // Create TCP socket
        if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
            perror("socket failed");
            exit(EXIT_FAILURE);
        }

        // Set the server address struct
        memset(&server_addr, 0, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_addr.s_addr = INADDR_ANY; // Listen on all available interfaces
        server_addr.sin_port = htons(PORT);

        // Bind socket to the address and port
        if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
            perror("bind failed");
            exit(EXIT_FAILURE);
        }

        // Put the socket in passive mode to listen for incoming connections
        if (listen(server_fd, 3) < 0) { // 3 is the maximum length of the pending connections queue
            perror("listen failed");
            exit(EXIT_FAILURE);
        }

        printf("Server listening on port %d\n", PORT);

        // Accept incoming connection
        client_len = sizeof(client_addr);
        if ((client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len)) < 0) {
        perror("accept failed");
        exit(EXIT_FAILURE);
        }

        printf("Connection accepted from %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

        setbuf(stdout,NULL);

        int fd_c2h = open("/dev/xdma0_c2h_0",O_RDWR);
        int fd_usr = open("/dev/xdma0_user",O_RDWR);
        char *reg_restart0[]={"0","/dev/xdma0_user","0x4000","w","1"};
        char *reg_restart1[]={"0","/dev/xdma0_user","0x4000","w","0"};
        reg_rw(5, reg_restart0,fd_usr);
        reg_rw(5, reg_restart1,fd_usr);
        
        while(1) {
            if (file_num < File_NUM) {
                int *c2h_align_mem = (int*)malloc(size);
                c2h_data_process(fd_c2h,fd_usr,c2h_align_mem, client_fd, delay_time);
                // printf ("c2h_align_mem[0]: %d", c2h_align_mem[0]);
                free(c2h_align_mem);
            } else {
                break;
            }
        }
        printf ("\nWrite done! %d files",file_num);
        close(client_fd);
        close(server_fd);
    }else{
        perror("daq_connect error");
        exit(EXIT_FAILURE);
    }

    return 0;
}