#include "serialComm.hpp"
#include <cstring>
#include <cstdio>

SerialComm::SerialComm() : m_isOpen(false) {
    std::memset(m_portName, 0, sizeof(m_portName));

    m_serialPort.init(
        "",
        SerialConfig::BAUD_RATE,
        SerialConfig::PARITY_TYPE,
        SerialConfig::DATA_BITS,
        SerialConfig::STOP_BITS,
        SerialConfig::FLOW_CTRL,
        SerialConfig::READ_TIMEOUT_MS
    );
}

SerialComm::~SerialComm() {
    closePort();
}

bool SerialComm::findFirstTtyUSB() {
    std::vector<SerialPortInfo> portInfos = CSerialPortInfo::availablePortInfos();

    for (const auto& info : portInfos) {
        const char* portStr = info.portName;
        if (std::strstr(portStr, "ttyCH341USB") != nullptr) {
            std::strncpy(m_portName, portStr, sizeof(m_portName) - 1);
            m_portName[sizeof(m_portName) - 1] = '\0';
            printf("[I][SERIAL]: ttyUSB device found: %s\n", m_portName);
            return true;
        }
    }

    return false;
}

bool SerialComm::openPort() {
    if (std::strlen(m_portName) == 0) {
        printf("[E][Serial]: Port name is empty, call findFirstTtyUSB() first\n");
        return false;
    }

    m_serialPort.init(
        m_portName,
        SerialConfig::BAUD_RATE,
        SerialConfig::PARITY_TYPE,
        SerialConfig::DATA_BITS,
        SerialConfig::STOP_BITS,
        SerialConfig::FLOW_CTRL,
        SerialConfig::READ_TIMEOUT_MS
    );

    if (!m_serialPort.open()) {
        printf("[E][Serial]: Failed to open port %s\n", m_portName);
        printf("[E][Serial]: Error code: %d\n", m_serialPort.getLastError());
        m_isOpen = false;
        return false;
    }

    m_isOpen = true;
    printf("[I][Serial]: Port opened successfully: %s\n", m_portName);
    printf("[I][Serial]:   Baud rate: %d\n", SerialConfig::BAUD_RATE);
    printf("[I][Serial]:   Data bits: 8\n");
    printf("[I][Serial]:   Stop bits: 1\n");
    printf("[I][Serial]:   Parity: None\n");

    return true;
}

void SerialComm::closePort() {
    if (m_isOpen) {
        m_serialPort.close();
        m_isOpen = false;
        printf("[I][Serial]: Port closed: %s\n", m_portName);
    }
}

bool SerialComm::sendData(const char* data, size_t length) {
    return sendData(reinterpret_cast<const uint8_t*>(data), length);
}

bool SerialComm::sendData(const uint8_t* data, size_t length) {
    if (!m_isOpen) {
        printf("[E][Serial]: Port not open\n");
        return false;
    }

    int bytesWritten = m_serialPort.writeData(data, length);

    if (bytesWritten > 0) {
        printf("[I][Serial]: Sent %d bytes\n", bytesWritten);
        return true;
    } else {
        printf("[E][Serial]: Failed to send data\n");
        return false;
    }
}

int SerialComm::receiveData(char* buffer, size_t maxLength) {
    return receiveData(reinterpret_cast<uint8_t*>(buffer), maxLength);
}

int SerialComm::receiveData(uint8_t* buffer, size_t maxLength) {
    if (!m_isOpen) {
        printf("[E][Serial]: Port not open\n");
        return -1;
    }

    int bytesRead = m_serialPort.readData(buffer, maxLength);

    if (bytesRead > 0) {
        printf("[I][Serial]: Received %d bytes\n", bytesRead);
    }

    return bytesRead;
}

void SerialComm::listAllPorts() {
    std::vector<SerialPortInfo> portInfos = CSerialPortInfo::availablePortInfos();

    printf("[I][Serial]: Available ports:\n");
    for (const auto& info : portInfos) {
        printf("[I][Serial]:   - %s\n", info.portName);
    }
}