
#if defined(USB_CONTROLLER)
#include "usbcontroller.h"

#ifdef Q_OS_WIN
#pragma comment (lib, "Setupapi.lib")
#endif

#include <QDebug>

#include "logcategories.h"

usbController::usbController()
{
    // As this is run in it's own thread, don't do anything in the constructor
	qInfo(logUsbControl()) << "Starting usbController()";
}

usbController::~usbController()
{
    qInfo(logUsbControl) << "Ending usbController()";

    if (handle) {
        programOverlay(60, "Goodbye from wfview");

        if (usbDevice == RC28) {
            ledControl(false, 3);
        }

        hid_close(handle);
    }

    hid_exit();
#if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
    if (gamepad != Q_NULLPTR)
    {
        delete gamepad;
        gamepad = Q_NULLPTR;
    }
#endif
}

void usbController::init(int sens, QMutex* mut)
{
    this->mutex = mut;
    this->sensitivity = sens;
    emit sendSensitivity(sensitivity);
#ifdef HID_API_VERSION_MAJOR
    if (HID_API_VERSION == HID_API_MAKE_VERSION(hid_version()->major, hid_version()->minor, hid_version()->patch)) {
        qInfo(logUsbControl) << QString("Compile-time version matches runtime version of hidapi: %0.%1.%2")
            .arg(hid_version()->major)
            .arg(hid_version()->minor)
            .arg(hid_version()->patch);
    }
    else {
        qInfo(logUsbControl) << QString("Compile-time and runtime versions of hidapi do not match (%0.%1.%2 vs %0.%1.%2)")
            .arg(HID_API_VERSION_MAJOR)
            .arg(HID_API_VERSION_MINOR)
            .arg(HID_API_VERSION_PATCH)
            .arg(hid_version()->major)
            .arg(hid_version()->minor)
            .arg(hid_version()->patch);
    }
#endif
    hidStatus = hid_init();
    if (hidStatus) {
        qInfo(logUsbControl()) << "Failed to intialize HID Devices";
    }
    else {

#ifdef HID_API_VERSION_MAJOR
#if defined(__APPLE__) && HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
        hid_darwin_set_open_exclusive(0);
#endif
#endif
        
        qInfo(logUsbControl()) << "Found available HID devices (not all will be suitable for use):";
        struct hid_device_info* devs;
        devs = hid_enumerate(0x0, 0x0);
        while (devs) {
            qInfo(logUsbControl()) << QString("Device found: (%0:%1) %2 manufacturer: (%3)%4 usage: 0x%5 usage_page 0x%6")
                .arg(devs->vendor_id, 4, 16, QChar('0'))
                .arg(devs->product_id, 4, 16, QChar('0'))
                .arg(QString::fromWCharArray(devs->product_string))
                .arg(QString::fromWCharArray(devs->product_string))
                .arg(QString::fromWCharArray(devs->manufacturer_string))
                .arg(devs->usage, 4, 16, QChar('0'))
                .arg(devs->usage_page, 4, 16, QChar('0'));
            devs = devs->next;
        }
        hid_free_enumeration(devs);
        
    }
}

/* run() is called every 2s and attempts to connect to a supported controller */
void usbController::run()
{
    if (commands == Q_NULLPTR || hidStatus) {
        // We are not ready yet, commands haven't been loaded or hid hasn't been initialized!
        QTimer::singleShot(1000, this, SLOT(run()));
        return;
    }

#if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
    if (gamepad == Q_NULLPTR) {
        auto gamepads = QGamepadManager::instance()->connectedGamepads();
        if (!gamepads.isEmpty()) {
            qInfo(logUsbControl()) << "Found" << gamepads.size() << "Gamepad controllers";
            // If we got here, we have detected a gamepad of some description!
            gamepad = new QGamepad(*gamepads.begin(), this);
            qInfo(logUsbControl()) << "Gamepad 0 is " << gamepad->name();

            if (gamepad->name() == "Microsoft X-Box 360 pad 0")
            {
                usbDevice = xBoxGamepad;
            }
            else {
                usbDevice = unknownGamepad;
            }
            connect(gamepad, &QGamepad::buttonDownChanged, this, [this](bool pressed) {
                qInfo(logUsbControl()) << "Button Down" << pressed;
                this->buttonState("DOWN", pressed);
            });
            connect(gamepad, &QGamepad::buttonUpChanged, this, [this](bool pressed) {
                qInfo(logUsbControl()) << "Button Up" << pressed;
                this->buttonState("UP", pressed);
            });
            connect(gamepad, &QGamepad::buttonLeftChanged, this, [this](bool pressed) {
                qInfo(logUsbControl()) << "Button Left" << pressed;
                this->buttonState("LEFT", pressed);
            });
            connect(gamepad, &QGamepad::buttonRightChanged, this, [this](bool pressed) {
                qInfo(logUsbControl()) << "Button Right" << pressed;
                this->buttonState("RIGHT", pressed);
            });
            connect(gamepad, &QGamepad::buttonCenterChanged, this, [this](bool pressed) {
                qInfo(logUsbControl()) << "Button Center" << pressed;
                this->buttonState("CENTER", pressed);
            });
            connect(gamepad, &QGamepad::axisLeftXChanged, this, [this](double value) {
                qInfo(logUsbControl()) << "Left X" << value;
                this->buttonState("LEFTX", value);
            });
            connect(gamepad, &QGamepad::axisLeftYChanged, this, [this](double value) {
                qInfo(logUsbControl()) << "Left Y" << value;
                this->buttonState("LEFTY", value);
            });
            connect(gamepad, &QGamepad::axisRightXChanged, this, [this](double value) {
                qInfo(logUsbControl()) << "Right X" << value;
                this->buttonState("RIGHTX", value);
            });
            connect(gamepad, &QGamepad::axisRightYChanged, this, [this](double value) {
                qInfo(logUsbControl()) << "Right Y" << value;
                this->buttonState("RIGHTY", value);
            });
            connect(gamepad, &QGamepad::buttonAChanged, this, [this](bool pressed) {
                qInfo(logUsbControl()) << "Button A" << pressed;
                this->buttonState("A", pressed);
            });
            connect(gamepad, &QGamepad::buttonBChanged, this, [this](bool pressed) {
                qInfo(logUsbControl()) << "Button B" << pressed;
                this->buttonState("B", pressed);
            });
            connect(gamepad, &QGamepad::buttonXChanged, this, [this](bool pressed) {
                qInfo(logUsbControl()) << "Button X" << pressed;
                this->buttonState("X", pressed);
            });
            connect(gamepad, &QGamepad::buttonYChanged, this, [this](bool pressed) {
                qInfo(logUsbControl()) << "Button Y" << pressed;
                this->buttonState("Y", pressed);
            });
            connect(gamepad, &QGamepad::buttonL1Changed, this, [this](bool pressed) {
                qInfo(logUsbControl()) << "Button L1" << pressed;
                this->buttonState("L1", pressed);
            });
            connect(gamepad, &QGamepad::buttonR1Changed, this, [this](bool pressed) {
                qInfo(logUsbControl()) << "Button R1" << pressed;
                this->buttonState("R1", pressed);
            });
            connect(gamepad, &QGamepad::buttonL2Changed, this, [this](double value) {
                qInfo(logUsbControl()) << "Button L2: " << value;
                this->buttonState("L2", value);
            });
            connect(gamepad, &QGamepad::buttonR2Changed, this, [this](double value) {
                qInfo(logUsbControl()) << "Button R2: " << value;
                this->buttonState("R2", value);
            });
            connect(gamepad, &QGamepad::buttonSelectChanged, this, [this](bool pressed) {
                qInfo(logUsbControl()) << "Button Select" << pressed;
                this->buttonState("SELECT", pressed);
            });
            connect(gamepad, &QGamepad::buttonStartChanged, this, [this](bool pressed) {
                qInfo(logUsbControl()) << "Button Start" << pressed;
                this->buttonState("START", pressed);
            });
            connect(gamepad, &QGamepad::buttonGuideChanged, this, [this](bool pressed) {
                qInfo(logUsbControl()) << "Button Guide" << pressed;
            });

            emit newDevice(usbDevice, buttonList, knobList, commands, mutex); // Let the UI know we have a new controller
            return;
        }
    }
    else if (!gamepad->isConnected()) {
        delete gamepad;
        gamepad = Q_NULLPTR;
    }
#endif

    struct hid_device_info* devs;
    devs = hid_enumerate(0x0, 0x0);
    usbDeviceType tempDev = usbDevice;
    this->path = "";
    // Always only look for the first device and then exit
    // Maybe in the future we could add support for multiple devices?


    while (devs) {
        for (int i = 0; i < (int)sizeof knownUsbDevices / (int)sizeof knownUsbDevices[0]; i++) {
            if (devs->vendor_id == knownUsbDevices[i][1] && devs->product_id == knownUsbDevices[i][2] 
                && (knownUsbDevices[i][3] == 0x00 || devs->usage == knownUsbDevices[i][3])
                && (knownUsbDevices[i][4] == 0x00 || devs->usage_page == knownUsbDevices[i][4])) 
            {
                this->manufacturer = QString::fromWCharArray(devs->manufacturer_string);
                this->vendorId = devs->vendor_id;
                this->productId = devs->product_id;
                this->product = QString::fromWCharArray(devs->product_string);
                this->serial = QString::fromWCharArray(devs->serial_number);
                this->path = QString::fromLocal8Bit(devs->path);
                this->deviceId = QString("0x%1").arg(this->productId, 4, 16, QChar('0'));
                this->usbDevice = (usbDeviceType)knownUsbDevices[i][0];
                break;
            }
        } 
        devs = devs->next;
    }

    hid_free_enumeration(devs);

    if (!this->path.isEmpty()) {
        qInfo(logUsbControl()) << QString("Attempting to connect to %0").arg(this->product);
        this->handle = hid_open_path(this->path.toLocal8Bit());       
    }

    if (this->handle)
    {
        qInfo(logUsbControl()) << QString("Connected to device: %0 from %1 S/N %2").arg(this->product).arg(this->manufacturer).arg(this->serial);
        hid_set_nonblocking(this->handle, 1);

        // Set RC28 LEDs to default state
        if (usbDevice == RC28) {
            getVersion();
            ledControl(false, 0);
            ledControl(false, 1);
            ledControl(false, 2);
            ledControl(true, 3);
        }
        else if (usbDevice == eCoderPlus)
        {
            knobValues.clear();
            knobSend.clear();
            knobValues.append({ 0,0,0 });
            knobSend.append({ 0,0,0 });
        } 
        else if (usbDevice == QuickKeys) {
            // Subscribe to event streams
            
            QByteArray b(32,0x0);
            b[0] = (qint8)0x02;
            b[1] = (qint8)0xb0;
            b[2] = (qint8)0x04;

            b.replace(10, sizeof(this->deviceId), this->deviceId.toLocal8Bit());
            hid_write(this->handle, (const unsigned char*)b.constData(), b.size());

            b[0] = (qint8)0x02;
            b[1] = (qint8)0xb4;
            b[2] = (qint8)0x10;
            b.replace(10, sizeof(this->deviceId), this->deviceId.toLocal8Bit());

            hid_write(this->handle, (const unsigned char*)b.constData(), b.size()); 
        }


        // Let the UI know we have a new controller
        emit newDevice(usbDevice, buttonList, knobList, commands, mutex); 
        // Run the periodic timer to get data
        QTimer::singleShot(25, this, SLOT(runTimer()));
    }
    else 
    {

        // This should only get displayed once if we fail to connect to a device
        if (usbDevice != usbNone && tempDev != usbDevice) 
        {
            qInfo(logUsbControl()) << QString("Error connecting to  %0: %1")
                .arg(this->product)
                .arg(QString::fromWCharArray(hid_error(this->handle)));
        }
        // Call me again in 2 seconds to try connecting again
        QTimer::singleShot(2000, this, SLOT(run()));
    }

}

/* runTimer is called every 25ms once a connection to a supported controller is established */
void usbController::runTimer()
{
    int res=1;

    if (!this->handle) {
        // Something bad happened!
        QTimer::singleShot(25, this, SLOT(runTimer()));
        res = 0;
    }

    QMutexLocker locker(mutex);

    while (res > 0) {
        QByteArray data(HIDDATALENGTH, 0x0);
        res = hid_read(this->handle, (unsigned char*)data.data(), HIDDATALENGTH);
        if (res < 0)
        {
            qInfo(logUsbControl()) << "USB Device disconnected" << this->product;
            emit newDevice(0, buttonList, knobList, commands, mutex);
            this->product = "";
            this->manufacturer = "";
            this->serial = "<none>";
            hid_close(this->handle);
            this->handle = NULL;
            QTimer::singleShot(1000, this, SLOT(run()));
            return;
        }
        else if (res == 5 && (usbDevice == shuttleXpress || usbDevice == shuttlePro2))
        {
            data.resize(res);

            /*qDebug(logUsbControl()) << "usbController Data received " << hex << (unsigned char)data[0] << ":"
                << hex << (unsigned char)data[1] << ":"
                << hex << (unsigned char)data[2] << ":"
                << hex << (unsigned char)data[3] << ":"
                << hex << (unsigned char)data[4];
                */
            quint32 tempButtons = ((quint8)data[4] << 8) | ((quint8)data[3] & 0xff);
            unsigned char tempJogpos = (unsigned char)data[1];
            unsigned char tempShutpos = (unsigned char)data[0];

            if (tempJogpos == jogpos + 1 || (tempJogpos == 0 && jogpos == 0xff))
            {
                jogCounter++;
                //qDebug(logUsbControl()) << "JOG PLUS" << jogCounter;
            }
            else if (tempJogpos != jogpos) {
                jogCounter--;
                //qDebug(logUsbControl()) << "JOG MINUS" << jogCounter;
            }

            /* Button matrix:
                1000000000000000 = button15
                0100000000000000 = button14
                0010000000000000 = button13
                0001000000000000 = button12
                0000100000000000 = button11
                0000010000000000 = button10
                0000001000000000 = button9
                0000000100000000 = button8 - xpress0
                0000000010000000 = button7 - xpress1
                0000000001000000 = button6 - xpress2
                0000000000100000 = button5 - xpress3
                0000000000010000 = button4 - xpress4
                0000000000001000 = button3
                0000000000000100 = button2
                0000000000000010 = button1
                0000000000000001 = button0
            */
            if (buttons != tempButtons)
            {

                // Step through all buttons and emit ones that have been pressed.

                for (unsigned char i = 0; i < 16; i++)
                {
                    auto but = std::find_if(buttonList->begin(), buttonList->end(), [this, i](const BUTTON& b) 
                        { return (b.dev == this->usbDevice && b.num == i); });
                    if (but != buttonList->end()) {
                        if ((tempButtons >> i & 1) && !(buttons >> i & 1))
                        {
                            qDebug(logUsbControl()) << "On Button event:" << but->onCommand->text;
                            emit button(but->onCommand);
                        }
                        else if ((buttons >> i & 1) && !(tempButtons >> i & 1))
                        {
                            qDebug(logUsbControl()) << "Off Button event:" << but->offCommand->text;
                            emit button(but->offCommand);
                        }
                    }
                }
            }

            buttons = tempButtons;
            jogpos = tempJogpos;
            shutpos = tempShutpos;

        }
        else if ((res > 31) && usbDevice == RC28)
        {
            // This is a response from the Icom RC28
            if ((unsigned char)data[0] == 0x02) {
                qInfo(logUsbControl()) << QString("Received RC-28 Firmware Version: %0").arg(QString(data.mid(1,data.indexOf(" ")-1)));
            }
            else 
            {

                data.resize(8);
                // Buttons

                BUTTON* butptt = Q_NULLPTR;
                BUTTON* butf1 = Q_NULLPTR;
                BUTTON* butf2 = Q_NULLPTR;;

                for (BUTTON* but = buttonList->begin(); but != buttonList->end(); but++) {
                    if (but->dev == usbDevice) {

                        if (but->num == 0)
                        {
                            butptt = but;
                        }
                        else if (but->num == 1)
                        {
                            butf1 = but;
                        }
                        else if (but->num == 2)
                        {
                            butf2 = but;
                        }
                    }
                }

                if (butptt != Q_NULLPTR && !((unsigned char)data[5] ^ 0x06) && ((unsigned char)lastData[5] ^ 0x06))
                {
                    // TRANSMIT key down only (no other keys down)
                    qDebug(logUsbControl()) << "PTT key down";
                    qInfo(logUsbControl()) << "On Button event:" << butptt->onCommand->text;
                    ledControl(true, 0);
                    emit button(butptt->onCommand);

                }
                else if (butptt != Q_NULLPTR && ((unsigned char)data[5] ^ 0x06) && !((unsigned char)lastData[5] ^ 0x06))
                {
                    // TRANSMIT key up only (no other keys down)
                    //emit button(false, 6);
                    qDebug(logUsbControl()) << "PTT key up";
                    qInfo(logUsbControl()) << "Off Button event:" << butptt->offCommand->text;
                    ledControl(false, 0);
                    emit button(butptt->offCommand);
                }
                
                if (butf1 != Q_NULLPTR && !((unsigned char)data[5] ^ 0x05) && ((unsigned char)lastData[5] ^ 0x05))
                {
                    // F-1 key up only (no other keys down)
                    //emit button(true, 5);
                    qDebug(logUsbControl()) << "F-1 key down";
                    qInfo(logUsbControl()) << "On Button event:" << butf1->onCommand->text;
                    ledControl(true, 1);
                    emit button(butf1->onCommand);
                }
                else if (butf1 != Q_NULLPTR && ((unsigned char)data[5] ^ 0x05) && !((unsigned char)lastData[5] ^ 0x05))
                {
                    // F-1 key down only (no other keys down)
                    //emit button(false, 5);
                    qDebug(logUsbControl()) << "F-1 key up";
                    qInfo(logUsbControl()) << "Off Button event:" << butf1->offCommand->text;
                    ledControl(false, 1);
                    emit button(butf1->offCommand);
                }
                
                if (butf2 != Q_NULLPTR && !((unsigned char)data[5] ^ 0x03) && ((unsigned char)lastData[5] ^ 0x03))
                {
                    // F-2 key up only (no other keys down)
                    //emit button(true, 7);
                    qDebug(logUsbControl()) << "F-2 key down";
                    qInfo(logUsbControl()) << "On Button event:" << butf2->onCommand->text;
                    ledControl(true, 2);
                    emit button(butf2->onCommand);
                }
                else if (butf2 != Q_NULLPTR && ((unsigned char)data[5] ^ 0x03) && !((unsigned char)lastData[5] ^ 0x03))
                {
                    // F-2 key down only (no other keys down)
                    //emit button(false, 7);
                    qDebug(logUsbControl()) << "F-2 key up";
                    qInfo(logUsbControl()) << "Off Button event:" << butf2->offCommand->text;
                    ledControl(false, 2);
                    emit button(butf2->offCommand);
                }

                if ((unsigned char)data[5] == 0x07)
                {
                    if ((unsigned char)data[3] == 0x01)
                    {
                        jogCounter = jogCounter + data[1];
                    }
                    else if ((unsigned char)data[3] == 0x02)
                    {
                        jogCounter = jogCounter - data[1];
                    }
                }

                lastData = data;
            }
        }
        else if (usbDevice == eCoderPlus && data.length() > 0x0f && (quint8)data[0] == 0xff) {
            /* Button matrix:
            DATA3   DATA2     DATA 1
            765432107654321076543210
            001000000000000000000000 = Left CW
            000100000000000000000000 = Right CW
            000010000000000000000000 = PTT
            000001000000000000000000 = Knob 3 Press
            000000100000000000000000 = Knob 2 Press
            000000010000000000000000 = Knob 1 Press
            000000000100000000000000 = button14
            000000000010000000000000 = button13
            000000000001000000000000 = button12
            000000000000100000000000 = button11
            000000000000010000000000 = button10
            000000000000001000000000 = button9
            000000000000000100000000 = button8 
            000000000000000010000000 = button7 
            000000000000000001000000 = button6
            000000000000000000100000 = button5
            000000000000000000010000 = button4
            000000000000000000001000 = button3
            000000000000000000000100 = button2
            000000000000000000000010 = button1
            */
            quint32 tempButtons = ((quint8)data[3] << 16) | ((quint8)data[2] << 8) | ((quint8)data[1] & 0xff);
            quint32 tempKnobs = ((quint8)data[16] << 16) | ((quint8)data[15] << 8) | ((quint8)data[14] & 0xff);

            if (buttons != tempButtons)
            {
                // Step through all buttons and emit ones that have been pressed.
                for (unsigned char i = 1; i < 23; i++)
                {
                    auto but = std::find_if(buttonList->begin(), buttonList->end(), [this, i](const BUTTON& b)
                    { return (b.dev == this->usbDevice && b.num == i); });
                    if (but != buttonList->end()) {
                        if ((tempButtons >> i & 1) && !(buttons >> i & 1))
                        {
                            qDebug(logUsbControl()) << "On Button event:" << but->onCommand->text;
                            emit button(but->onCommand);
                        }
                        else if ((buttons >> i & 1) && !(tempButtons >> i & 1))
                        {
                            qDebug(logUsbControl()) << "Off Button event:" << but->offCommand->text;
                            emit button(but->offCommand);
                        }
                    }
                }
            }
            buttons = tempButtons;

            if (knobs != tempKnobs) {
                // One of the knobs has moved
                for (unsigned char i = 0; i < 3; i++) {
                    if ((tempKnobs >> (i * 8) & 0xff) != (knobs >> (i * 8) & 0xff)) {
                        knobValues[i] = knobValues[i] + (qint8)((knobs >> (i * 8)) & 0xff);
                    }
                }
            }
            knobs = tempKnobs;
 
            // Tuning knob
            jogCounter = jogCounter + (qint8)data[13];

        } else if (usbDevice == QuickKeys && (quint8)data[0] == 0x02) {

            if ((quint8)data[1] == 0xf0) {

                //qInfo(logUsbControl()) << "Received:" << data;
                quint32 tempButtons = (data[3] << 8) | (data[2] & 0xff);

                // Step through all buttons and emit ones that have been pressed.
                for (unsigned char i = 0; i < 10; i++)
                {
                    auto but = std::find_if(buttonList->begin(), buttonList->end(), [this, i](const BUTTON& b)
                    { return (b.dev == this->usbDevice && b.num == i); });
                    if (but != buttonList->end()) {
                        if ((tempButtons >> i & 1) && !(buttons >> i & 1))
                        {
                            qDebug(logUsbControl()) << "On Button event:" << but->onCommand->text;
                            emit button(but->onCommand);
                            programButton(but->num, but->offCommand->text);
                        }
                        else if ((buttons >> i & 1) && !(tempButtons >> i & 1))
                        {
                            qDebug(logUsbControl()) << "Off Button event:" << but->offCommand->text;
                            emit button(but->offCommand);
                            programButton(but->num, but->onCommand->text);
                        }
                    }
                }

                buttons = tempButtons;

                // Tuning knob
                if (data[7] & 0x01) {
                    jogCounter++;
                }
                else if (data[7] & 0x02) {
                    jogCounter--;
                }
            } 
            else if ((quint8)data[1] == 0xf2 && (quint8)data[2] == 0x01)
            {
                // Battery level
                quint8 battery = (quint8)data[3];
                qDebug(logUsbControl()) << QString("Battery level %1 %").arg(battery);
            }
        }

        if (lastusbController.msecsTo(QTime::currentTime()) >= 100 || lastusbController > QTime::currentTime())
        {
            if (usbDevice == shuttleXpress || usbDevice == shuttlePro2)
            {
                if (shutpos > 0  && shutpos < 0x08)
                {
                    shutMult = shutpos;
                    emit doShuttle(true, shutMult);
                    qDebug(logUsbControl()) << "Shuttle PLUS" << shutMult;

                }
                else if (shutpos > 0xEF) {
                    shutMult = abs(shutpos - 0xff) + 1;
                    emit doShuttle(false, shutMult);
                    qDebug(logUsbControl()) << "Shuttle MINUS" << shutMult;
                }
            }

            if (usbDevice == eCoderPlus) {
                for (unsigned char i = 0; i < 3; i++) {
                    for (KNOB* kb = knobList->begin(); kb != knobList->end(); kb++) {
                        if (kb && kb->dev == usbDevice && kb->num == i + 1 && knobValues[i]) {
                            COMMAND cmd;
                            cmd.command = kb->command->command;
                            if (knobSend[i] + (knobValues[i] * 10) <= 0)
                            {
                                knobSend[i] = 0;
                            }
                            else if (knobSend[i] + (knobValues[i] * 10) >= 255)
                            {
                                knobSend[i] = 255;
                            }
                            else {
                                knobSend[i] = knobSend[i] + (knobValues[i] * 10);
                            }
                            cmd.suffix = knobSend[i];
                            qInfo(logUsbControl()) << "Sending Knob:" << kb->num << "Command:" << cmd.command << " Value:" << cmd.suffix << "Raw value:" << knobValues[i];
                            emit button(&cmd);
                            knobValues[i] = 0;
                        }
                    }
                }
            }

            if (jogCounter != 0) {
                emit sendJog(jogCounter/sensitivity);
                qDebug(logUsbControl()) << "Change Frequency by" << jogCounter << "hz";
                jogCounter = 0;
            }

            lastusbController = QTime::currentTime();
        }

    }

    // Run every 25ms
    QTimer::singleShot(25, this, SLOT(runTimer()));
}


/* Functions below receive various settings from other classes/threads */
void usbController::receiveCommands(QVector<COMMAND>* cmds)
{
    qDebug(logUsbControl()) << "Receiving commands";
    commands = cmds;
}

void usbController::receiveButtons(QVector<BUTTON>* buts)
{
    qDebug(logUsbControl()) << "Receiving buttons";
    buttonList = buts;
}

void usbController::receiveKnobs(QVector<KNOB>* kbs)
{
    qDebug(logUsbControl()) << "Receiving knobs";
    knobList = kbs;
}

void usbController::receiveSensitivity(int val)
{
    sensitivity = val;
}

void usbController::receivePTTStatus(bool on) {
    static QColor lastColour = currentColour;
    static bool ptt;
    if (on && !ptt) {
        lastColour = currentColour;
        programWheelColour(255, 0, 0);
    }
    else {
        programWheelColour((quint8)lastColour.red(), (quint8)lastColour.green(), (quint8)lastColour.blue());
    }
    ptt = on;
}

/*
 * All functions below here are for specific controllers
*/

/* Functions below are for RC28 */

void usbController::ledControl(bool on, unsigned char num)
{
    if (usbDevice == RC28) {
        QByteArray data(3, 0x0);
        data[1] = 0x01;
        static unsigned char ledNum = 0x07;
        if (on)
            ledNum &= ~(1UL << num);
        else
            ledNum |= 1UL << num;

        data[2] = ledNum;

        int res = hid_write(this->handle, (const unsigned char*)data.constData(), data.size());

        if (res < 0) {
            qDebug(logUsbControl()) << "Unable to write(), Error:" << hid_error(this->handle);
            return;
        }

        qDebug(logUsbControl()) << "write() success";
    }
}

void usbController::getVersion()
{
    QByteArray data(64, 0x0);
    data[0] = 63;
    data[1] = 0x02;
    int res = hid_write(handle, (const unsigned char*)data.constData(), data.size());

    if (res < 0) {
        qDebug(logUsbControl()) << "Unable to write(), Error:" << hid_error(handle);
    }
}

/* End of RC28 functions*/

/* Functions below are for Gamepad controllers */
void usbController::buttonState(QString name, bool val)
{
    for (BUTTON* but = buttonList->begin(); but != buttonList->end(); but++) {
        if (but->dev == usbDevice && but->name == name) {

            if (val && but->onCommand->index > 0) {
                qInfo(logUsbControl()) << "On Button" << but->name << "event:" << but->onCommand->text;
                emit button(but->onCommand);
            }
            if (!val && but->offCommand->index > 0) {
                qInfo(logUsbControl()) << "Off Button" << but->name << "event:" << but->offCommand->text;
                emit button(but->offCommand);
            }
        }
    }
}

void usbController::buttonState(QString name, double val)
{

    if (name == "LEFTX")
    {
        int value = val * 1000000;
        emit sendJog(value);
    }
    /*
    for (BUTTON* but = buttonList->begin(); but != buttonList->end(); but++) {
        if (but->dev == usbDevice && but->name == name) {

            if (val && but->onCommand->index > 0) {
                qInfo(logUsbControl()) << "On Button" << but->name << "event:" << but->onCommand->text;
                emit button(but->onCommand);
            }
            if (!val && but->offCommand->index > 0) {
                qInfo(logUsbControl()) << "Off Button" << but->name << "event:" << but->offCommand->text;
                emit button(but->offCommand);
            }
        }
    }
    */
}

/* End of Gamepad functions*/


/* Functions below are for Xencelabs QuickKeys*/
void usbController::programButton(quint8 val, QString text)
{
    if (handle && usbDevice == QuickKeys && val < 8) {
        text = text.mid(0, 10); // Make sure text is no more than 10 characters.
        qDebug(logUsbControl()) << QString("Programming button %0 with %1").arg(val).arg(text);
        QByteArray data(32, 0x0);
        data[0] = (qint8)0x02;
        data[1] = (qint8)0xb1;
        data[3] = val + 1;
        data[5] = text.length() * 2;
        data.replace(10, this->deviceId.size(), this->deviceId.toLocal8Bit());

        QByteArray le = qToLittleEndian(QByteArray::fromRawData(reinterpret_cast<const char*>(text.constData()), text.size() * 2));
        data.replace(16, le.size(), le);

        int res = hid_write(this->handle, (const unsigned char*)data.constData(), data.size());

        if (res < 0) {
            qDebug(logUsbControl()) << "Unable to write(), Error:" << hid_error(this->handle);
            return;
        }
    }
}

void usbController::programBrightness(quint8 val) {
    if (handle && usbDevice == QuickKeys) {
        qDebug(logUsbControl()) << QString("Programming brightness to %0").arg(val);
        QByteArray data(32, 0x0);
        data[0] = (qint8)0x02;
        data[1] = (qint8)0xb1;
        data[2] = (qint8)0x0a;
        data[3] = (qint8)0x01;
        data[4] = val;
        data.replace(10, this->deviceId.size(), this->deviceId.toLocal8Bit());
        hid_write(this->handle, (const unsigned char*)data.constData(), data.size());
    }
}

void usbController::programOrientation(quint8 val) {
    if (handle && usbDevice == QuickKeys) {
        qDebug(logUsbControl()) << QString("Programming orientation to %0").arg(val);
        QByteArray data(32, 0x0);
        data[0] = (qint8)0x02;
        data[1] = (qint8)0xb1;
        data[2] = (qint8)val;
        data.replace(10, this->deviceId.size(), this->deviceId.toLocal8Bit());
        hid_write(this->handle, (const unsigned char*)data.constData(), data.size());
    }
}

void usbController::programSpeed(quint8 val) {
    if (handle && usbDevice == QuickKeys) {
        qDebug(logUsbControl()) << QString("Programming speed to %0").arg(val);
        QByteArray data(32, 0x0);
        data[0] = (qint8)0x02;
        data[1] = (qint8)0xb4;
        data[2] = (qint8)0x04;
        data[3] = (qint8)0x01;
        data[4] = (qint8)0x01;
        data[5] = val;
        data.replace(10, this->deviceId.size(), this->deviceId.toLocal8Bit());
        hid_write(this->handle, (const unsigned char*)data.constData(), data.size());
    }
}


void usbController::programWheelColour(quint8 r, quint8 g, quint8 b)
{
    if (handle && usbDevice == QuickKeys) {
        QByteArray data(32, 0x0);
        data[0] = (qint8)0x02;
        data[1] = (qint8)0xb4;
        data[2] = (qint8)0x01;
        data[3] = (qint8)0x01;
        data[6] = (qint8)r;
        data[7] = (qint8)g;
        data[8] = (qint8)b;
        data.replace(10, this->deviceId.size(), this->deviceId.toLocal8Bit());
        hid_write(this->handle, (const unsigned char*)data.constData(), data.size());
        currentColour.setRed(r);
        currentColour.setGreen(g);
        currentColour.setBlue(b);
    }
}

void usbController::programOverlay(quint8 duration, QString text)
{
    if (handle && usbDevice == QuickKeys) {
        text = text.mid(0, 32);
        QByteArray data(32, 0x0);
        data[0] = (qint8)0x02;
        data[1] = (qint8)0xb1;
        data[3] = (qint8)duration;
        data.replace(10, this->deviceId.size(), this->deviceId.toLocal8Bit());
        for (int i = 0; i < text.length(); i = i + 8)
        {
            data[2] = (i == 0) ? 0x05 : 0x06;
            QByteArray le = qToLittleEndian(QByteArray::fromRawData(reinterpret_cast<const char*>(text.mid(i, 8).constData()), text.mid(i, 8).size() * 2));
            data.replace(16, le.size(), le);
            data[5] = text.mid(i, 8).length() * 2;
            data[6] = (i > 0 && text.mid(i).size() > 8) ? 0x01 : 0x00;
            hid_write(this->handle, (const unsigned char*)data.constData(), data.size());
        }
        //qInfo(logUsbControl()) << "Sent overlay" << text;
    }
}

void usbController::programTimeout(quint8 val)
{
    if (handle && usbDevice == QuickKeys) {
        qInfo(logUsbControl()) << QString("Programming timeout to %0 minutes").arg(val);
        QByteArray data(32, 0x0);
        data[0] = (qint8)0x02;
        data[1] = (qint8)0xb4;
        data[2] = (qint8)0x08;
        data[3] = (qint8)0x01;
        data[4] = val;
        data.replace(10, this->deviceId.size(), this->deviceId.toLocal8Bit());
        hid_write(this->handle, (const unsigned char*)data.constData(), data.size());
    }
}

/* End of functions for Xencelabs QuickKeys*/


#endif
