#include "rs485_common.h"
#include "uart.h"

#include <termios.h>    /*PPSIX终端控制定义*/
#include <linux/serial.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <poll.h>

/***@brief  设置串口通信速率
*@param  fd     类型 int  打开串口的文件句柄
*@param  speed  类型 int  串口速度
*@return  void*/

static int tcp_connection_poll(int sockfd, int timeout)
{
	int ret, error;
	socklen_t slt;
	struct pollfd pfd;

again:
	pfd.fd = sockfd;
	pfd.events = POLLOUT | POLLPRI | POLLERR;
	pfd.revents = 0;
	ret = poll(&pfd, 0x1, timeout);
	if (ret < 0) {
		error = errno;
		if (error == EINTR)
			goto again;
		return -1;
	}

	if (ret == 0)
		return -2;

    error = 0;
    slt = sizeof(error);
    ret = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &slt);
	if (ret == -1)
		return -3;
	return error ? -4 : 0;
}

int uart_remote_init(const char * remote_addr, int remote_port)
{
    int sockfd, ret, err;
    struct sockaddr_in saddr;

    memset(&saddr, 0, sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons((unsigned short) remote_port);
    ret = inet_pton(AF_INET, remote_addr, (void *) &saddr.sin_addr);
    if (ret != 1) {
        dbg_syslog(LOG_ERR, "invalid RS485/modbus remote addr: %s", remote_addr);
        return -1;
    }

    sockfd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
    if (sockfd == -1) {
        err = errno;
        dbg_syslog(LOG_ERR, "Failed to create TCP socket: %s", strerror(err));
        return -1;
    }

    ret = connect(sockfd, (struct sockaddr *) &saddr, sizeof(saddr));
    if (ret == -1) {
        err = errno;
        if (err == EINPROGRESS) {
            /* timeout in 5 seconds */
            ret = tcp_connection_poll(sockfd, 5000);
            if (ret == 0)
                goto connect_ok;
            ret = errno;
            if (ret != 0)
                err = ret;
        }
        dbg_syslog(LOG_ERR, "Failed to connect to %s:%d => %s",
            remote_addr, remote_port, strerror(err));
        close(sockfd);
        return -1;
    }

connect_ok:
    err = 1;
    // Enable TCP_NODELAY option for TCP data delivery
    ret = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &err, (socklen_t) sizeof(err));
    if (ret == -1) {
        err = errno;
        dbg_syslog(LOG_ERR, "Failed to enable TCP_NODELAY option: %s\n", strerror(err));
    }
    return sockfd;
}

int uart_reset_termios(int pfd)
{
    int ret;
    struct termios tios;

    memset(&tios, 0, sizeof(tios));
    ret = tcgetattr(pfd, &tios);
    if (ret == -1) {
        /* sliently ignore the error */
        return -1;
    }

    ret = tcsetattr(pfd, TCSANOW, &tios);
    return ret;
}

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,
                 };

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)
    {
        dbg_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:
            dbg_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:
            dbg_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:
            dbg_syslog(LOG_INFO, "uart_set Unsupported stopbits.");
            return -1;
    }

    speed = get_baudrate(baudrate);
    if ((int) speed == -1)
    {
        /* TODO: throw an exception */
        dbg_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)
    {
        dbg_syslog(LOG_INFO, "uart_set tcgetattr TCSANOW fail.");
        return -1;
    }

    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(const 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, O_RDWR | O_NOCTTY | O_NONBLOCK);
    if (fd < 0)
    {
        return -1;
    }

    if (uart_config(fd, bits, stop, cparity, speed))
    {
        dbg_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);
}
