/** @file devicecom.cpp
 * @author Tomas Vit
 * @date 10.5.2015
*/

#include "devicecom.h"
#include "mainwindow.h"

/**
 * @brief Creates an instance of the DeviceCom.
*/

DeviceCom::DeviceCom()
{
    connected = false;
}

/**
 * @brief Destroys the DeviceCom object.
*/

DeviceCom::~DeviceCom()
{
    disconnect();
}

/**
 * @brief Returns the connection status of devices.
 * @return True if connected
*/

bool DeviceCom::isConnected() {
    if ( connected ) {
        return true;
    }
    return false;
}

/**
 * @brief Connects to the Laboratory power supply
 * @retval  1   Device not found
 * @retval  2   Devices could not be opened
 * @retval  3   Set commucation Failed
 * @retval  0   Successful connections
*/

int DeviceCom::connect() {

    FT_STATUS ftStatus;
    DWORD numDevs;
    const char* s = DEVICE_SERIAL;

    ftStatus = FT_ListDevices(&numDevs,NULL,FT_LIST_NUMBER_ONLY);
    if (ftStatus != FT_OK || numDevs == 0) {
        return 1;
    }

    ftStatus = FT_OpenEx(const_cast<char*>(s),FT_OPEN_BY_SERIAL_NUMBER,&DeviceHandle);
    if (ftStatus != FT_OK) {
        return 2;
    }

    ftStatus |= FT_ResetDevice(DeviceHandle);
    ftStatus |= FT_ResetPort(DeviceHandle);
    ftStatus |= FT_Purge(DeviceHandle, FT_PURGE_RX | FT_PURGE_TX);

    ftStatus |= FT_ResetDevice(DeviceHandle);
    ftStatus |= FT_SetBaudRate(DeviceHandle, 19200);
    // Set 8 data bits, 1 stop bit and no parity
    ftStatus |= FT_SetDataCharacteristics(DeviceHandle, FT_BITS_8, FT_STOP_BITS_1,FT_PARITY_EVEN);
    ftStatus |= FT_SetTimeouts(DeviceHandle, 1000, 2000);
    ftStatus |= FT_SetLatencyTimer(DeviceHandle, 2);
    ftStatus |= FT_SetChars(DeviceHandle, false, 0, false, 0);
    clean();


    if (ftStatus != FT_OK) {
        disconnect();
        return 3;
    }

    connected = true;
    return 0;
}

/**
 * @brief Disconnects the device
*/

int DeviceCom::disconnect() {
    FT_Close(DeviceHandle);
    connected = false;
    return 0;
}

/**
 * @brief Writes command or string to the device.
 * @param outputBuffer  Enrollee string
 * @param size  Strings length enrollee.
 * @return Zero if it failed writing otherwise it returns the number of characters entered.
*/

int DeviceCom::write(BYTE *outputBuffer, int size) {
    FT_STATUS ftStatus;
    DWORD numBytesSent;

    ftStatus = FT_Write(DeviceHandle, outputBuffer, size, &numBytesSent);
    if (ftStatus != FT_OK) {
        return 0;
    }

    return numBytesSent;
}

/**
 * @brief Reads command or string from the device.
 * @param inputBuffer   String enrollment read messages.
 * @return  Zero if it failed reading otherwise it returns the number of characters readed.
*/

int DeviceCom::read(BYTE *inputBuffer) {
    FT_STATUS ftStatus;
    DWORD numBytesToRead, numBytesRead;

    Sleep(20);
    do
    {
        ftStatus = FT_GetQueueStatus(DeviceHandle, &numBytesToRead);

    } while ((numBytesToRead == 0) && (ftStatus == FT_OK));

    ftStatus = FT_Read(DeviceHandle, inputBuffer, numBytesToRead, &numBytesRead);
    if (ftStatus != FT_OK) {
        return 0;
    }

    return numBytesRead;
}

/**
 * @brief Check device status.
 * @retval  1   The device is not active.
 * @retval  0   The device is okay.
*/

int DeviceCom::checkDevice() {
    FT_STATUS ftStatus;
    char SerialNumber[16];

    ftStatus = FT_GetDeviceInfo(DeviceHandle,NULL,NULL, SerialNumber, NULL,NULL);

    if (ftStatus != FT_OK) {
        return 1;
    }

    return 0;
}

/**
 * @brief Sends a request to the new values and reads the answer.
 * @param newValues String enrollment new values.
 * @retval  0   Communication was okay
 * @retval  1   UART Write failed
 * @retval  2   UART Read failed
 * @retval  3   Read string is bad
*/

int DeviceCom::getNewValues(BYTE *newValues) {

    DWORD numBytesRead;
    BYTE outputBuffer[3], inputBuffer[6];

    //SEND a1U
    outputBuffer[0] = START_FRAME;
    outputBuffer[1] = 1;
    outputBuffer[2] = END_FRAME;

    QMutexLocker locker(&mutex);

    if ( write(outputBuffer, 3) == 0 ) {
        return 1;
    }

    numBytesRead = read(inputBuffer);

    if ( numBytesRead == 0 ) {
        return 2;
    }

    if ( inputBuffer[0] != START_FRAME || inputBuffer[numBytesRead - 1] != END_FRAME || inputBuffer[1] == 0x81 || numBytesRead != 6) {
        //qDebug() << "velikost: " << numBytesRead;
        //qDebug() << inputBuffer[0];
        //qDebug() << inputBuffer[1];
        //qDebug() << inputBuffer[2];
        //qDebug() << inputBuffer[3];
        //qDebug() << inputBuffer[4];
        //qDebug() << inputBuffer[5];

        return 3;
    }

    newValues[0] = inputBuffer[1];  //voltage
    newValues[1] = inputBuffer[2];  //current
    newValues[2] = inputBuffer[3];  //controlMode
    newValues[3] = inputBuffer[4];  //enableOutput

    return 0;
}

/**
 * @brief DeviceCom::setValue
 * @param address
 * @param value
 * @return
 */

int DeviceCom::setValue(unsigned char address, BYTE value) {
    BYTE outputBuffer[5], inputBuffer[5];
    DWORD numBytesRead;

    //SEND a2'value'U
    outputBuffer[0] = START_FRAME;
    outputBuffer[1] = 2;
    outputBuffer[2] = address;
    outputBuffer[3] = value;
    outputBuffer[4] = END_FRAME;


    QMutexLocker locker(&mutex);

    if ( write(outputBuffer, 5) == 0 ) {
        return 1;
    }

    numBytesRead = read(inputBuffer);

    if ( numBytesRead == 0 ) {
        return 2;
    }

    if ( inputBuffer[0] != 'a' || inputBuffer[numBytesRead - 1] != 'U' || inputBuffer[1] == 0x82) {
        return 3;
    }
    return 0;
}

int DeviceCom::clean() {
    FT_STATUS ftStatus;

    ftStatus = FT_Purge(DeviceHandle, FT_PURGE_RX | FT_PURGE_TX);

    if (ftStatus == FT_OK) {
        return 0;
    }
    else {
        return 1;
    }

}

int DeviceCom::setOutput(bool set_output) {
    BYTE outputBuffer[4], inputBuffer[5];
    DWORD numBytesRead;

    //SEND a2'value'U
    outputBuffer[0] = START_FRAME;
    outputBuffer[1] = 3;
    if ( set_output ) {
        outputBuffer[2] = 1;
    } else {
        outputBuffer[2] = 0;
    }
    outputBuffer[3] = END_FRAME;


    QMutexLocker locker(&mutex);

    if ( write(outputBuffer, 4) == 0 ) {
        return 1;
    }

    numBytesRead = read(inputBuffer);

    if ( numBytesRead == 0 ) {
        return 2;
    }

    if ( inputBuffer[0] != 'a' || inputBuffer[numBytesRead - 1] != 'U' || inputBuffer[1] == 0x83) {
        return 3;
    }
    return 0;
}

