#include "uart.hpp"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>

#include <iostream>

static ssize_t uart_write(int __fd, const void *__buf, size_t __n)
{
    return write(__fd, __buf, __n);
}

static ssize_t uart_read(int __fd, void *__buf, size_t __nbytes)
{
    return read(__fd, __buf, __nbytes);
}

namespace awe
{
Uart::Uart(const char * dev) : fd(0)
{
    char tty[64] = "";

    strcat(tty, dev);


    fd = open(tty, O_RDWR | O_NOCTTY); 
    //fd = open(tty, O_RDWR | O_NOCTTY | O_NDELAY); 
    // if (fd > 0) {
    //     printf("open %s!\n", tty);
    // }
}

Uart::~Uart()
{
    if (fd > 0)
        close(fd);
}

int Uart::setOpt(uint64_t speed, int bits, char event, int stop)
{
    if (fd < 0) {
        return -1;
        perror("Uart Init failed");
    }

    fcntl(fd, F_SETFL, 0);

    struct termios newtio, oldtio;
    if ( tcgetattr( fd, &oldtio ) != 0) {
        perror("tcgetattr error");
        return -1;
    }

    tcflush(fd, TCIOFLUSH);

    bzero( &newtio, sizeof(newtio) );
    newtio.c_cflag |= CLOCAL | CREAD;
    newtio.c_cflag &= ~CSIZE;

    newtio.c_lflag &= ~(ICANON | ISIG | ECHO | ECHOE | IEXTEN);
    newtio.c_oflag &= ~OPOST;
    newtio.c_iflag &= ~(IXON | IXOFF | IXANY | ICRNL | BRKINT | INPCK | ISTRIP);
    newtio.c_cflag &= ~CRTSCTS;

    switch ( bits ) {
        case 7:
            newtio.c_cflag |= CS7;
            break;
        case 8:
            newtio.c_cflag |= CS8;
            break;
    }

    switch (event) {
        case 'O':
            newtio.c_cflag |= PARENB;
            newtio.c_cflag |= PARODD;
            newtio.c_iflag |= (INPCK | ISTRIP);
            break;
        case 'E':
            newtio.c_cflag |= PARENB;
            newtio.c_cflag &= ~PARODD;
            newtio.c_iflag |= (INPCK | ISTRIP);
            break;
        case 'N':
            newtio.c_cflag &= ~PARENB;
            break;
    }

    switch (speed) {
        case 4800:
            cfsetispeed(&newtio, B4800);
            cfsetospeed(&newtio, B4800);
            break;
        default:
        case 9600:
            cfsetispeed(&newtio, B9600);
            cfsetospeed(&newtio, B9600);
            break;
        case 19200:
            cfsetispeed(&newtio, B19200);
            cfsetospeed(&newtio, B19200);
            break;
        case 38400:
            cfsetispeed(&newtio, B38400);
            cfsetospeed(&newtio, B38400);
            break;
        case 57600:
            cfsetispeed(&newtio, B57600);
            cfsetospeed(&newtio, B57600);
            break;
        case 115200:
            cfsetispeed(&newtio, B115200);
            cfsetospeed(&newtio, B115200);
            break;
        case 230400:
            cfsetispeed(&newtio, B230400);
            cfsetospeed(&newtio, B230400);
            break;
        case 460800:
            cfsetispeed(&newtio, B460800);
            cfsetospeed(&newtio, B460800);
            break;
        case 500000:
            cfsetispeed(&newtio, B500000);
            cfsetospeed(&newtio, B500000);
            break;
        case 576000:
            cfsetispeed(&newtio, B576000);
            cfsetospeed(&newtio, B576000);
            break;
        case 921600:
            cfsetispeed(&newtio, B921600);
            cfsetospeed(&newtio, B921600);
            break;
        case 1000000:
            cfsetispeed(&newtio, B1000000);
            cfsetospeed(&newtio, B1000000);
            break;
        case 1152000:
            cfsetispeed(&newtio, B1152000);
            cfsetospeed(&newtio, B1152000);
            break;
        case 1500000:
            cfsetispeed(&newtio, B1500000);
            cfsetospeed(&newtio, B1500000);
            break;
        case 2000000:
            cfsetispeed(&newtio, B2000000);
            cfsetospeed(&newtio, B2000000);
            break;
        case 2500000:
            cfsetispeed(&newtio, B2500000);
            cfsetospeed(&newtio, B2500000);
            break;
        case 3000000:
            cfsetispeed(&newtio, B3000000);
            cfsetospeed(&newtio, B3000000);
            break;
        case 3500000:
            cfsetispeed(&newtio, B3500000);
            cfsetospeed(&newtio, B3500000);
            break;
        case 4000000:
            cfsetispeed(&newtio, B4000000);
            cfsetospeed(&newtio, B4000000);
            break;
    }

    if (stop == 1) {
        newtio.c_cflag &= ~CSTOPB;
    }
    else if (stop == 2) {
        newtio.c_cflag |= CSTOPB;
    }

    newtio.c_cc[VTIME] = 5;
    newtio.c_cc[VMIN] = 0;

    

    if ((tcsetattr(fd, TCSANOW, &newtio)) != 0) {
        perror("set opt error");
        return -1;
    }

    tcflush(fd, TCIFLUSH);

    return 0;
}

int Uart::write(uint8_t * buf, size_t size)
{
    if (fd < 0) {
        return -1;
        perror("Uart Init failed");
    }

    int ret = uart_write(fd, buf, size);
    if (ret < 0) {
        perror("uart write failed");
    }
    return ret;
}

int Uart::read(uint8_t * buf, size_t size) 
{
    if (fd < 0) {
        return -1;
        perror("Uart Init failed");
    }
    
    fd_set set;
    struct timeval timeout;

    FD_ZERO(&set);
    FD_SET(fd, &set);

    timeout.tv_sec = 0;
    timeout.tv_usec = 0;

    int ret = select(fd + 1, &set, NULL, NULL, &timeout);

    if (ret > 0) {
        ret = uart_read(fd, buf, size);
    }
  
    return ret;
}

int Uart::getFd()
{
    return fd;
}

}