#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/mman.h>
#define MAIN_PREFIX   "main : "
#define WTASK_PREFIX  "write_task: "
#define RTASK_PREFIX  "read_task: "
#define WRITE_FILE    "rtser0"
#define READ_FILE     "rtser1"
int read_fd  = -1;
int write_fd = -1;
#define STATE_FILE_OPENED         1
#define STATE_TASK_CREATED        2
unsigned int read_state = 0;
unsigned int write_state = 0;
RTIME write_task_period_ns =    100000000llu;
RT_TASK write_task;
RT_TASK read_task;
        .baud_rate         = 115200,
        .parity            = RTSER_DEF_PARITY,
        .data_bits         = RTSER_DEF_BITS,
        .handshake         = RTSER_DEF_HAND,
        .fifo_depth        = RTSER_DEF_FIFO_DEPTH,
        .rx_timeout        = RTSER_DEF_TIMEOUT,
        .tx_timeout        = RTSER_DEF_TIMEOUT,
        .event_timeout     = 1000000000, 
        .timestamp_history = RTSER_RX_TIMESTAMP_HISTORY,
        .event_mask        = RTSER_EVENT_RXPEND,
};
        .
config_mask       = RTSER_SET_BAUD | RTSER_SET_TIMESTAMP_HISTORY,
 
        .baud_rate         = 115200,
        .timestamp_history = RTSER_DEF_TIMESTAMP_HISTORY,
        
};
static int close_file( int fd, char *name)
{
        int err, i=0;
        do {
                i++;
                err = rt_dev_close(fd);
                switch (err) {
                case -EAGAIN:
                        printf(MAIN_PREFIX "%s -> EAGAIN (%d times)\n",
                               name, i);
                        break;
                case 0:
                        printf(MAIN_PREFIX "%s -> closed\n", name);
                        break;
                default:
                        printf(MAIN_PREFIX "%s -> %s\n", name,
                               strerror(-err));
                        break;
                }
        } while (err == -EAGAIN && i < 10);
        return err;
}
void cleanup_all(void)
{
        if (read_state & STATE_FILE_OPENED) {
                close_file(read_fd, READ_FILE" (read)");
                read_state &= ~STATE_FILE_OPENED;
        }
        if (write_state & STATE_FILE_OPENED) {
                close_file(write_fd, WRITE_FILE " (write)");
                write_state &= ~STATE_FILE_OPENED;
        }
        if (write_state & STATE_TASK_CREATED) {
                printf(MAIN_PREFIX "delete write_task\n");
                write_state &= ~STATE_TASK_CREATED;
        }
        if (read_state & STATE_TASK_CREATED) {
                printf(MAIN_PREFIX "delete read_task\n");
                read_state &= ~STATE_TASK_CREATED;
        }
}
void catch_signal(int sig)
{
        cleanup_all();
        printf(MAIN_PREFIX "exit\n");
        return;
}
void write_task_proc(void *arg)
{
        int err;
        RTIME write_time;
        ssize_t sz = sizeof(RTIME);
        ssize_t written = 0;
        if (err) {
                printf(WTASK_PREFIX "error on set periodic, %s\n",
                       strerror(-err));
                goto exit_write_task;
        }
        while (1) {
                if (err) {
                        printf(WTASK_PREFIX
                               "error on rt_task_wait_period, %s\n",
                               strerror(-err));
                        break;
                }
                written = rt_dev_write(write_fd, &write_time, sz);
                if (written < 0 ) {
                        printf(WTASK_PREFIX "error on rt_dev_write, %s\n",
                               strerror(-err));
                        break;
                } else if (written != sz) {
                        printf(WTASK_PREFIX "only %d / %d byte transmitted\n",
                               written, sz);
                        break;
                }
        }
 exit_write_task:
        if ((write_state & STATE_FILE_OPENED) &&
            close_file(write_fd, WRITE_FILE " (write)") == 0)
                write_state &= ~STATE_FILE_OPENED;
        printf(WTASK_PREFIX "exit\n");
}
void read_task_proc(void *arg)
{
        int err;
        int nr = 0;
        RTIME read_time  = 0;
        RTIME write_time = 0;
        RTIME irq_time   = 0;
        ssize_t sz = sizeof(RTIME);
        ssize_t read = 0;
        printf(" Nr |   write->irq    |    irq->read    |   write->read   |\n");
        printf("-----------------------------------------------------------\n");
        
        while (1) {
                
                if (err) {
                        printf(RTASK_PREFIX
                               "error on RTSER_RTIOC_WAIT_EVENT, %s\n",
                               strerror(-err));
                        if (err == -ETIMEDOUT)
                                continue;
                        break;
                }
                irq_time = rx_event.rxpend_timestamp;
                read = rt_dev_read(read_fd, &write_time, sz);
                if (read == sz) {
                        printf("%3d |%16llu |%16llu |%16llu\n", nr,
                               irq_time  - write_time,
                               read_time - irq_time,
                               read_time - write_time);
                        nr++;
                } else if (read < 0 ) {
                        printf(RTASK_PREFIX "error on rt_dev_read, code %s\n",
                               strerror(-err));
                        break;
                } else {
                        printf(RTASK_PREFIX "only %d / %d byte received \n",
                               read, sz);
                        break;
                }
        }
        if ((read_state & STATE_FILE_OPENED) &&
            close_file(read_fd, READ_FILE " (read)") == 0)
                read_state &= ~STATE_FILE_OPENED;
        printf(RTASK_PREFIX "exit\n");
}
int main(int argc, char* argv[])
{
        int err = 0;
        signal(SIGTERM, catch_signal);
        signal(SIGINT, catch_signal);
        
        mlockall(MCL_CURRENT | MCL_FUTURE);
        
        write_fd = rt_dev_open( WRITE_FILE, 0);
        if (write_fd < 0) {
                printf(MAIN_PREFIX "can't open %s (write), %s\n", WRITE_FILE,
                       strerror(-write_fd));
                goto error;
        }
        write_state |= STATE_FILE_OPENED;
        printf(MAIN_PREFIX "write-file opened\n");
        
        if (err) {
                printf(MAIN_PREFIX "error while RTSER_RTIOC_SET_CONFIG, %s\n",
                       strerror(-err));
                goto error;
        }
        printf(MAIN_PREFIX "write-config written\n");
        
        read_fd = rt_dev_open( READ_FILE, 0 );
        if (read_fd < 0) {
                printf(MAIN_PREFIX "can't open %s (read), %s\n", READ_FILE,
                       strerror(-read_fd));
                goto error;
        }
        read_state |= STATE_FILE_OPENED;
        printf(MAIN_PREFIX "read-file opened\n");
        
        if (err) {
                printf(MAIN_PREFIX "error while rt_dev_ioctl, %s\n",
                       strerror(-err));
                goto error;
        }
        printf(MAIN_PREFIX "read-config written\n");
        
        if (err) {
                printf(MAIN_PREFIX "failed to create write_task, %s\n",
                       strerror(-err));
                goto error;
        }
        write_state |= STATE_TASK_CREATED;
        printf(MAIN_PREFIX "write-task created\n");
        
        if (err) {
                printf(MAIN_PREFIX "failed to create read_task, %s\n",
                       strerror(-err));
                goto error;
        }
        read_state |= STATE_TASK_CREATED;
        printf(MAIN_PREFIX "read-task created\n");
        
        printf(MAIN_PREFIX "starting write-task\n");
        if (err) {
                printf(MAIN_PREFIX "failed to start write_task, %s\n",
                       strerror(-err));
                goto error;
        }
        
        printf(MAIN_PREFIX "starting read-task\n");
        if (err) {
                printf(MAIN_PREFIX "failed to start read_task, %s\n",
                       strerror(-err));
                goto error;
        }
        pause();
        return 0;
 error:
        cleanup_all();
        return err;
}