#include "DI710Driver.h"
#include <QDebug>
#include <QElapsedTimer>


#include <iostream>
#include <cstring>      // Needed for memset
#include <sys/socket.h> // Needed for the socket functions
#include <netdb.h>      // Needed for the socket functions
#include <unistd.h>



CDI710Driver::CDI710Driver()
{
    mRunThread = true;
    mRunning = false;
    mPendingOperation = DI710_NO_OPERATION;
}

unsigned int CDI710Driver::OpenDevice(QString IPAdress, int Port)
{

//    int status;
//    struct addrinfo host_info;       // The struct that getaddrinfo() fills up with data.
//    struct addrinfo *host_info_list; // Pointer to the to the linked list of host_info's.

//    // The MAN page of getaddrinfo() states "All  the other fields in the structure pointed
//    // to by hints must contain either 0 or a null pointer, as appropriate." When a struct
//    // is created in c++, it will be given a block of memory. This memory is not nessesary
//    // empty. Therefor we use the memset function to make sure all fields are NULL.
//    memset(&host_info, 0, sizeof host_info);

//    std::cout << "Setting up the structs..."  << std::endl;

//    host_info.ai_family = AF_UNSPEC;     // IP version not specified. Can be both.
//    host_info.ai_socktype = SOCK_STREAM; // Use SOCK_STREAM for TCP or SOCK_DGRAM for UDP.

//    // Now fill up the linked list of host_info structs with google's address information.
//    status = getaddrinfo("www.google.com", "80", &host_info, &host_info_list);
//    // getaddrinfo returns 0 on succes, or some other value when an error occured.
//    // (translated into human readable text by the gai_gai_strerror function).
//    if (status != 0)  std::cout << "getaddrinfo error" << gai_strerror(status) ;


//    std::cout << "Creating a socket..."  << std::endl;
//    int socketfd ; // The socket descripter
//    socketfd = socket(host_info_list->ai_family, host_info_list->ai_socktype,
//                      host_info_list->ai_protocol);
//    if (socketfd == -1)  std::cout << "socket error " ;


//    std::cout << "Connect()ing..."  << std::endl;
//    status = connect(socketfd, host_info_list->ai_addr, host_info_list->ai_addrlen);
//    if (status == -1)  std::cout << "connect error" ;


//    std::cout << "send()ing message..."  << std::endl;
//    char *msg = "GET / HTTP/1.1\nhost: www.google.com\n\n";
//    int len;
//    ssize_t bytes_sent;
//    len = strlen(msg);
//    bytes_sent = send(socketfd, msg, len, 0);

//    std::cout << "Waiting to recieve data..."  << std::endl;
//    ssize_t bytes_recieved;
//    char incomming_data_buffer[1000];
//    bytes_recieved = recv(socketfd, incomming_data_buffer,1000, 0);
//    // If no data arrives, the program will just wait here until some data arrives.
//    if (bytes_recieved == 0) std::cout << "host shut down." << std::endl ;
//    if (bytes_recieved == -1)std::cout << "recieve error!" << std::endl ;
//    std::cout << bytes_recieved << " bytes recieved :" << std::endl ;
//    incomming_data_buffer[bytes_recieved] = '\0' ;
//    std::cout << incomming_data_buffer << std::endl;
//    std::cout << "Receiving complete. Closing socket..." << std::endl;
//    freeaddrinfo(host_info_list);
//    close(socketfd);









    mDataQIPAddress.setAddress(IPAdress);
    mDataQPort = Port;
    mTCPSocket.setSocketOption(QAbstractSocket::LowDelayOption,1);

    mTCPSocket.connectToHost(IPAdress,Port);

    if(mTCPSocket.waitForConnected(5000) == true)
    {
        qDebug("DI710 device Connected");
        return SetupDevice();
        return DI710_RET_OK;
    }

    return DI710_RET_ERR_MODULE_OFFLINE;
}

void CDI710Driver::Run()
{
    bool Run = true;
    int Operation;
    mRunning = true;
    QElapsedTimer timer;
    timer.start();

    qDebug("DataQ thread started");

    while(Run)
    {

        mTCPSocket.waitForReadyRead(200);
        if(mTCPSocket.bytesAvailable() >= 2)
        {
//            QByteArray data = mTCPSocket.read(2);
//            qint16 temp;
//            QDataStream stream(&data,QIODevice::ReadOnly);
//            stream.device()->seek(0);
//            stream >> temp;




            char data[2];
            quint16 temp = 0;
            mTCPSocket.read(data,2);
           // data[3] = 0;

            temp = data[1];
            temp <<= 8;
            temp += data[0];

            mMutex.lockForWrite();
            mCurData = (int)temp;
            mMutex.unlock();
            qDebug() << QByteArray(data,2).toHex() << " : " << temp;
            //qDebug() << timer.elapsed();
  //          qDebug() << mTCPSocket.bytesAvailable();
            timer.start();

        }

        mMutex.lockForRead();
        //Run = mRunThread;
        Operation = mPendingOperation;
        mPendingOperation = DI710_NO_OPERATION;
        mMutex.unlock();


        switch(Operation)
        {
            case DI710_NO_OPERATION:
            {
                break;
            }
            case DI710_STOP_ACQUISITION_OPERATION:
            {
                SendStopAcquisitionFrame();
//                qDebug("DataQ stop acquisition operation executed");
                break;
            }
            case DI710_START_ACQUISITION_OPERATION:
            {
                SendStartAcquisitionFrame();
//                qDebug("Dataq start acquisition operation executed");
                break;
            }
            case DI710_STOP_THREAD_OPERATION:
            {
                SendStopAcquisitionFrame();
                Run = false;
//                qDebug("Dataq stop thread operation executed");
                break;
            }
            case DI710_INVALID_OPERATION:
            {
                qDebug("Dataq invalid operation...");
                break;
            }
        }
    }

    mMutex.lockForWrite();
    mRunning = false;
    mMutex.unlock();
//    qDebug("DataQ thread stopped");
}

int CDI710Driver::GetAnalogData(int channel)
{
    Q_UNUSED(channel)

    int data = 0XDEADBEEF;
    mMutex.lockForRead();
    data = mCurData;
    mMutex.unlock();

    return data;

}

unsigned int CDI710Driver::SetupDevice()
{

    mTCPSocket.readAll();

    QByteArray data;


    if(SendStopAcquisitionFrame() != DI710_RET_OK)
        return DI710_RET_CANNOT_CONFIGURE;



    data.clear();
    data.append((char)0);
    data.append("X00");
    mTCPSocket.write(data);
    if(mTCPSocket.waitForReadyRead(1000) == true)
    {
        qDebug() << mTCPSocket.readAll();
    }
    else
    {
        return DI710_RET_CANNOT_CONFIGURE;
    }

    data.clear();
    data.append((char)0);
    data.append("M000E");
    mTCPSocket.write(data);
    if(mTCPSocket.waitForReadyRead(1000) == true)
    {
        qDebug() << mTCPSocket.readAll();
    }
    else
    {
       return DI710_RET_CANNOT_CONFIGURE;
    }

    data.clear();
    data.append((char)0);
    data.append("L00F001");
    mTCPSocket.write(data);
    if(mTCPSocket.waitForReadyRead(1000) == true)
    {
        qDebug() << mTCPSocket.readAll();
    }
    else
    {
        return DI710_RET_CANNOT_CONFIGURE;
    }

    data.clear();
    data.append((char)0);
    data.append("C01");
    mTCPSocket.write(data);
    if(mTCPSocket.waitForReadyRead(1000) == true)
    {
        qDebug() << mTCPSocket.readAll();
    }
    else
    {
        return DI710_RET_CANNOT_CONFIGURE;
    }


    //Check if we can start...
    if(SendStartAcquisitionFrame() != DI710_RET_OK)
        return DI710_RET_CANNOT_CONFIGURE;

    //stop for the moment...
    if(SendStopAcquisitionFrame() != DI710_RET_OK)
        return DI710_RET_CANNOT_CONFIGURE;

//     mTCPSocket.setReadBufferSize(2);


    return DI710_RET_OK;
}

int CDI710Driver::SendStopAcquisitionFrame()
{
    bool done = false;
    QByteArray data;
    data.clear();
    data.append((char)0);
    data.append("T0");
    mTCPSocket.setReadBufferSize(0);
    mTCPSocket.readAll();
    mTCPSocket.write(data);

    QByteArray rxdata;
    rxdata.clear();
    while(done == false)
    {

        if(mTCPSocket.waitForReadyRead(1000) == true)
        {
            rxdata.append(mTCPSocket.readAll());

            if(rxdata.contains("T0"))
            {
                done = true;
            }

//            qDebug() << rxdata;
        }
        else
        {
            return DI710_RET_CANNOT_CONFIGURE;
        }
    }



    return DI710_RET_OK;
}

int CDI710Driver::SendStartAcquisitionFrame()
{
    bool done = false;
    QByteArray data;
    data.clear();
    data.append((char)0);
    data.append("S3");
    mTCPSocket.setReadBufferSize(0);
    mTCPSocket.write(data);

    QByteArray rxdata;
    rxdata.clear();
    while(done == false)
    {
        if(mTCPSocket.waitForReadyRead(1000) == true)
        {

            rxdata.append(mTCPSocket.read(1));
            if(rxdata.contains("S3"))
            {
                done = true;
//                qDebug() << rxdata;
            }

        }
        else
        {
            qDebug() << rxdata;
            return DI710_RET_CANNOT_CONFIGURE;
        }
    }

//     mTCPSocket.setReadBufferSize(2);

    return DI710_RET_OK;
}

void CDI710Driver::StopThread()
{
//    mMutex.lockForWrite();
//    mRunThread = false;
//    mMutex.unlock();
    mMutex.lockForWrite();
    mPendingOperation = DI710_STOP_THREAD_OPERATION;
    mMutex.unlock();
}

bool CDI710Driver::IsThreadRunning()
{
    bool ret;
    mMutex.lockForRead();
    ret = mRunning;
    mMutex.unlock();

    return ret;
}

unsigned int CDI710Driver::StartAcquisition()
{
    mMutex.lockForWrite();
    mPendingOperation = DI710_START_ACQUISITION_OPERATION;
    mMutex.unlock();
    return DI710_RET_OK;
}

unsigned int CDI710Driver::StopAcquisition()
{
    mMutex.lockForWrite();
    mPendingOperation = DI710_STOP_ACQUISITION_OPERATION;
    mMutex.unlock();
    return DI710_RET_OK;
}

unsigned int CDI710Driver::GetAnalogInput(int Channel, int *Data)
{
	Q_UNUSED(Channel)
    *Data = GetAnalogData(0);
    return DI710_RET_OK;
}
