#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <termios.h>
#include <linux/serial.h>

#include "uart.h"
#include "dy_utils/dy_common.h"

int speed_arr[] = {
    B500000,
    B460800,
    B230400,
    B115200,
    B57600,
    B38400,
    B19200,
    B9600,
    B4800,
    B2400,
    B1200,
    B300,
    B57600,
    B38400,
    B19200,
    B9600,
    B4800,
    B2400,
    B1200,
    B300,
};
int name_arr[] = {
    500000,
    460800,
    230400,
    115200,
    57600,
    38400,
    19200,
    9600,
    4800,
    2400,
    1200,
    300,
    57600,
    38400,
    19200,
    9600,
    4800,
    2400,
    1200,
    300,
};

/***@brief  设置uart的speed
*@param  fd      int  UART口的文件描述符  
*@param  speed   int  波特率
*@return  void*/
void uart_set_speed(int fd, int speed)
{
    int i;
    int status;
    struct termios Opt;
    tcgetattr(fd, &Opt);
    for (i = 0; i < sizeof(speed_arr) / sizeof(int); i++)
    {
        if (speed == name_arr[i])
        {
            tcflush(fd, TCIOFLUSH);
            cfsetispeed(&Opt, speed_arr[i]);
            status = cfsetospeed(&Opt, speed_arr[i]);
            printf("set speed %d, status %d\n", speed, status);
            status = tcsetattr(fd, TCSANOW, &Opt);
            if (status != 0)
            {
                dy_syslog(LOG_ERR, "tcsetattr fd1, errno %d\n", errno);
            }
            return;
        }
        tcflush(fd, TCIOFLUSH);
    }
}

static speed_t get_baudrate(int baudrate)
{
    switch (baudrate)
    {
    case 0:
        return B0;
    case 50:
        return B50;
    case 75:
        return B75;
    case 110:
        return B110;
    case 134:
        return B134;
    case 150:
        return B150;
    case 200:
        return B200;
    case 300:
        return B300;
    case 600:
        return B600;
    case 1200:
        return B1200;
    case 1800:
        return B1800;
    case 2400:
        return B2400;
    case 4800:
        return B4800;
    case 9600:
        return B9600;
    case 19200:
        return B19200;
    case 38400:
        return B38400;
    case 57600:
        return B57600;
    case 115200:
        return B115200;
    case 230400:
        return B230400;
    case 460800:
        return B460800;
    case 500000:
        return B500000;
    case 576000:
        return B576000;
    case 921600:
        return B921600;
    case 1000000:
        return B1000000;
    case 1152000:
        return B1152000;
    case 1500000:
        return B1500000;
    case 2000000:
        return B2000000;
    case 2500000:
        return B2500000;
    case 3000000:
        return B3000000;
    case 3500000:
        return B3500000;
    case 4000000:
        return B4000000;
    default:
        return -1;
    }
}

int uart_config(int fd, int databits, int stopbits, int parity, int baudrate)
{
    speed_t speed;
    struct termios Opt;

    if (tcgetattr(fd, &Opt) != 0)
    {
        dy_syslog(LOG_INFO, "uart_set tcgetattr init fail.");
        return -1;
    }

    Opt.c_cflag |= (CLOCAL | CREAD);

    switch (databits)
    {
    case 7:
        Opt.c_cflag &= ~CSIZE;
        Opt.c_cflag |= CS7;
        break;
    case 8:
        Opt.c_cflag &= ~CSIZE;
        Opt.c_cflag |= CS8;
        break;
    default:
        dy_syslog(LOG_INFO, "uart_set Unsupported databits.");
        return -1;
    }

    switch (parity)
    {
    case 'n':
    case 'N':
        Opt.c_cflag &= ~PARENB;
        Opt.c_iflag &= ~INPCK;
        break;
    case 'o':
    case 'O':
        Opt.c_cflag |= PARENB;
        Opt.c_cflag |= PARODD;
        Opt.c_iflag |= INPCK;
        break;
    case 'e':
    case 'E':
        Opt.c_cflag |= PARENB;
        Opt.c_cflag &= ~PARODD;
        Opt.c_iflag |= INPCK;
        break;
    case 's':
    case 'S':
        Opt.c_cflag &= ~PARENB;
        Opt.c_cflag &= ~CSTOPB;
        Opt.c_iflag |= INPCK;
        break;
    default:
        dy_syslog(LOG_INFO, "uart_set Unsupported parity.");
        return -1;
    }

    switch (stopbits)
    {
    case 1:
        Opt.c_cflag &= ~CSTOPB;
        break;
    case 2:
        Opt.c_cflag |= CSTOPB;
        break;
    default:
        dy_syslog(LOG_INFO, "uart_set Unsupported stopbits.");
        return -1;
    }

    speed = get_baudrate(baudrate);
    if ((int)speed == -1)
    {
        /* TODO: throw an exception */
        dy_syslog(LOG_INFO, "uart_set Invalid baudrate.");
        return -1;
    }
    tcflush(fd, TCIOFLUSH);
    cfsetispeed(&Opt, speed);
    cfsetospeed(&Opt, speed);

    Opt.c_cflag |= (CLOCAL | CREAD);

    Opt.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

    Opt.c_oflag &= ~OPOST;
    Opt.c_oflag &= ~(ONLCR | OCRNL);

    Opt.c_iflag &= ~(ICRNL | INLCR);
    Opt.c_iflag &= ~(IXON | IXOFF | IXANY);

    tcflush(fd, TCIFLUSH);

    Opt.c_cc[VTIME] = 0;
    Opt.c_cc[VMIN] = 0;

    if (tcsetattr(fd, TCSANOW, &Opt) != 0)
    {
        dy_syslog(LOG_INFO, "uart_set tcgetattr TCSANOW fail.");
        return -1;
    }

    return 0;
}

/*******************************************************************
* 名称：            UART0_Recv
* 功能：            接收串口数据
* 入口参数：        fd         文件描述符
*                   rcv_buf    接收串口中数据存入rcv_buf缓冲区中
*                   data_len   一帧数据的长度
* 出口参数：        正确返回为长度，错误返回为0 -1
*******************************************************************/
int UART0_Recv(int fd, char *rcv_buf, int data_len)
{
    int fs_sel;
    fd_set fs_read;
    int len = 0;
    int count = 0;
    int ret = 0;
    struct timeval time;

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

    time.tv_sec = 3;
    time.tv_usec = 0;

    //使用select实现串口的多路通信
    fs_sel = select(fd + 1, &fs_read, NULL, NULL, &time);
    if (fs_sel)
    {
        while (1)
        {
            ret = read(fd, rcv_buf + len, data_len - len);
            if (ret > 0)
            {
                len += ret;
                count = 0;
            }
            else
            {
                count++;
                if (count < 2)
                {
                    usleep(10 * 1000);
                }
                else
                {
                    break;
                }
            }
        }
        return len;
    }
    else
    {
        return -1;
    }
}

/********************************************************************
* 名称：            UART0_Send
* 功能：            发送数据
* 入口参数：        fd           文件描述符
*                   send_buf     存放串口发送数据
*                   data_len     一帧数据的个数
* 出口参数：        正确返回为1，错误返回为0
*******************************************************************/
int UART0_Send(int fd, char *send_buf, int data_len)
{
    /** The amount of bytes written. */
    unsigned int dataWritten;
    /** The amount of bytes still needed to be written. */
    unsigned int dataToWrite = data_len;

    if (fd == -1)
    {
        return -1;
    }

    while (dataToWrite)
    {
        dataWritten = write(fd, (void *)send_buf, dataToWrite);
        if (-1 == dataWritten)
        {
            if (EAGAIN == errno)
            {
                continue;
            }
            else
            {
                tcflush(fd, TCOFLUSH);
                return -1;
            }
        }
        else
        {
            dataToWrite -= dataWritten;
            send_buf += dataWritten;
        }
    }

    return (int32_t)data_len;
}

/**
*@brief   ���ô�������λ��ֹͣλ��Ч��λ
*@param  fd     ����  int  �򿪵Ĵ����ļ����*
*@param  databits ����  int ����λ   ȡֵ Ϊ 7 ����8*
*@param  stopbits ����  int ֹͣλ   ȡֵΪ 1 ����2*
*@param  parity  ����  int  Ч������ ȡֵΪN,E,O,,S
*/
int uart_set_param(int fd, int databits, int stopbits, int parity, int flow)
{
    struct termios options;

    if (tcgetattr(fd, &options) != 0)
    {
        dy_syslog(LOG_ERR, "SetupSerial 1, errno %d\n", errno);
        return -1;
    }

    options.c_cflag &= ~CSIZE;
    switch (databits) /*��������λ��*/
    {
    case 5:
        options.c_cflag |= CS5;
        break;
    case 6:
        options.c_cflag |= CS6;
        break;
    case 7:
        options.c_cflag |= CS7;
        break;
    case 8:
        options.c_cflag |= CS8;
        break;
    default:
        dy_syslog(LOG_ERR, "Unsupported data size, errno %d\n", errno);
        return -1;
    }

    switch (parity)
    {
    case 'n':
    case 'N':
        options.c_cflag &= ~PARENB; /* Clear parity enable */
        options.c_iflag &= ~INPCK;  /* Enable parity checking */
        break;
    case 'o':
    case 'O':
        options.c_cflag |= (PARODD | PARENB); /* ����Ϊ��Ч��*/
        options.c_iflag |= INPCK;             /* Disnable parity checking */
        break;
    case 'e':
    case 'E':
        options.c_cflag |= PARENB;  /* Enable parity */
        options.c_cflag &= ~PARODD; /* ת��ΪżЧ��*/
        options.c_iflag |= INPCK;   /* Disnable parity checking */
        break;
    case 'S':
    case 's': /*as no parity*/
        options.c_cflag &= ~PARENB;
        options.c_cflag &= ~CSTOPB;
        break;
    default:
        dy_syslog(LOG_ERR, "Unsupported parity, errno %d\n", errno);
        return -1;
    }

    /* ����ֹͣλ*/
    switch (stopbits)
    {
    case 1:
        options.c_cflag &= ~CSTOPB;
        break;
    case 2:
        options.c_cflag |= CSTOPB;
        break;
    default:
        dy_syslog(LOG_ERR, "Unsupported stop bits, errno %d\n", errno);
        return -1;
    }

    /* Set input parity option */
    if (parity != 'n')
    {
        options.c_iflag |= INPCK;
    }

    //  options.c_cc[VTIME] = 150; // 15 seconds
    //  options.c_cc[VTIME] = 0;
    //  options.c_cc[VMIN] = read_len;

    if (flow == 0)
    {
        options.c_iflag &= ~(IXON | IXOFF);
    }

    if (tcsetattr(fd, TCSANOW, &options) != 0)
    {
        dy_syslog(LOG_ERR, "SetupSerial 3, errno %d\n", errno);
        return -1;
    }

    tcflush(fd, TCIOFLUSH); /* Update the options and do it NOW */

    return 0;
}
/**
*@breif �򿪴���
*/
int uart_open(char *Dev, int open_flags)
{
    struct termios opt;

    int fd = open(Dev, open_flags); //
    if (-1 == fd)
    {
        dy_syslog(LOG_ERR, "Can't Open Serial Port, errno %d\n", errno);
        return -1;
    }

    tcgetattr(fd, &opt);
    //  opt.c_lflag &= ~(ICANON | ECHO | ISIG);
    opt.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);

    /*
     * No SIGINT on BREAK, CR-to-NL off, input parity
     * check off, don't strip 8th bit on input, output
     * flow control off.
     */
    //    opt.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
    opt.c_iflag &= ~(BRKINT | ICRNL);
    opt.c_oflag &= ~(OPOST);

    tcsetattr(fd, TCSANOW, &opt);

    tcflush(fd, TCIOFLUSH);

    return fd;
}

int uart_set_raw(int fd)
{
    struct termios opt;

    tcgetattr(fd, &opt);

    opt.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | INPCK | IUCLC);
    opt.c_oflag &= ~OPOST;
    opt.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
    opt.c_cflag &= ~(CSIZE | PARENB);
    opt.c_cflag |= CS8;

    tcsetattr(fd, TCSANOW, &opt);

    tcflush(fd, TCIOFLUSH);

    return 0;
}

/*
speed:
    500000, 460800, 230400, 115200, 57600, 38400,  19200,  9600,  4800,  2400,  1200,  300,
        57600, 38400,  19200,  9600, 4800, 2400, 1200,  300
nonblock:
    �Ƿ������
*/

int uart_init(char *dev, int speed, int stop, int parity, int bits, int nonblock)
{
    int fd;
    char cparity = 'N';

    int open_flags = O_RDWR | O_NOCTTY;
    if (nonblock)
    {
        open_flags |= O_NONBLOCK;
    }

    if (parity == 1)
    {
        cparity = 'O';
    }
    else if (parity == 2)
    {
        cparity = 'E';
    }
    fd = open(dev, open_flags);
    if (fd < 0)
    {
        return -1;
    }

    if (uart_config(fd, bits, stop, cparity, speed))
    {
        dy_syslog(LOG_ERR, "uart set param error, errno %d\n", errno);
        if (fd > 0)
        {
            close(fd);
        }
        return -1;
    }
    tcflush(fd, TCIOFLUSH);

    return fd;
}

void uart_uninit(int fd)
{
    close(fd);
}
