#include "com.h"
#include <QDebug>
//#include "controlBus.h"

//////////////////////////////////////////////////////////////////////////////////
///
///      serial hardware layer
///
//////////////////////////////////////////////////////////////////////////////////

static int64_t com_want2read;
// -------------------------------------------------------------------------------------------------------------
// --------- PUBLIC --------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------------------

void T_com::writeToSerial(const QByteArray &data, uint16_t sendLength)
{
    sendBuffer=data;
    sendLen=sendLength;
    if (CatSerial->isOpen())
    {
        //qDebug() << "sending..." << sendBuffer;
        CatSerial->write(sendBuffer);
    } else
        qDebug() << "error sending, port is not open";

}


bool T_com::readFromSerial(QByteArray &data, uint16_t &sendLength)
{
    // return one time true if new data (completly) read.
    // return new data in &data and &sendLength  to other objects
    uint16_t ll=rawInLen;
    if (!CatSerial->isOpen())
        return false;
    data.clear();
    data.append(rawInput);
    sendLength=ll;
    rawInLen=0;         // beim 2. Aufruf 0 zurück weil nichts neues da
    if (ll>0)
        return true;
    return false;
}


// -------------------------------------------------------------------------------------------------------------
// ---------   PRIVATES  --------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------------------


T_com::T_com(QWidget *parent) : QMainWindow(parent)
{
    // port settings come from tabCom->Sdata->serial

    gpi_serialChanged();
    CatSerial = new QSerialPort();  // PortHW object for Control&Analyse Tool
    //CatSerial->clear();
    //CatSerial->clearError();

    connect(CatSerial, &QSerialPort::readyRead, this, &T_com::readSomeBytes);
        // still reading, not sure if complete, undefined number of calls while reading

    connect(CatSerial, &QSerialPort::bytesWritten, this, &T_com::serialSendComplete);
        // system confirms sending complete

    //connect(CatSerial, &QSerialPort::dataTerminalReadyChanged, this, &T_com::incomingWake);
    //connect(CatSerial, &QSerialPort::requestToSendChanged, this, &T_com::incomingWake);

    // timer detects time gap in input flow
    serRecTime = new QTimer();
    connect(serRecTime, SIGNAL(timeout()), this, SLOT(receiveTO()));
    serRecTime->setSingleShot(true);        // single shot! only one impulse if receive complete
    serRecTime->stop();                     // on hold

    // check COM-TAB periodic if user wants to connect or disconnect
    QTimer *ChkConnectTimer = new QTimer();
    connect(ChkConnectTimer, SIGNAL(timeout()), this, SLOT(ser_ISR100ms()));
    ChkConnectTimer->setSingleShot(false);
    ChkConnectTimer->start(100);     // in ms
    com_want2read=0;
}




T_com::~T_com()
{
    if (CatSerial->isOpen())
        CatSerial->close();
}


void T_com::ser_ISR100ms()
{
    //qDebug() << "~~>LIB" << "ENTER...";
    // call every 100ms to check if user(HMI) wants to connect or disconnect
    uint8_t chkConn = gpi_getSerialConn();    // from global GUI buffer (Sdata)

    //qDebug() << "~~>LIB" << "checking connect button... " << chkConn;

    switch (chkConn)
    {
        case 0: // 0 button "connect" was just released
                //qDebug() << "close serial port" << chkConn;
                closeSerialPort();
                gpi_serialChanged();    // set chkConn to 2, thus getting edge
                break;
        case 1: // 1 button "connect" was just pressed
                //qDebug() << "open serial port" << chkConn;
                open_Serial_Port();
                gpi_serialChanged();    // set chkConn to 2, thus getting edge
                break;
    }


    if (CatSerial->isOpen()) {
        gpi_serialIsOpen(true);
    } else {
        gpi_serialIsOpen(false);
    }

    //qDebug() << "LEAVE " << chkConn;
}

// -------------------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------------------

char T_com::open_Serial_Port()
{
    //qDebug() << "ENTER";

    bool ret;
    QString myString=nullptr, myPortName=nullptr, myBaudStr=nullptr;
    int myBaudNr;

    if (CatSerial->isOpen()) {
        qDebug() << "!!!IS OPEN!!!";
        return 0;                   // opening twice is not allowed
    }

    //qDebug() << "connecting..." << myPortName;
    myPortName=gpi_getComPortName();                // was selected and stored from GUI
    CatSerial->setPortName(myPortName);
    myBaudNr=gpi_getBaudNr();                       // was selected and stored from GUI

    //qDebug() << "myPortName" << myPortName << ", myBaudNr" << myBaudNr;

    switch (myBaudNr)
    {
        // 0:1200   1:9600   2:19200   3:38400   4:57600   5:115200
        case 0: CatSerial->setBaudRate(QSerialPort::Baud1200); myBaudStr="1200"; break;
        case 1: CatSerial->setBaudRate(QSerialPort::Baud9600); myBaudStr="9600"; break;
        case 2: CatSerial->setBaudRate(QSerialPort::Baud19200); myBaudStr="19200"; break;
        case 3: CatSerial->setBaudRate(QSerialPort::Baud38400); myBaudStr="38400"; break;
        case 4: CatSerial->setBaudRate(QSerialPort::Baud57600); myBaudStr="57600"; break;
        case 5: CatSerial->setBaudRate(QSerialPort::Baud115200); myBaudStr="115200"; break;
    }

    CatSerial->setDataBits(QSerialPort::Data8);
        // alt: QSerialPort::Data5,6,7,8

    CatSerial->setParity(QSerialPort::NoParity);
        // alt: EvenParity, OddParity, NoParity

    CatSerial->setStopBits(QSerialPort::OneStop);
        // alternative: OneStop, TwoStop, OneAndHalfStop

    CatSerial->setFlowControl(QSerialPort::NoFlowControl);
        // alt: HardwareControl, SoftwareControl, NoFlowControl

    ret=CatSerial->open(QIODevice::ReadWrite);
        // alt: QIODevice::ReadWrite  QIODevice::ReadOnly  QIODevice::WriteOnly
    if (!ret)
    {
        myString.clear();
        myString = "error ";
        myString.append(CatSerial->errorString());
        qDebug() << myString;
        gpi_setTxt4comStateLine(myString);

        //qDebug() << "LEAVE";

        return 0;
    } else
    {
        myString.clear();
        myString.append(myPortName);
        //lang=myString.size();
        myString.append(" opened with ");
        myString.append(myBaudStr);
        myString.append(" 8N1");
        qDebug() << myString;
        gpi_setTxt4comStateLine(myString);
        gpi_setTxt4RsDiagWin(myString+"\n");

    }

    //qDebug() << "LEAVE";
    return 0;
}

void T_com::closeSerialPort()
{
    //qDebug() << "ENTER";

    if (CatSerial->isOpen())
    {
        qDebug() << "closing connection";
        CatSerial->close();
        gpi_setTxt4comStateLine("closed");
        gpi_setTxt4RsDiagWin("closed");

    }

    //qDebug() << "LEAVE";
}


void T_com::readSomeBytes(void)
{
    // called by serial-read-detection
    // restart off-time as input flow is ongoing

    // timer for slow receive
    // and serves as timeout for fast receive is msg is shorter as expected
        serRecTime->stop();
        serRecTime->start(20);     // in ms

        //qDebug()<< "com-rec read some bytes";

    this->receiveByLength();    // since 14.12.21: fast receive
}

void T_com::receiveFixLen(int64_t nrOfbytesToReceive)
{
    // call this before sending a request to slave
    // then we know exactly when reception is complete -> much faster
    com_want2read=nrOfbytesToReceive;
        // since 14.12.21: FastDevice Protocol has two lengthen:
        // fast: 12byte reception  long: 68byte
}

void T_com::receiveByLength(void)
{

    if (CatSerial->isOpen())
    {
        QString myString=nullptr, tmpStr=nullptr;
        int64_t nrOfBytesreceived = CatSerial->bytesAvailable();        // nr of received bytes

        //qDebug()<< "com-rec current Len: "<< nrOfBytesreceived;

        if (nrOfBytesreceived >= com_want2read)
        {
            QByteArray data = CatSerial->readAll();     // erst auslesen wenn alles da! löscht den Empfangspuffer
            serRecTime->stop();     // stop timeout to avoid 2nd emit
            rawInLen=uint16_t (nrOfBytesreceived);
            rawInput.clear();
            rawInput.append(data);
            // report "new data received" to other objects
            //qDebug()<< "com-recFinished by Len "<< rawInLen;
            emit receivingFinished();
        }
    }
}

void T_com::receiveTO(void)
{
    // no new input data for 20ms, --> assuming frame complete
    // save data in private "rawInput"-buffer

    if (CatSerial->isOpen())
    {

        QString myString=nullptr, tmpStr=nullptr;
        int64_t nrOfBytesreceived = CatSerial->bytesAvailable();        // nr of received bytes
        QByteArray data = CatSerial->readAll();

        rawInLen=uint16_t (nrOfBytesreceived);
        rawInput.clear();
        rawInput.append(data);
        //rawInput[rawInLen]=0;       // Zwangsterminierung bei QByteArray nicht nötig

        // diag display in serial in/out window and debug window
        myString.clear();
        myString.setNum(rawInLen);
        myString.append(" in: ");
        //myString.append(rawInput);
        for (int ii=0; ii<rawInLen; ii++)
        {
            tmpStr.clear();
            tmpStr.setNum(rawInput[ii],16);   // problem: wenn >0x80 dann wird EIN Byte 16 stellig angezeigt
            int ll=tmpStr.length();
            if (ll>2)
            {
                myString.append(tmpStr[ll-2]);
                myString.append(tmpStr[ll-1]);
            } else
            {
                myString.append(tmpStr);
            }
                myString.append(" ");
        }
        myString.append("\n");
    #ifdef PRINTALLDEBUGS
        qDebug() << "VCP:" << myString;   // display all inputs and outputs in output window
    #endif
        gpi_setTxt4RsDiagWin(myString);
        //gpi_set2ndTxt4RsDiagWin(myString);

        // report "new data received" to other objects
        //qDebug()<< "com-recFinished by TO";
        emit receivingFinished();
    }
}

void T_com::serialSendComplete(void)
{
    // system confirms sending complete, diag display
    QString myString=nullptr, tmpStr=nullptr;

    myString.clear();
    myString.setNum(sendLen);
    myString.append(" out: ");

    for (int ii=0; ii<sendLen; ii++)
    {
        tmpStr.clear();
        tmpStr.setNum(sendBuffer[ii],16);   // problem: wenn >0x80 dann 16stellig
        int ll=tmpStr.length();
        if (ll>2)
        {
            //qDebug() << "long_string" << ll << "\n";
            myString.append(tmpStr[ll-2]);
            myString.append(tmpStr[ll-1]);

        } else
        {
            myString.append(tmpStr);
        }
            myString.append(" ");
    }

#ifdef PRINTALLDEBUGS
    myString.append("\n");
    qDebug() << myString; // display all output data in out-window
#endif

    gpi_setTxt4RsDiagWin(myString);

    emit sendingFinished();     // for whom it may interest
}


bool T_com::isPortOpen(void)
{
    if (CatSerial->isOpen())
        return true;
    return false;
}



// -------------------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------------------

/*
uint8_t T_com::getAllPortPins(void)
{
    uint8_t rs232pins=0;
    rs232pins= uint8_t(CatSerial->pinoutSignals());
        // rs232pins: all signals bitwise coded in one byte:
        // readback output:  bit 0=TxD(=output)      bit2=DTR (=output)    bit 6=RTS (=output)
        // unused inputs:       bit1=RxD    bit 3=DCD    bit 5 = RING
        // handshake inputs:           bit 4=DSR (0x10)     bit 7=CTS (0x80)
    //qDebug()<<"serial port pins: " << rs232pins;
    return rs232pins;
}

bool T_com::getHSin_CTS(void)
{
    // return the used Handshake IN (CTS, alt. DSR): true= high level (+8V)

    uint8_t rs232pins=0;

    rs232pins= uint8_t(CatSerial->pinoutSignals());
        // rs232pins: all signals bitwise coded in one byte:
        // readback output:  bit 0=TxD(=output)      bit2=DTR (=output)    bit 6=RTS (=output)
        // unused inputs:       bit1=RxD    bit 3=DCD    bit 5 = RING
        // handshake inputs:           bit 4=DSR (0x10)     bit 7=CTS (0x80)

    if (rs232pins & 0x80)   // CTS
        return true;

    return false;
}


bool T_com::getHSin_DSR(void)
{
    uint8_t rs232pins=0;
    rs232pins= uint8_t(CatSerial->pinoutSignals());
    if (rs232pins & 0x10)   // DSR
        return true;
    return false;
}

void T_com::incomingWake(void)       //(bool LevelOfTheBit)
{
    emit wasWokenBySerialHandshake();
}

bool T_com::setHSout_RTS(bool hsout)
{
    // hsout true=positiv voltage +12V  false= -12V
    // retval: true=setting OK

    bool cc;
    // 10.5.19, am Windows-PC nachgemessen, funktioniert gut
    // false ergibt -12V    true ergibt +12V

    cc=CatSerial->setRequestToSend(hsout);  // RTS out
            // retval true means "setting was successful"

    // alternative: use DTR as Handshake:
    //cc=CatSerial->setDataTerminalReady(false);     // DTR out
        // retval true means "setting was successful"
    //qDebug()<<"RTS " <<cc;
    return cc;
}

bool T_com::setHSout_DTR(bool hsout)
{
    // hsout true=positiv voltage +12V  false= -12V
    // retval: true=setting OK

    bool cc;
    // 10.5.19, am Windows-PC nachgemessen, funktioniert gut
    // false ergibt -12V    true ergibt +12V

    cc=CatSerial->setDataTerminalReady(hsout);     // DTR out
        // retval true means "setting was successful"
    //qDebug()<<"DTR " <<cc;
    return cc;
}
*/
