#include "CSerialPort/SerialPort.h"

#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <sys/select.h>
#include <cerrno>
#include <cstring>

namespace itas109 {

CSerialPort::CSerialPort()
    : m_baudRate(115200)
    , m_parity(Parity::ParityNone)
    , m_dataBits(DataBits::DataBits8)
    , m_stopBits(StopBits::StopOne)
    , m_flowControl(FlowControl::FlowNone)
    , m_readTimeoutMs(1000)
    , m_fd(-1)
    , m_lastError(0)
    , m_isOpen(false)
{
    std::memset(m_portName, 0, sizeof(m_portName));
}

CSerialPort::~CSerialPort() {
    close();
}

void CSerialPort::init(const char* portName,
                       int         baudRate,
                       Parity      parity,
                       DataBits    dataBits,
                       StopBits    stopBits,
                       FlowControl flowControl,
                       int         readTimeoutMs)
{
    std::strncpy(m_portName, portName, sizeof(m_portName) - 1);
    m_portName[sizeof(m_portName) - 1] = '\0';
    m_baudRate      = baudRate;
    m_parity        = parity;
    m_dataBits      = dataBits;
    m_stopBits      = stopBits;
    m_flowControl   = flowControl;
    m_readTimeoutMs = readTimeoutMs;
}

bool CSerialPort::open() {
    if (m_isOpen) close();

    m_fd = ::open(m_portName, O_RDWR | O_NOCTTY | O_NONBLOCK);
    if (m_fd < 0) {
        m_lastError = errno;
        return false;
    }

    struct termios tty;
    std::memset(&tty, 0, sizeof(tty));
    if (tcgetattr(m_fd, &tty) != 0) {
        m_lastError = errno;
        ::close(m_fd);
        m_fd = -1;
        return false;
    }

    // 波特率
    speed_t speed = static_cast<speed_t>(toBaudRate(m_baudRate));
    cfsetispeed(&tty, speed);
    cfsetospeed(&tty, speed);

    // 数据位
    tty.c_cflag &= ~CSIZE;
    switch (m_dataBits) {
        case DataBits::DataBits5: tty.c_cflag |= CS5; break;
        case DataBits::DataBits6: tty.c_cflag |= CS6; break;
        case DataBits::DataBits7: tty.c_cflag |= CS7; break;
        case DataBits::DataBits8:
        default:                  tty.c_cflag |= CS8; break;
    }

    // 停止位
    if (m_stopBits == StopBits::StopTwo)
        tty.c_cflag |= CSTOPB;
    else
        tty.c_cflag &= ~CSTOPB;

    // 校验位
    switch (m_parity) {
        case Parity::ParityOdd:
            tty.c_cflag |=  PARENB;
            tty.c_cflag |=  PARODD;
            tty.c_iflag |=  (INPCK | ISTRIP);
            break;
        case Parity::ParityEven:
            tty.c_cflag |=  PARENB;
            tty.c_cflag &= ~PARODD;
            tty.c_iflag |=  (INPCK | ISTRIP);
            break;
        case Parity::ParityNone:
        default:
            tty.c_cflag &= ~PARENB;
            tty.c_iflag &= ~(INPCK | ISTRIP);
            break;
    }

    // 流控
    if (m_flowControl == FlowControl::FlowHardware)
        tty.c_cflag |= CRTSCTS;
    else
        tty.c_cflag &= ~CRTSCTS;

    if (m_flowControl == FlowControl::FlowSoftware)
        tty.c_iflag |= (IXON | IXOFF | IXANY);
    else
        tty.c_iflag &= ~(IXON | IXOFF | IXANY);

    // 本地模式 & 输入模式（raw）
    tty.c_cflag |=  (CREAD | CLOCAL);
    tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL);
    tty.c_oflag &= ~OPOST;

    // 非阻塞读，超时由 select 控制
    tty.c_cc[VMIN]  = 0;
    tty.c_cc[VTIME] = 0;

    if (tcsetattr(m_fd, TCSANOW, &tty) != 0) {
        m_lastError = errno;
        ::close(m_fd);
        m_fd = -1;
        return false;
    }

    tcflush(m_fd, TCIOFLUSH);
    m_isOpen    = true;
    m_lastError = 0;
    return true;
}

void CSerialPort::close() {
    if (m_fd >= 0) {
        ::close(m_fd);
        m_fd = -1;
    }
    m_isOpen = false;
}

int CSerialPort::writeData(const uint8_t* data, size_t length) {
    if (!m_isOpen || m_fd < 0) {
        m_lastError = EBADF;
        return -1;
    }
    ssize_t written = ::write(m_fd, data, length);
    if (written < 0) {
        m_lastError = errno;
        return -1;
    }
    tcdrain(m_fd);  // 等待发送完成
    return static_cast<int>(written);
}

int CSerialPort::readData(uint8_t* buffer, size_t maxLength) {
    if (!m_isOpen || m_fd < 0) {
        m_lastError = EBADF;
        return -1;
    }

    // 用 select 实现超时
    struct timeval tv;
    tv.tv_sec  = m_readTimeoutMs / 1000;
    tv.tv_usec = (m_readTimeoutMs % 1000) * 1000;

    fd_set fds;
    FD_ZERO(&fds);
    FD_SET(m_fd, &fds);

    int ret = select(m_fd + 1, &fds, nullptr, nullptr, &tv);
    if (ret <= 0) {
        // 超时或错误
        m_lastError = (ret == 0) ? ETIME : errno;
        return 0;
    }

    ssize_t bytesRead = ::read(m_fd, buffer, maxLength);
    if (bytesRead < 0) {
        m_lastError = errno;
        return -1;
    }
    m_lastError = 0;
    return static_cast<int>(bytesRead);
}

int CSerialPort::toBaudRate(int baud) {
    switch (baud) {
        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 921600: return B921600;
        default:     return B115200;
    }
}

} // namespace itas109
