/*********************************************************************
// created:    2013/07/10
// filename:   PacpusSerialPort.cpp
//
// author:     Julien Moras
// 
// version:    $Id: $
//
// purpose:    This class defines a Qt 5.1 driver for serial port
 *********************************************************************/

#include <Pacpus/PacpusTools/PacpusSerialPort.h>

#include <Pacpus/kernel/ComponentFactory.h>
#include <Pacpus/kernel/Log.h>
#include <Pacpus/kernel/inputOutputInterface.h>

using namespace pacpus;

DECLARE_STATIC_LOGGER("pacpus.core.serialport");

static ComponentFactory<PacpusSerialPort> sFactory("PacpusSerialPort");

PacpusSerialPort::PacpusSerialPort(QString name):ComponentBase(name) {

    serialPort = new QSerialPort(this);
    // Default settings
#if(Win32)
    portName = "COM1";
#else
    portName = "/dev/tty0";
#endif
    direction = QSerialPort::AllDirections;
    baudRate  = QSerialPort::Baud9600;
    dataBits  = QSerialPort::Data8;
    parity    = QSerialPort::NoParity;
    stopBits  = QSerialPort::OneStop;
    flowControl = QSerialPort::NoFlowControl;
    errorPolicy = QSerialPort::SkipPolicy;

    dataMode = ASCII;
    sepChar = "\n";     // TODO remove

    startChar = "";
    stopChar = "\n";
    minPacketSize=0;

    log = false; // TODO remove when component ok

    // Or threaded
    connect(serialPort,SIGNAL(readyRead()),this,SLOT(readData()));

}

PacpusSerialPort::~PacpusSerialPort() {

    if(serialPort->isOpen())
        serialPort->close();
    delete serialPort;
}

void PacpusSerialPort:: addInput()
{
    ADD_INPUT("inStream",PacpusSerialPort,QByteArray,processInputFrame);
}

void PacpusSerialPort:: addOutput()
{
    ADD_OUTPUT("outStream",PacpusSerialPort,QByteArray);
    ADD_OUTPUT("pinOutSignal",PacpusSerialPort,quint16);
}


ComponentBase::COMPONENT_CONFIGURATION PacpusSerialPort::configureComponent(XmlComponentConfig config)
{

    /// TODO get parameter from xml

    if(param.hasProperty("portName"))
    portName = param.getProperty("portName");

    if(param.hasProperty("baudRate"))
    baudRate = (QSerialPort::BaudRate) param.getProperty("baudRate").toInt();

    if(param.hasProperty("direction"))
    direction = (QSerialPort::Direction) param.getProperty("direction").toInt();

    if(param.hasProperty("dataBits"))
    dataBits = (QSerialPort::DataBits) param.getProperty("dataBits").toInt();

    if(param.hasProperty("parity"))
    parity = (QSerialPort::Parity) param.getProperty("parity").toInt();

    if(param.hasProperty("stopBits"))
    stopBits = (QSerialPort::StopBits) param.getProperty("stopBits").toInt();

    if(param.hasProperty("flowControl"))
    flowControl = (QSerialPort::FlowControl) param.getProperty("flowControl").toInt();

    if(param.hasProperty("errorPolicy"))
    errorPolicy = (QSerialPort::DataErrorPolicy) param.getProperty("errorPolicy").toInt();

    if(param.hasProperty("dataMode"))
    dataMode = (DataMode) param.getProperty("dataMode").toInt();

    if(param.hasProperty("sepChar"))
    sepChar =  param.getProperty("sepChar");

    if(param.hasProperty("startChar"))
    startChar =  param.getProperty("startChar");

    if(param.hasProperty("stopChar"))
    stopChar =  param.getProperty("stopChar");

    if(param.hasProperty("minPacketSize"))
    minPacketSize = (DataMode) param.getProperty("minPacketSize").toInt();

    serialPort->setPortName(portName);

    if(!serialPort->setBaudRate(baudRate,direction))
        LOG_ERROR(getName() << "\t error n° "<< serialPort->error());
    if(!serialPort->setDataBits(dataBits))
        LOG_ERROR(getName() << "\t error n° "<< serialPort->error());
    if(!serialPort->setParity(parity))
        LOG_ERROR(getName() << "\t error n° "<< serialPort->error());
    if(!serialPort->setStopBits(stopBits))
        LOG_ERROR(getName() << "\t error n° "<< serialPort->error());
    if(!serialPort->setFlowControl(flowControl))
        LOG_ERROR(getName() << "\t error n° "<< serialPort->error());
    if(!serialPort->setDataErrorPolicy(errorPolicy))
        LOG_ERROR(getName() << "\t error n° "<< serialPort->error());

    // For futher developpements
//    serialPort->setBreakEnabled();
//    serialPort->setDataTerminalReady();
//    serialPort->setRequestToSend();
    return ComponentBase::CONFIGURED_OK;
}

void PacpusSerialPort::startActivity()
{

    buffer.clear();

    if(!serialPort->open((QSerialPort::OpenModeFlag)direction))
    { LOG_FATAL("device "<< serialPort->portName().toStdString() <<" failed to open: " << serialPort->errorString()); }
    else
       { LOG_INFO("listening for data on " << serialPort->portName()); }

    moveToThread(&thread_);
    thread_.start();

    //TODO Remove QThread inheritance if this works fine
    //THREAD_ALIVE = true;
    //start();
    setState(ComponentBase::MONITOR_OK);
}

void PacpusSerialPort::stopActivity()
{     
    QMetaObject::invokeMethod(&thread_, "quit");

    //THREAD_ALIVE = false;
    //while(!isFinished()); // TODO timeout

    serialPort->close();


}

void PacpusSerialPort::processInputFrame(const QByteArray & inputFrame)
{
    LOG_TRACE("Input frame " << inputFrame.data());

    // Write input to serial port
    if(serialPort->write(inputFrame)>=0)
    {
        setState(MONITOR_OK);
        LOG_DEBUG(getName() << " Serial write performed")
    }
    else
     {
        setState(MONITOR_NOK);
        LOG_WARN(getName() << " Serial write error")
    }
}

void PacpusSerialPort::readData()
{
    int n;

    if(dataMode == BINARY & minPacketSize >0)
        if(serialPort->bytesAvailable() < minPacketSize)
            return;

    buffer += serialPort->readAll();

    while(true) // loop until no data to send
        {
        switch(dataMode)
        {
            case ASCII : // Split the first line to send
            {
                if(startChar.size()>0)
                {
                    int indexStart = buffer.indexOf(startChar,0);
                    if(indexStart < 0)
                        return;
                    else
                        buffer.remove(0,indexStart);
                }

                int indexStop = buffer.indexOf(stopChar,1);
                if(indexStop < 1)
                    return;

                n=indexStop;
                break;

            // This work but replace by previous code that need to be test
//                n = buffer.indexOf(sepChar,1) + 1;
//                if(n<=0)
//                    return; // end line char not found
//                break;
            }
            case BINARY : // Get all data
                n = buffer.size();//serialPort->bytesAvailable();
                if(n<=0)
                    return;
                break;

            default:

                LOG_WARN("unknown dataMode")
                setState(MONITOR_NOK);
                return;

        }

        QByteArray data(buffer.data(),n);
        buffer.remove(0,n);

        quint16 pinout = (quint16)serialPort->pinoutSignals();

        GET_OUTPUT("outStream",PacpusSerialPort,QByteArray)->send(data);
        GET_OUTPUT("pinOutSignal",PacpusSerialPort,quint16)->send(pinout);
        setState(MONITOR_OK);
        LOG_DEBUG("Signal Read " << n << " bytes, PinOut : " << pinout);
        LOG_DEBUG(data.data());

        if(log)
        {
            switch(dataMode)
            {
                case ASCII :
                    LOG_INFO("Buffer " << buffer.data());

                case BINARY :
                {
                        QByteArray str;
                        for(int i =0;i<22 ;i++)
                            str+= QByteArray::number((uchar)buffer.data()[i],16).toUpper() + " ";
                        LOG_INFO("Value " << str.data());
                }
            }
        }
    }
}

/*void PacpusSerialPort::run()
{
    while (THREAD_ALIVE)
    {
        if (serialPort->waitForReadyRead(timeOut))
        {
            QByteArray data = serialPort->readAll();
            GET_OUTPUT("outStream",PacpusSerialPort,QByteArray)->send(data);
            GET_OUTPUT("pinOutSignal",PacpusSerialPort,quint16)->send((quint16)serialPort->pinoutSignals());
            setState(MONITOR_OK);
            LOG_INFO("Run Read");
            LOG_INFO(data.data());
        } else {
            setState(MONITOR_NOK);
        }
    }
}*/
