///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022-2024 Jon Beniston, M7RCE <jon@beniston.com>                //
// Copyright (C) 2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com>               //
// Copyright (C) 2022 Jiří Pinkava <jiri.pinkava@rossum.ai>                      //
//                                                                               //
// This program is free software; you can redistribute it and/or modify          //
// it under the terms of the GNU General Public License as published by          //
// the Free Software Foundation as version 3 of the License, or                  //
// (at your option) any later version.                                           //
//                                                                               //
// This program is distributed in the hope that it will be useful,               //
// but WITHOUT ANY WARRANTY; without even the implied warranty of                //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the                  //
// GNU General Public License V3 for more details.                               //
//                                                                               //
// You should have received a copy of the GNU General Public License             //
// along with this program. If not, see <http://www.gnu.org/licenses/>.          //
///////////////////////////////////////////////////////////////////////////////////

#include <QDebug>
#include <cstring>

#include "device/deviceapi.h"
#include "util/message.h"
#include "maincore.h"

#include "remotetcpinputtcphandler.h"
#include "remotetcpinput.h"
#include "../../channelrx/remotetcpsink/remotetcpprotocol.h"

MESSAGE_CLASS_DEFINITION(RemoteTCPInputTCPHandler::MsgReportRemoteDevice, Message)
MESSAGE_CLASS_DEFINITION(RemoteTCPInputTCPHandler::MsgReportConnection, Message)
MESSAGE_CLASS_DEFINITION(RemoteTCPInputTCPHandler::MsgConfigureTcpHandler, Message)

RemoteTCPInputTCPHandler::RemoteTCPInputTCPHandler(SampleSinkFifo *sampleFifo, DeviceAPI *deviceAPI, ReplayBuffer<FixReal> *replayBuffer) :
    m_deviceAPI(deviceAPI),
    m_running(false),
    m_dataSocket(nullptr),
    m_tcpSocket(nullptr),
    m_webSocket(nullptr),
    m_tcpBuf(nullptr),
    m_sampleFifo(sampleFifo),
	m_replayBuffer(replayBuffer),
    m_messageQueueToInput(nullptr),
    m_messageQueueToGUI(nullptr),
    m_fillBuffer(true),
    m_timer(this),
    m_reconnectTimer(this),
    m_sdra(false),
    m_converterBuffer(nullptr),
    m_converterBufferNbSamples(0),
    m_settings(),
    m_remoteControl(true),
    m_iqOnly(false),
    m_decoder(nullptr),
    m_zOutBuf(m_zBufSize, '\0'),
    m_blacklisted(false),
    m_magsq(0.0f),
    m_magsqSum(0.0f),
    m_magsqPeak(0.0f),
    m_magsqCount(0)
{
    m_sampleFifo->setSize(5000000); // Start with large FIFO, to avoid having to resize
    m_tcpBuf = new char[m_sampleFifo->size()*2*4];
    m_timer.setInterval(50); // Previously 125, but this results in an obviously slow spectrum refresh rate
    connect(&m_reconnectTimer, SIGNAL(timeout()), this, SLOT(reconnect()));
    m_reconnectTimer.setSingleShot(true);

    // Initialise zlib decompressor
    m_zStream.zalloc = nullptr;
    m_zStream.zfree = nullptr;
    m_zStream.opaque = nullptr;
    m_zStream.avail_in = 0;
    m_zStream.next_in = nullptr;
    if (Z_OK != inflateInit(&m_zStream)) {
        qDebug() << "RemoteTCPInputTCPHandler::RemoteTCPInputTCPHandler: inflateInit failed.";
    }
}

RemoteTCPInputTCPHandler::~RemoteTCPInputTCPHandler()
{
    qDebug() << "RemoteTCPInputTCPHandler::~RemoteTCPInputTCPHandler";
    delete[] m_tcpBuf;
    if (m_converterBuffer) {
        delete[] m_converterBuffer;
    }
    cleanup();
}

void RemoteTCPInputTCPHandler::reset()
{
    QMutexLocker mutexLocker(&m_mutex);
    m_inputMessageQueue.clear();
    m_blacklisted = false;
}

// start() is called from DSPDeviceSourceEngine thread
// QTcpSockets need to be created on same thread they are used from, so only create it in started()
void RemoteTCPInputTCPHandler::start()
{
    QMutexLocker mutexLocker(&m_mutex);

    qDebug("RemoteTCPInputTCPHandler::start");

    if (m_running) {
        return;
    }
    connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
    connect(thread(), SIGNAL(started()), this, SLOT(started()));
    connect(thread(), SIGNAL(finished()), this, SLOT(finished()));

    m_running = true;
}

void RemoteTCPInputTCPHandler::stop()
{
    QMutexLocker mutexLocker(&m_mutex);

    qDebug("RemoteTCPInputTCPHandler::stop");

    disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
}

void RemoteTCPInputTCPHandler::started()
{
    QMutexLocker mutexLocker(&m_mutex);

    // Don't connectToHost until we get settings
    connect(&m_timer, SIGNAL(timeout()), this, SLOT(processData()));

    disconnect(thread(), SIGNAL(started()), this, SLOT(started()));
}

void RemoteTCPInputTCPHandler::finished()
{
    qDebug("RemoteTCPInputTCPHandler::finished");
    QMutexLocker mutexLocker(&m_mutex);
    m_timer.stop();
    disconnect(&m_timer, SIGNAL(timeout()), this, SLOT(processData()));
    //disconnectFromHost();
    cleanup();
    disconnect(thread(), SIGNAL(finished()), this, SLOT(finished()));
    m_running = false;
}

void RemoteTCPInputTCPHandler::connectToHost(const QString& address, quint16 port, const QString& protocol)
{
    qDebug("RemoteTCPInputTCPHandler::connectToHost: connect to %s %s:%d", protocol.toStdString().c_str(), address.toStdString().c_str(), port);
    m_fillBuffer = true;
    m_readMetaData = false;
    if (protocol == "SDRangel wss")
    {
#ifndef QT_NO_OPENSSL
        m_webSocket = new QWebSocket(QString(), QWebSocketProtocol::VersionLatest, this);
        connect(m_webSocket, &QWebSocket::binaryFrameReceived, this, &RemoteTCPInputTCPHandler::dataReadyRead);
        connect(m_webSocket, &QWebSocket::connected, this, &RemoteTCPInputTCPHandler::connected);
        connect(m_webSocket, &QWebSocket::disconnected, this, &RemoteTCPInputTCPHandler::disconnected);
#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
        connect(m_webSocket, &QWebSocket::errorOccurred, this, &RemoteTCPInputTCPHandler::errorOccurred);
#endif
        connect(m_webSocket, &QWebSocket::sslErrors, this, &RemoteTCPInputTCPHandler::sslErrors);
        m_webSocket->open(QUrl(QString("wss://%1:%2").arg(address).arg(port)));
        if (m_webSocket) {
            m_dataSocket = new WebSocket(m_webSocket);
        }
#else
        qWarning() << "RemoteTCPInput unable to use wss protocol as SSL is not supported";
#endif
    }
    else
    {
        m_tcpSocket = new QTcpSocket(this);
        connect(m_tcpSocket, SIGNAL(readyRead()), this, SLOT(dataReadyRead()));
        connect(m_tcpSocket, SIGNAL(connected()), this, SLOT(connected()));
        connect(m_tcpSocket, SIGNAL(disconnected()), this, SLOT(disconnected()));
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
        connect(m_tcpSocket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), this, &RemoteTCPInputTCPHandler::errorOccurred);
#else
        connect(m_tcpSocket, &QAbstractSocket::errorOccurred, this, &RemoteTCPInputTCPHandler::errorOccurred);
#endif
        m_tcpSocket->connectToHost(address, port);
        if (m_tcpSocket) { // Can be set to nullptr by RemoteTCPInputTCPHandler::errorOccurred while connecting
            m_dataSocket = new TCPSocket(m_tcpSocket);
        }
    }
}

/*void RemoteTCPInputTCPHandler::disconnectFromHost()
{
    if (m_dataSocket)
    {
        qDebug() << "RemoteTCPInputTCPHandler::disconnectFromHost";
        disconnect(m_dataSocket, SIGNAL(readyRead()), this, SLOT(dataReadyRead()));
        disconnect(m_dataSocket, SIGNAL(connected()), this, SLOT(connected()));
        disconnect(m_dataSocket, SIGNAL(disconnected()), this, SLOT(disconnected()));
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
        disconnect(m_dataSocket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), this, &RemoteTCPInputTCPHandler::errorOccurred);
#else
        disconnect(m_dataSocket, &QAbstractSocket::errorOccurred, this, &RemoteTCPInputTCPHandler::errorOccurred);
#endif
        //m_dataSocket->disconnectFromHost();
        cleanup();
    }
}*/

void RemoteTCPInputTCPHandler::cleanup()
{
    if (m_decoder)
    {
        FLAC__stream_decoder_delete(m_decoder);
        m_decoder = nullptr;
    }
#ifndef QT_NO_OPENSSL
    if (m_webSocket)
    {
        qDebug() << "RemoteTCPInputTCPHandler::cleanup: Closing and deleting web socket";
        disconnect(m_webSocket, &QWebSocket::binaryFrameReceived, this, &RemoteTCPInputTCPHandler::dataReadyRead);
        disconnect(m_webSocket, &QWebSocket::connected, this, &RemoteTCPInputTCPHandler::connected);
        disconnect(m_webSocket, &QWebSocket::disconnected, this, &RemoteTCPInputTCPHandler::disconnected);
#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
        disconnect(m_webSocket, &QWebSocket::errorOccurred, this, &RemoteTCPInputTCPHandler::errorOccurred);
#endif
    }
#endif
    if (m_tcpSocket)
    {
        qDebug() << "RemoteTCPInputTCPHandler::cleanup: Closing and deleting TCP socket";
        // Disconnect disconnected, so don't get called recursively
        disconnect(m_tcpSocket, SIGNAL(readyRead()), this, SLOT(dataReadyRead()));
        disconnect(m_tcpSocket, SIGNAL(connected()), this, SLOT(connected()));
        disconnect(m_tcpSocket, SIGNAL(disconnected()), this, SLOT(disconnected()));
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
        disconnect(m_tcpSocket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), this, &RemoteTCPInputTCPHandler::errorOccurred);
#else
        disconnect(m_tcpSocket, &QAbstractSocket::errorOccurred, this, &RemoteTCPInputTCPHandler::errorOccurred);
#endif
    }
    if (m_dataSocket)
    {
        m_dataSocket->close();
        m_dataSocket->deleteLater();
        m_dataSocket = nullptr;
    }
    if (m_webSocket)
    {
        m_webSocket->deleteLater();
        m_webSocket = nullptr;
    }
    if (m_tcpSocket)
    {
        m_tcpSocket->deleteLater();
        m_tcpSocket = nullptr;
    }
}

// Clear input buffer when settings change that invalidate the data in it
// E.g. sample rate or bit depth
void RemoteTCPInputTCPHandler::clearBuffer()
{
    if (m_dataSocket && m_readMetaData)
    {
        if (m_spyServer)
        {
            // Can't just flush buffer, otherwise we'll lose header sync
            // Read and throw away any available data
            processSpyServerData(m_dataSocket->bytesAvailable(), true);
            m_fillBuffer = true;
        }
        else
        {
            m_dataSocket->flush();
            if (!m_decoder) { // Can't throw away FLAC header
                m_dataSocket->readAll();
                m_fillBuffer = true;
            }
        }
    }
}

void RemoteTCPInputTCPHandler::sendCommand(RemoteTCPProtocol::Command cmd, quint32 value)
{
    QMutexLocker mutexLocker(&m_mutex);
    quint8 request[5];

    request[0] = (quint8) cmd;
    RemoteTCPProtocol::encodeUInt32(&request[1], value);
    if (m_dataSocket)
    {
        qint64 len = m_dataSocket->write((char*)request, sizeof(request));
        if (len != sizeof(request)) {
            qDebug() << "RemoteTCPInputTCPHandler::sendCommand: Failed to write all of request:" << len;
        }
    } else {
        qDebug() << "RemoteTCPInputTCPHandler::sendCommand: No socket";
    }
}

void RemoteTCPInputTCPHandler::sendCommandFloat(RemoteTCPProtocol::Command cmd, float value)
{
    QMutexLocker mutexLocker(&m_mutex);
    quint8 request[5];

    request[0] = (quint8) cmd;
    RemoteTCPProtocol::encodeFloat(&request[1], value);
    if (m_dataSocket)
    {
        qint64 len = m_dataSocket->write((char*)request, sizeof(request));
        if (len != sizeof(request)) {
            qDebug() << "RemoteTCPInputTCPHandler::sendCommand: Failed to write all of request:" << len;
        }
    } else {
        qDebug() << "RemoteTCPInputTCPHandler::sendCommand: No socket";
    }
}

void RemoteTCPInputTCPHandler::setSampleRate(int sampleRate)
{
    sendCommand(RemoteTCPProtocol::setSampleRate, sampleRate);
}

void RemoteTCPInputTCPHandler::setCenterFrequency(quint64 frequency)
{
    sendCommand(RemoteTCPProtocol::setCenterFrequency, frequency); // FIXME: Can't support >4GHz
}

void RemoteTCPInputTCPHandler::setTunerAGC(bool agc)
{
    sendCommand(RemoteTCPProtocol::setTunerGainMode, agc);
}

void RemoteTCPInputTCPHandler::setTunerGain(int gain)
{
    sendCommand(RemoteTCPProtocol::setTunerGain, gain);
}

void RemoteTCPInputTCPHandler::setGainByIndex(int index)
{
    sendCommand(RemoteTCPProtocol::setGainByIndex, index);
}

void RemoteTCPInputTCPHandler::setFreqCorrection(int correction)
{
    sendCommand(RemoteTCPProtocol::setFrequencyCorrection, correction);
}

void RemoteTCPInputTCPHandler::setIFGain(quint16 stage, quint16 gain)
{
    sendCommand(RemoteTCPProtocol::setTunerIFGain, (stage << 16) | gain);
}

void RemoteTCPInputTCPHandler::setAGC(bool agc)
{
    sendCommand(RemoteTCPProtocol::setAGCMode, agc);
}

void RemoteTCPInputTCPHandler::setDirectSampling(bool enabled)
{
    sendCommand(RemoteTCPProtocol::setDirectSampling, enabled ? 3 : 0);
}

void RemoteTCPInputTCPHandler::setDCOffsetRemoval(bool enabled)
{
    sendCommand(RemoteTCPProtocol::setDCOffsetRemoval, enabled);
}

void RemoteTCPInputTCPHandler::setIQCorrection(bool enabled)
{
    sendCommand(RemoteTCPProtocol::setIQCorrection, enabled);
}

void RemoteTCPInputTCPHandler::setBiasTee(bool enabled)
{
    sendCommand(RemoteTCPProtocol::setBiasTee, enabled);
}

void RemoteTCPInputTCPHandler::setBandwidth(int bandwidth)
{
    sendCommand(RemoteTCPProtocol::setTunerBandwidth, bandwidth);
}

void RemoteTCPInputTCPHandler::setDecimation(int dec)
{
    sendCommand(RemoteTCPProtocol::setDecimation, dec);
}

void RemoteTCPInputTCPHandler::setChannelSampleRate(int sampleRate)
{
    sendCommand(RemoteTCPProtocol::setChannelSampleRate, sampleRate);
}

void RemoteTCPInputTCPHandler::setChannelFreqOffset(int offset)
{
    sendCommand(RemoteTCPProtocol::setChannelFreqOffset, offset);
}

void RemoteTCPInputTCPHandler::setChannelGain(int gain)
{
    sendCommand(RemoteTCPProtocol::setChannelGain, gain);
}

void RemoteTCPInputTCPHandler::setSampleBitDepth(int sampleBits)
{
    sendCommand(RemoteTCPProtocol::setSampleBitDepth, sampleBits);
}

void RemoteTCPInputTCPHandler::setSquelchEnabled(bool enabled)
{
    sendCommand(RemoteTCPProtocol::setIQSquelchEnabled, (quint32) enabled);
}

void RemoteTCPInputTCPHandler::setSquelch(float squelch)
{
    sendCommandFloat(RemoteTCPProtocol::setIQSquelch, squelch);
}

void RemoteTCPInputTCPHandler::setSquelchGate(float squelchGate)
{
    sendCommandFloat(RemoteTCPProtocol::setIQSquelchGate, squelchGate);
}

void RemoteTCPInputTCPHandler::sendMessage(const QString& callsign, const QString& text, bool broadcast)
{
    QMutexLocker mutexLocker(&m_mutex);

    if (m_dataSocket)
    {
        qint64 len;
        char cmd[1+4+1];
        QByteArray callsignBytes = callsign.toUtf8();
        QByteArray textBytes = text.toUtf8();
        QByteArray bytes;

        bytes.append(callsignBytes);
        bytes.append('\0');
        bytes.append(textBytes);
        bytes.append('\0');

        cmd[0] = (char) RemoteTCPProtocol::sendMessage;
        RemoteTCPProtocol::encodeUInt32((quint8*) &cmd[1], bytes.size() + 1);
        cmd[5] = (char) broadcast;

        len = m_dataSocket->write(&cmd[0], sizeof(cmd));
        if (len != sizeof(cmd)) {
            qDebug() << "RemoteTCPInputTCPHandler::set: Failed to write all of message header:" << len;
        }
        len = m_dataSocket->write(bytes.data(), bytes.size());
        if (len != bytes.size()) {
            qDebug() << "RemoteTCPInputTCPHandler::set: Failed to write all of message:" << len;
        }
        m_dataSocket->flush();
        qDebug() << "sendMessage" << text;
    } else {
        qDebug() << "RemoteTCPInputTCPHandler::sendMessage: No socket";
    }
}

void RemoteTCPInputTCPHandler::spyServerConnect()
{
    QMutexLocker mutexLocker(&m_mutex);

    quint8 request[8+4+9];
    SpyServerProtocol::encodeUInt32(&request[0], 0);
    SpyServerProtocol::encodeUInt32(&request[4], 4+9);
    SpyServerProtocol::encodeUInt32(&request[8], SpyServerProtocol::ProtocolID);
    memcpy(&request[8+4], "SDRangel", 9);
    if (m_dataSocket)
    {
        m_dataSocket->write((char*)request, sizeof(request));
        m_dataSocket->flush();
    }
}

void RemoteTCPInputTCPHandler::spyServerSet(int setting, int value)
{
    QMutexLocker mutexLocker(&m_mutex);

    quint8 request[8+8];
    SpyServerProtocol::encodeUInt32(&request[0], 2);
    SpyServerProtocol::encodeUInt32(&request[4], 8);
    SpyServerProtocol::encodeUInt32(&request[8], setting);
    SpyServerProtocol::encodeUInt32(&request[12], value);
    if (m_dataSocket)
    {
        m_dataSocket->write((char*)request, sizeof(request));
        m_dataSocket->flush();
    }
}

void RemoteTCPInputTCPHandler::spyServerSetIQFormat(int sampleBits)
{
    quint32 format;

    if (sampleBits == 8) {
        format = 1;
    } else if (sampleBits == 16) {
        format = 2;
    } else if (sampleBits == 24) {
        format = 3;
    } else if (sampleBits == 32) {
        format = 4; // This is float
    } else {
        qDebug() << "RemoteTCPInputTCPHandler::spyServerSetIQFormat: Unsupported value" << sampleBits;
        format = 1;
    }
    spyServerSet(SpyServerProtocol::setIQFormat, format);
}

void RemoteTCPInputTCPHandler::spyServerSetStreamIQ()
{
    spyServerSetIQFormat(m_settings.m_sampleBits);
    spyServerSet(SpyServerProtocol::setStreamingMode, 1);       // Stream IQ only
    spyServerSet(SpyServerProtocol::setStreamingEnabled, 1);    // Enable streaming
}

void RemoteTCPInputTCPHandler::applySettings(const RemoteTCPInputSettings& settings, const QList<QString>& settingsKeys, bool force)
{
    qDebug() << "RemoteTCPInputTCPHandler::applySettings: "
                << "force: " << force
                << settings.getDebugString(settingsKeys, force);
    QMutexLocker mutexLocker(&m_mutex);

    if (m_spyServer)
    {
        if (settingsKeys.contains("centerFrequency") || force) {
            spyServerSet(SpyServerProtocol::setCenterFrequency, settings.m_centerFrequency);
        }
        if ((settings.m_channelSampleRate != m_settings.m_channelSampleRate) || force)
        {
            // Resize FIFO to give us 1 second
            if ((settingsKeys.contains("channelSampleRate") || force) && (settings.m_channelSampleRate > (qint32)m_sampleFifo->size()))
            {
                qDebug() << "RemoteTCPInputTCPHandler::applySettings: Resizing sample FIFO from " << m_sampleFifo->size() << "to" << settings.m_channelSampleRate;
                m_sampleFifo->setSize(settings.m_channelSampleRate);
                delete[] m_tcpBuf;
                m_tcpBuf = new char[m_sampleFifo->size()*2*4];
                m_fillBuffer = true; // So we reprime FIFO
            }
            // Protocol only seems to allow changing decimation
            //spyServerSet(SpyServerProtocol::???, settings.m_channelSampleRate);
            clearBuffer();
        }
        if (settingsKeys.contains("sampleBits") || force)
        {
            spyServerSetIQFormat(settings.m_sampleBits);
            clearBuffer();
        }
        if (settingsKeys.contains("log2Decim") || force)
        {
            spyServerSet(SpyServerProtocol::setIQDecimation, settings.m_log2Decim);
            clearBuffer();
        }
        if (settingsKeys.contains("gain[0]") || force)
        {
            spyServerSet(SpyServerProtocol::setGain, settings.m_gain[0] / 10); // Convert 10ths dB to index
        }
    }
    else
    {
        if (settingsKeys.contains("centerFrequency") || force) {
            setCenterFrequency(settings.m_centerFrequency);
        }
        if (settingsKeys.contains("loPpmCorrection") || force) {
            setFreqCorrection(settings.m_loPpmCorrection);
        }
        if (settingsKeys.contains("dcBlock") || force) {
            if (m_sdra) {
                setDCOffsetRemoval(settings.m_dcBlock);
            }
        }
        if (settingsKeys.contains("iqCorrection") || force) {
            if (m_sdra) {
                setIQCorrection(settings.m_iqCorrection);
            }
        }
        if (settingsKeys.contains("biasTee") || force) {
            setBiasTee(settings.m_biasTee);
        }
        if (settingsKeys.contains("directSampling") || force) {
            setDirectSampling(settings.m_directSampling);
        }
        if (settingsKeys.contains("log2Decim") || force) {
            if (m_sdra) {
                setDecimation(settings.m_log2Decim);
            }
        }
        if (settingsKeys.contains("devSampleRate") || force) {
            setSampleRate(settings.m_devSampleRate);
        }
        if (settingsKeys.contains("agc") || force) {
            setAGC(settings.m_agc);
        }
        if (force) {
            setTunerAGC(1); // The SDRangel RTLSDR driver always has tuner gain as manual
        }
        if (settingsKeys.contains("gain[0]") || force) {
            setTunerGain(settings.m_gain[0]);
        }
        for (int i = 1; i < 3; i++)
        {
            if (settingsKeys.contains(QString("gain[%1]").arg(i)) || force) {
                setIFGain(i, settings.m_gain[i]);
            }
        }
        if (settingsKeys.contains("rfBW") || force) {
            setBandwidth(settings.m_rfBW);
        }
        if (settingsKeys.contains("inputFrequencyOffset") || force) {
            if (m_sdra) {
                setChannelFreqOffset(settings.m_inputFrequencyOffset);
            }
        }
        if (settingsKeys.contains("channelGain") || force) {
            if (m_sdra) {
                setChannelGain(settings.m_channelGain);
            }
        }
        if ((settings.m_channelSampleRate != m_settings.m_channelSampleRate) || force)
        {
            // Resize FIFO to give us 1 second
            if ((settingsKeys.contains("channelSampleRate") || force) && (settings.m_channelSampleRate > (qint32)m_sampleFifo->size()))
            {
                qDebug() << "RemoteTCPInputTCPHandler::applySettings: Resizing sample FIFO from " << m_sampleFifo->size() << "to" << settings.m_channelSampleRate;
                m_sampleFifo->setSize(settings.m_channelSampleRate);
                delete[] m_tcpBuf;
                m_tcpBuf = new char[m_sampleFifo->size()*2*4];
                m_fillBuffer = true; // So we reprime FIFO
            }
            if (m_sdra) {
                setChannelSampleRate(settings.m_channelSampleRate);
            }
            clearBuffer();
        }
        if (settingsKeys.contains("sampleBits") || force)
        {
            if (m_sdra) {
                setSampleBitDepth(settings.m_sampleBits);
            }
            clearBuffer();
        }
        if (settingsKeys.contains("squelchEnabled") || force)
        {
            if (m_sdra) {
                setSquelchEnabled(settings.m_squelchEnabled);
            }
        }
        if (settingsKeys.contains("squelch") || force)
        {
            if (m_sdra) {
                setSquelch(settings.m_squelch);
            }
        }
        if (settingsKeys.contains("squelchGate") || force)
        {
            if (m_sdra) {
                setSquelchGate(settings.m_squelchGate);
            }
        }
    }

    if (m_dataSocket) {
        m_dataSocket->flush(); // Apparently needed for WebAssembly with proxy
    }

    // Don't use force, as disconnect can cause rtl_tcp to quit
    if (settingsKeys.contains("dataAddress")
        || settingsKeys.contains("dataPort")
        || ((m_dataSocket == nullptr) && !m_blacklisted))
    {
        //disconnectFromHost();
        cleanup();
        connectToHost(settings.m_dataAddress, settings.m_dataPort, settings.m_protocol);
    }

    if (force) {
        m_settings = settings;
    } else {
        m_settings.applySettings(settingsKeys, settings);
    }
}

static FLAC__StreamDecoderReadStatus flacReadCallback(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *clientData)
{
    RemoteTCPInputTCPHandler *handler = (RemoteTCPInputTCPHandler *) clientData;

    return handler->flacRead(decoder, buffer, bytes);
}

static FLAC__StreamDecoderWriteStatus flacWriteCallback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *clientData)
{
    RemoteTCPInputTCPHandler *handler = (RemoteTCPInputTCPHandler *) clientData;

    return handler->flacWrite(decoder, frame, buffer);
}

static void flacErrorCallback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *clientData)
{
    RemoteTCPInputTCPHandler *handler = (RemoteTCPInputTCPHandler *) clientData;

    return handler->flacError(decoder, status);
}

FLAC__StreamDecoderReadStatus RemoteTCPInputTCPHandler::flacRead(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes)
{
    (void) decoder;

    qsizetype bytesRequested = *bytes;
    qsizetype bytesRead = std::min(bytesRequested, (qsizetype) m_compressedData.size());

    memcpy(buffer, m_compressedData.constData(), bytesRead);
    m_compressedData.remove(0, bytesRead);

    if (bytesRead == 0)
    {
        qDebug() << "RemoteTCPInputTCPHandler::flacRead: Decoder will hang if we can't return data";
        abort();
    }

    *bytes = (size_t) bytesRead;
    return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
}

FIFO::FIFO(qsizetype elements)
{
    m_data.resize(elements);
    clear();
}

qsizetype FIFO::write(quint8 *data, qsizetype elements)
{
    qsizetype writeCount = std::min(elements, m_data.size() - m_fill);
    qsizetype remaining = m_data.size() - m_writePtr;
    qsizetype len2 = writeCount - remaining;

    //qDebug() << "write" << write << remaining << len2;

    if (len2 < 0)
    {
        std::memcpy(&m_data.data()[m_writePtr], data, writeCount);
        m_writePtr += writeCount;
    }
    else if (len2 == 0)
    {
        std::memcpy(&m_data.data()[m_writePtr], data, writeCount);
        m_writePtr = 0;
    }
    else
    {
        std::memcpy(&m_data.data()[m_writePtr], data, remaining);
        std::memcpy(&m_data.data()[0], &data[remaining], len2);
        m_writePtr = len2;
    }

    m_fill += writeCount;

    return writeCount;
}

qsizetype FIFO::read(quint8 *data, qsizetype elements)
{
    qsizetype readCount = std::min(elements, m_fill);
    qsizetype remaining = m_data.size() - m_readPtr;
    qsizetype len2 = readCount - remaining;

   // qDebug() << "read" << read << remaining << len2;

    if (len2 < 0)
    {
        std::memcpy(data, &m_data.data()[m_readPtr], readCount);
        m_readPtr += readCount;
    }
    else if (len2 == 0)
    {
        std::memcpy(data, &m_data.data()[m_readPtr], readCount);
        m_readPtr = 0;
    }
    else
    {
        std::memcpy(&data[0], &m_data.data()[m_readPtr], remaining);
        std::memcpy(&data[remaining], &m_data.data()[0], len2);
        m_readPtr = len2;
    }

    m_fill -= readCount;

    return readCount;
}

qsizetype FIFO::readPtr(quint8 **data, qsizetype elements)
{
    *data = (quint8 *) &m_data.data()[m_readPtr];

    return std::min(elements, m_data.size() - m_readPtr);
}

void FIFO::read(qsizetype elements)
{
    m_readPtr = (m_readPtr + elements) % m_data.size();
    m_fill -= elements;
    if (m_fill < 0)
    {
        qDebug() << "FIFO::read: Underrun";
        m_fill = 0;
    }
}

void FIFO::resize(qsizetype elements)
{
    m_data.resize(elements);
    m_data.squeeze();
}

void FIFO::clear()
{
    m_writePtr = 0;
    m_readPtr = 0;
    m_fill = 0;
}

void RemoteTCPInputTCPHandler::calcPower(const Sample *iq, int nbSamples)
{
    for (int i = 0; i < nbSamples; i++)
    {
        Real re = iq[i].real();// SDR_RX_SCALED;
        Real im = iq[i].imag();// SDR_RX_SCALED;

        Real magsq = (re*re + im*im) / (SDR_RX_SCALED*SDR_RX_SCALED);
        m_movingAverage(magsq);
        m_magsq = m_movingAverage.asDouble();
        m_magsqSum += magsq;
        m_magsqPeak = std::max<double>(magsq, m_magsqPeak);
        m_magsqCount++;
    }
}

FLAC__StreamDecoderWriteStatus RemoteTCPInputTCPHandler::flacWrite(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[])
{
    (void) decoder;

    m_uncompressedFrames++;

    int nbSamples = frame->header.blocksize;
    if (nbSamples > (int) m_converterBufferNbSamples)
    {
        if (m_converterBuffer) {
            delete[] m_converterBuffer;
        }
        m_converterBuffer = new int32_t[nbSamples*2];
    }

    // Convert and interleave samples and output to FIFO
    if ((frame->header.bits_per_sample == 8) && (SDR_RX_SAMP_SZ == 24) && (frame->header.channels == 2))
    {
        qint32 *out = (qint32 *)m_converterBuffer;
        const qint32 *inI = buffer[0];
        const qint32 *inQ = buffer[1];

        for (int i = 0; i < nbSamples; i++)
        {
            *out++ = *inI++ << 16;
            *out++ = *inQ++ << 16;
        }
        m_uncompressedData.write(reinterpret_cast<quint8*>(m_converterBuffer), nbSamples*sizeof(Sample));
    }
    else if ((frame->header.bits_per_sample == 16) && (SDR_RX_SAMP_SZ == 24) && (frame->header.channels == 2))
    {
        qint32 *out = (qint32 *)m_converterBuffer;
        const qint32 *inI = buffer[0];
        const qint32 *inQ = buffer[1];

        for (int i = 0; i < nbSamples; i++)
        {
            *out++ = *inI++ << 8;
            *out++ = *inQ++ << 8;
        }
        m_uncompressedData.write(reinterpret_cast<quint8*>(m_converterBuffer), nbSamples*sizeof(Sample));
    }
    else if ((frame->header.bits_per_sample == 24) && (SDR_RX_SAMP_SZ == 24) && (frame->header.channels == 2))
    {
        qint32 *out = (qint32 *)m_converterBuffer;
        const qint32 *inI = buffer[0];
        const qint32 *inQ = buffer[1];

        for (int i = 0; i < nbSamples; i++)
        {
            *out++ = *inI++;
            *out++ = *inQ++;
        }
        m_uncompressedData.write(reinterpret_cast<quint8*>(m_converterBuffer), nbSamples*sizeof(Sample));
    }
    else if ((frame->header.bits_per_sample == 32) && (SDR_RX_SAMP_SZ == 24) && (frame->header.channels == 2))
    {
        qint32 *out = (qint32 *)m_converterBuffer;
        const qint32 *inI = buffer[0];
        const qint32 *inQ = buffer[1];

        for (int i = 0; i < nbSamples; i++)
        {
            *out++ = *inI++;
            *out++ = *inQ++;
        }
        m_uncompressedData.write(reinterpret_cast<quint8*>(m_converterBuffer), nbSamples*sizeof(Sample));
    }
    else if ((frame->header.bits_per_sample == 8) && (SDR_RX_SAMP_SZ == 16) && (frame->header.channels == 2))
    {
        qint16 *out = (qint16 *)m_converterBuffer;
        const qint32 *inI = buffer[0];
        const qint32 *inQ = buffer[1];

        for (int i = 0; i < nbSamples; i++)
        {
            *out++ = *inI++ << 8;
            *out++ = *inQ++ << 8;
        }
        m_uncompressedData.write(reinterpret_cast<quint8*>(m_converterBuffer), nbSamples*sizeof(Sample));
    }
    else if ((frame->header.bits_per_sample == 16) && (SDR_RX_SAMP_SZ == 16) && (frame->header.channels == 2))
    {
        qint16 *out = (qint16 *)m_converterBuffer;
        const qint32 *inI = buffer[0];
        const qint32 *inQ = buffer[1];

        for (int i = 0; i < nbSamples; i++)
        {
            *out++ = *inI++;
            *out++ = *inQ++;
        }
        m_uncompressedData.write(reinterpret_cast<quint8*>(m_converterBuffer), nbSamples*sizeof(Sample));
    }
    else if ((frame->header.bits_per_sample == 24) && (SDR_RX_SAMP_SZ == 16) && (frame->header.channels == 2))
    {
        qint16 *out = (qint16 *)m_converterBuffer;
        const qint32 *inI = buffer[0];
        const qint32 *inQ = buffer[1];

        for (int i = 0; i < nbSamples; i++)
        {
            *out++ = *inI++ >> 8;
            *out++ = *inQ++ >> 8;
        }
        m_uncompressedData.write(reinterpret_cast<quint8*>(m_converterBuffer), nbSamples*sizeof(Sample));
    }
    else if ((frame->header.bits_per_sample == 32) && (SDR_RX_SAMP_SZ == 16) && (frame->header.channels == 2))
    {
        qint16 *out = (qint16 *)m_converterBuffer;
        const qint32 *inI = buffer[0];
        const qint32 *inQ = buffer[1];

        for (int i = 0; i < nbSamples; i++)
        {
            *out++ = *inI++ >> 8;
            *out++ = *inQ++ >> 8;
        }
        m_uncompressedData.write(reinterpret_cast<quint8*>(m_converterBuffer), nbSamples*sizeof(Sample));
    }
    else
    {
        qDebug() << "RemoteTCPInputTCPHandler::flacWrite: Unsupported format";
    }

    return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}


// Convert from zlib uncompressed network format to Samples, to uncompressed data FIFO
void RemoteTCPInputTCPHandler::processDecompressedZlibData(const char *inBuf, int nbSamples)
{
    // Ensure conversion buffer is large enough - FIXME: Don't use this buffer - just write in to FIFO
    if (nbSamples > (int) m_converterBufferNbSamples)
    {
        if (m_converterBuffer) {
            delete[] m_converterBuffer;
        }
        m_converterBuffer = new int32_t[nbSamples*2];
    }

    // Convert from network format to Sample
    if ((m_settings.m_sampleBits == 8) && (SDR_RX_SAMP_SZ == 16))
    {
        const quint8 *in = (const quint8 *) inBuf;
        qint16 *out = (qint16 *) m_converterBuffer;

        for (int is = 0; is < nbSamples*2; is++) {
            out[is] = (((qint16)in[is]) - 128) << 8;
        }
    }
    else if ((m_settings.m_sampleBits == 8) && (SDR_RX_SAMP_SZ == 24))
    {
        const quint8 *in = (const quint8 *) inBuf;
        qint32 *out = (qint32 *) m_converterBuffer;

        for (int is = 0; is < nbSamples*2; is++) {
            out[is] = (((qint32)in[is]) - 128) << 16;
        }
    }
    else if ((m_settings.m_sampleBits == 16) && (SDR_RX_SAMP_SZ == 16))
    {
        const qint16 *in = (const qint16 *) inBuf;
        qint32 *out = (qint32 *) m_converterBuffer;

        for (int is = 0; is < nbSamples*2; is++) {
            out[is] = in[is];
        }
    }
    else if ((m_settings.m_sampleBits == 16) && (SDR_RX_SAMP_SZ == 24))
    {
        const qint16 *in = (const qint16 *) inBuf;
        qint32 *out = (qint32 *) m_converterBuffer;

        for (int is = 0; is < nbSamples*2; is++) {
            out[is] = in[is] << 8;
        }
    }
    else if ((m_settings.m_sampleBits == 24) && (SDR_RX_SAMP_SZ == 24))
    {
        const quint8 *in = (const quint8 *) inBuf;
        qint32 *out = (qint32 *) m_converterBuffer;

        for (int is = 0; is < nbSamples*2; is++) {
            out[is] = (((in[3*is+2] << 16) | (in[3*is+1] << 8) | in[3*is]) << 8) >> 8;
        }
    }
    else if ((m_settings.m_sampleBits == 24) && (SDR_RX_SAMP_SZ == 16))
    {
        const quint8 *in = (const quint8 *) inBuf;
        qint16 *out = (qint16 *) m_converterBuffer;

        for (int is = 0; is < nbSamples*2; is++) {
            out[is] = (in[3*is+2] << 8) | in[3*is+1];
        }
    }
    else if ((m_settings.m_sampleBits == 32) && (SDR_RX_SAMP_SZ == 16))
    {
        const qint32 *in = (const qint32 *) inBuf;
        qint16 *out = (qint16 *) m_converterBuffer;

        for (int is = 0; is < nbSamples*2; is++) {
            out[is] = in[is] >> 8;
        }
    }
    else if ((m_settings.m_sampleBits == 32) && (SDR_RX_SAMP_SZ == 24))
    {
        const qint32 *in = (const qint32 *) inBuf;
        qint32 *out = (qint32 *) m_converterBuffer;

        for (int is = 0; is < nbSamples*2; is++) {
            out[is] = in[is];
        }
    }
    else // invalid size
    {
        qWarning("RemoteTCPInputTCPHandler::convert: unexpected sample size in stream: %d bits", (int) m_settings.m_sampleBits);
    }

    m_uncompressedData.write(reinterpret_cast<quint8*>(m_converterBuffer), nbSamples*sizeof(Sample));
}

void RemoteTCPInputTCPHandler::flacError(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status)
{
    (void) decoder;

    qDebug() << "RemoteTCPInputTCPHandler::flacError: Error:" << status;
}

void RemoteTCPInputTCPHandler::connected()
{
    QMutexLocker mutexLocker(&m_mutex);
    qDebug() << "RemoteTCPInputTCPHandler::connected";
    if (m_messageQueueToGUI)
    {
        MsgReportConnection *msg = MsgReportConnection::create(true);
        m_messageQueueToGUI->push(msg);
    }
    m_spyServer = m_settings.m_protocol == "Spy Server";
    m_state = HEADER;
    m_sdra = false;
    m_remoteControl = true;
    m_iqOnly = true;
    if (m_spyServer) {
        spyServerConnect();
    }
    // Start calls to processData
    m_timer.start();
}

void RemoteTCPInputTCPHandler::reconnect()
{
    QMutexLocker mutexLocker(&m_mutex);
    if (!m_dataSocket) {
        connectToHost(m_settings.m_dataAddress, m_settings.m_dataPort, m_settings.m_protocol);
    }
}

void RemoteTCPInputTCPHandler::disconnected()
{
    QMutexLocker mutexLocker(&m_mutex);
    qDebug() << "RemoteTCPInputTCPHandler::disconnected";
    cleanup();
    if (m_messageQueueToGUI)
    {
        MsgReportConnection *msg = MsgReportConnection::create(false);
        m_messageQueueToGUI->push(msg);
    }
    if (!m_blacklisted)
    {
        // Try to reconnect immediately - it may just be server settings changed
        m_reconnectTimer.start(1);
    }
    else
    {
        // Stop device so we don't try to reconnect
        RemoteTCPInput::MsgStartStop *msg = RemoteTCPInput::MsgStartStop::create(false);
        m_messageQueueToInput->push(msg);
    }
}

void RemoteTCPInputTCPHandler::errorOccurred(QAbstractSocket::SocketError socketError)
{
    QMutexLocker mutexLocker(&m_mutex);
    qDebug() << "RemoteTCPInputTCPHandler::errorOccurred: " << socketError;

    // For RemoteHostClosedError, disconnected() will be called afterwards, so don't try to reconnect here
    // We try to reconnect here, for errors such as ConnectionRefusedError
    if (socketError != QAbstractSocket::RemoteHostClosedError)
    {
        cleanup();
        if (m_messageQueueToGUI)
        {
            MsgReportConnection *msg = MsgReportConnection::create(false);
            m_messageQueueToGUI->push(msg);
        }
        // Try to reconnect
        m_reconnectTimer.start(500);
    }
}

#ifndef QT_NO_OPENSSL
void RemoteTCPInputTCPHandler::sslErrors(const QList<QSslError> &errors)
{
    qDebug() << "RemoteTCPInputTCPHandler::sslErrors: " << errors;
    m_webSocket->ignoreSslErrors(); // FIXME: Add a setting whether to do this?
}
#endif

void RemoteTCPInputTCPHandler::dataReadyRead()
{
    QMutexLocker mutexLocker(&m_mutex);

    if (!m_readMetaData && !m_spyServer) {
        processMetaData();
    } else if (!m_readMetaData && m_spyServer) {
        processSpyServerMetaData();
    }

    if (m_readMetaData && !m_iqOnly) {
        processCommands();
    }
}

void RemoteTCPInputTCPHandler::processMetaData()
{
    quint8 metaData[RemoteTCPProtocol::m_sdraMetaDataSize];
    if (m_dataSocket->bytesAvailable() >= (qint64)sizeof(metaData))
    {
        qint64 bytesRead = m_dataSocket->read((char *)&metaData[0], 4);
        if (bytesRead == 4)
        {
            // Read first 4 bytes which indicate which protocol is in use
            // RTL0 or SDRA
            char protochars[5];
            memcpy(protochars, metaData, 4);
            protochars[4] = '\0';
            QString protocol(protochars);

            if (protocol == "RTL0")
            {
                m_sdra = false;
                m_spyServer = false;
                bytesRead = m_dataSocket->read((char *)&metaData[4], RemoteTCPProtocol::m_rtl0MetaDataSize-4);

                m_device = (RemoteTCPProtocol::Device)RemoteTCPProtocol::extractUInt32(&metaData[4]);
                if (m_messageQueueToGUI) {
                    m_messageQueueToGUI->push(MsgReportRemoteDevice::create(m_device, protocol, false, true));
                }
                if (m_settings.m_sampleBits != 8)
                {
                    RemoteTCPInputSettings& settings = m_settings;
                    settings.m_sampleBits = 8;
                    sendSettings(settings, {"sampleBits"});
                }
            }
            else if (protocol == "SDRA")
            {
                m_sdra = true;
                m_spyServer = false;
                bytesRead = m_dataSocket->read((char *)&metaData[4], RemoteTCPProtocol::m_sdraMetaDataSize-4);

                m_device = (RemoteTCPProtocol::Device)RemoteTCPProtocol::extractUInt32(&metaData[4]);
                quint32 protocolRevision = RemoteTCPProtocol::extractUInt32(&metaData[60]);
                quint32 flags = RemoteTCPProtocol::extractUInt32(&metaData[20]);
                if (protocolRevision >= 1)
                {
                    m_iqOnly = !(bool) ((flags >> 7) & 1);
                    m_remoteControl = (bool) ((flags >> 6) & 1);
                }
                else
                {
                    m_iqOnly = true;
                    m_remoteControl = true;
                }
                if (m_messageQueueToGUI) {
                    m_messageQueueToGUI->push(MsgReportRemoteDevice::create(m_device, protocol, m_iqOnly, m_remoteControl));
                }
                if (!m_settings.m_overrideRemoteSettings || !m_remoteControl)
                {
                    // Update local settings to match remote
                    RemoteTCPInputSettings& settings = m_settings;
                    QList<QString> settingsKeys;
                    settings.m_centerFrequency = RemoteTCPProtocol::extractUInt64(&metaData[8]);
                    settingsKeys.append("centerFrequency");
                    settings.m_loPpmCorrection = RemoteTCPProtocol::extractUInt32(&metaData[16]);
                    settingsKeys.append("loPpmCorrection");
                    settings.m_biasTee = flags & 1;
                    settingsKeys.append("biasTee");
                    settings.m_directSampling = (flags >> 1) & 1;
                    settingsKeys.append("directSampling");
                    settings.m_agc = (flags >> 2) & 1;
                    settingsKeys.append("agc");
                    settings.m_dcBlock = (flags >> 3) & 1;
                    settingsKeys.append("dcBlock");
                    settings.m_iqCorrection = (flags >> 4) & 1;
                    settingsKeys.append("iqCorrection");
                    settings.m_devSampleRate = RemoteTCPProtocol::extractUInt32(&metaData[24]);
                    settingsKeys.append("devSampleRate");
                    settings.m_log2Decim = RemoteTCPProtocol::extractUInt32(&metaData[28]);
                    settingsKeys.append("log2Decim");
                    settings.m_gain[0] = RemoteTCPProtocol::extractInt16(&metaData[32]);
                    settings.m_gain[1] = RemoteTCPProtocol::extractInt16(&metaData[34]);
                    settings.m_gain[2] = RemoteTCPProtocol::extractInt16(&metaData[36]);
                    settingsKeys.append("gain[0]");
                    settingsKeys.append("gain[1]");
                    settingsKeys.append("gain[2]");
                    settings.m_rfBW = RemoteTCPProtocol::extractUInt32(&metaData[40]);
                    settingsKeys.append("rfBW");
                    settings.m_inputFrequencyOffset = RemoteTCPProtocol::extractUInt32(&metaData[44]);
                    settingsKeys.append("inputFrequencyOffset");
                    settings.m_channelGain = RemoteTCPProtocol::extractUInt32(&metaData[48]);
                    settingsKeys.append("channelGain");
                    settings.m_channelSampleRate = RemoteTCPProtocol::extractUInt32(&metaData[52]);
                    settingsKeys.append("channelSampleRate");
                    settings.m_sampleBits = RemoteTCPProtocol::extractUInt32(&metaData[56]);
                    settingsKeys.append("sampleBits");
                    if (settings.m_channelSampleRate != (settings.m_devSampleRate >> settings.m_log2Decim))
                    {
                        settings.m_channelDecimation = true;
                        settingsKeys.append("channelDecimation");
                    }
                    if (protocolRevision >= 1)
                    {
                        settings.m_squelchEnabled = (flags >> 5) & 1;
                        settingsKeys.append("squelchEnabled");
                        settings.m_squelch =  RemoteTCPProtocol::extractFloat(&metaData[64]);
                        settingsKeys.append("squelch");
                        settings.m_squelchGate =  RemoteTCPProtocol::extractFloat(&metaData[68]);
                        settingsKeys.append("squelchGate");
                    }
                    sendSettings(settings, settingsKeys);
                }

                if (!m_iqOnly)
                {
                    qDebug() << "RemoteTCPInputTCPHandler: Compression enabled";
                    // Create FLAC decoder for IQ decompression
                    m_decoder = FLAC__stream_decoder_new();
                    m_remainingSamples = 0;
                    m_compressedFrames = 0;
                    m_uncompressedFrames = 0;

                    int bytesPerSecond = m_settings.m_channelSampleRate * 2 * sizeof(Sample);
                    int fifoSize = 2 * m_settings.m_preFill * bytesPerSecond;
                    m_uncompressedData.resize(fifoSize);
                    m_uncompressedData.clear();

                    if (m_decoder)
                    {
                        FLAC__StreamDecoderInitStatus initStatus;
                        initStatus = FLAC__stream_decoder_init_stream(m_decoder, flacReadCallback, nullptr, nullptr, nullptr, nullptr, flacWriteCallback, nullptr, flacErrorCallback, this);
                        if (initStatus != FLAC__STREAM_DECODER_INIT_STATUS_OK)
                        {
                            qDebug() << "RemoteTCPInputTCPHandler: Failed to init FLAC decoder: " << initStatus;
                        }
                    }
                    else
                    {
                        qDebug() << "RemoteTCPInputTCPHandler: Failed to allocate FLAC decoder";
                    }
                }
                else
                {
                    qDebug() << "RemoteTCPInputTCPHandler: Compression disabled";
                }
            }
            else
            {
                qDebug() << "RemoteTCPInputTCPHandler::dataReadyRead: Unknown protocol: " << protocol;
                m_dataSocket->close();
            }
            if (m_settings.m_overrideRemoteSettings && m_remoteControl)
            {
                // Force settings to be sent to remote device (this needs to be after m_sdra is determined above)
                applySettings(m_settings, QList<QString>(), true);
            }
        }
        m_readMetaData = true;
    }
}

void RemoteTCPInputTCPHandler::processSpyServerMetaData()
{
    bool done = false;

    while (!done)
    {
        if (m_state == HEADER)
        {
            if (m_dataSocket->bytesAvailable() >= (qint64)sizeof(SpyServerProtocol::Header))
            {
                qint64 bytesRead = m_dataSocket->read((char *)&m_spyServerHeader, sizeof(SpyServerProtocol::Header));
                if (bytesRead == sizeof(SpyServerProtocol::Header)) {
                    m_state = DATA;
                } else {
                    qDebug() << "RemoteTCPInputTCPHandler::processSpyServerMetaData: Failed to read:" << bytesRead << "/" << sizeof(SpyServerProtocol::Header);
                }
            }
            else
            {
                done = true;
            }
        }
        else if (m_state == DATA)
        {
            if (m_dataSocket->bytesAvailable() >= m_spyServerHeader.m_size)
            {
                qint64 bytesRead = m_dataSocket->read(&m_tcpBuf[0], m_spyServerHeader.m_size);
                if (bytesRead == m_spyServerHeader.m_size)
                {
                    if (m_spyServerHeader.m_message == SpyServerProtocol::DeviceMessage)
                    {
                        processSpyServerDevice((SpyServerProtocol::Device *) &m_tcpBuf[0]);
                        m_state = HEADER;
                    }
                    else if (m_spyServerHeader.m_message == SpyServerProtocol::StateMessage)
                    {
                        // This call can result in applySettings() calling clearBuffer() then processSpyServerData()
                        processSpyServerState((SpyServerProtocol::State *) &m_tcpBuf[0], true);
                        spyServerSetStreamIQ();

                        m_state = HEADER;
                        m_readMetaData = true;
                        done = true;
                    }
                    else
                    {
                        qDebug() << "RemoteTCPInputTCPHandler::processSpyServerMetaData: Unexpected message type" << m_spyServerHeader.m_message;
                        m_state = HEADER;
                    }
                }
                else
                {
                    qDebug() << "RemoteTCPInputTCPHandler::processSpyServerMetaData: Failed to read:" << bytesRead << "/" << m_spyServerHeader.m_size;
                }
            }
            else
            {
                done = true;
            }
        }
    }
}

void RemoteTCPInputTCPHandler::processSpyServerDevice(const SpyServerProtocol::Device* ssDevice)
{
    qDebug() << "RemoteTCPInputTCPHandler::processSpyServerDevice:"
        << "device:" << ssDevice->m_device
        << "serial:" << ssDevice->m_serial
        << "sampleRate:" << ssDevice->m_sampleRate
        << "decimationStages:" << ssDevice->m_decimationStages
        << "maxGainIndex:" << ssDevice->m_maxGainIndex
        << "minFrequency:" << ssDevice->m_minFrequency
        << "maxFrequency:" << ssDevice->m_maxFrequency
        << "sampleBits:" << ssDevice->m_sampleBits
        << "minDecimation:" << ssDevice->m_minDecimation;

    switch (ssDevice->m_device)
    {
    case 1:
        m_device = RemoteTCPProtocol::AIRSPY;
        break;
    case 2:
        m_device = RemoteTCPProtocol::AIRSPY_HF;
        break;
    case 3:
        m_device = ssDevice->m_maxGainIndex == 14
            ? RemoteTCPProtocol::RTLSDR_E4000
            : RemoteTCPProtocol::RTLSDR_R820T;
        break;
    default:
        m_device = RemoteTCPProtocol::UNKNOWN;
        break;
    }
    if (m_messageQueueToGUI) {
        m_messageQueueToGUI->push(MsgReportRemoteDevice::create(m_device, "Spy Server", false, true, ssDevice->m_maxGainIndex));
    }

    RemoteTCPInputSettings& settings = m_settings;
    QList<QString> settingsKeys{};
    // We can't change sample rate, so always have to update local setting to match
    m_settings.m_devSampleRate = settings.m_devSampleRate = ssDevice->m_sampleRate;
    settingsKeys.append("devSampleRate");
    // Make sure decimation setting is at least the minimum
    if (!m_settings.m_overrideRemoteSettings || (settings.m_log2Decim < (int) ssDevice->m_minDecimation))
    {
        m_settings.m_log2Decim = settings.m_log2Decim = ssDevice->m_minDecimation;
        settingsKeys.append("log2Decim");
    }
    sendSettings(settings, settingsKeys);
}

void RemoteTCPInputTCPHandler::processSpyServerState(const SpyServerProtocol::State* ssState, bool initial)
{
    qDebug() << "RemoteTCPInputTCPHandler::processSpyServerState: "
        << "initial:" << initial
        << "controllable:" << ssState->m_controllable
        << "gain:" << ssState->m_gain
        << "deviceCenterFrequency:" << ssState->m_deviceCenterFrequency
        << "iqCenterFrequency:" << ssState->m_iqCenterFrequency;

    if (initial && ssState->m_controllable && m_settings.m_overrideRemoteSettings)
    {
        // Force client settings to be sent to server
        applySettings(m_settings, QList<QString>(), true);
    }
    else
    {
        // Update client settings with that from server
        RemoteTCPInputSettings& settings = m_settings;
        QList<QString> settingsKeys;

        if (m_settings.m_centerFrequency != ssState->m_iqCenterFrequency)
        {
            settings.m_centerFrequency = ssState->m_iqCenterFrequency;
            settingsKeys.append("centerFrequency");
        }
        if (m_settings.m_gain[0] != (qint32) ssState->m_gain)
        {
            settings.m_gain[0] = ssState->m_gain;
            settingsKeys.append("gain[0]");
        }
        if (settingsKeys.size() > 0)
        {
            sendSettings(settings, settingsKeys);
        }
    }
}

void RemoteTCPInputTCPHandler::processSpyServerData(int requiredBytes, bool clear)
{
    if (!m_readMetaData) {
        return;
    }

    bool done = false;

    while (!done)
    {
        if (m_state == HEADER)
        {
            if (m_dataSocket->bytesAvailable() >= (qint64) sizeof(SpyServerProtocol::Header))
            {
                qint64 bytesRead = m_dataSocket->read((char *) &m_spyServerHeader, sizeof(SpyServerProtocol::Header));
                if (bytesRead == sizeof(SpyServerProtocol::Header)) {
                    m_state = DATA;
                } else {
                    qDebug() << "RemoteTCPInputTCPHandler::processSpyServerData: Failed to read:" << bytesRead << "/" << sizeof(SpyServerProtocol::Header);
                }
            }
            else
            {
                done = true;
            }
        }
        else if (m_state == DATA)
        {
            int bytes;

            if ((m_spyServerHeader.m_message >= SpyServerProtocol::IQ8MMessage) && (m_spyServerHeader.m_message <= SpyServerProtocol::IQ32Message)) {
                bytes = std::min(requiredBytes, (int) m_spyServerHeader.m_size);
            } else {
                bytes = m_spyServerHeader.m_size;
            }

            if (m_dataSocket->bytesAvailable() >= bytes)
            {
                qint64 bytesRead = m_dataSocket->read(&m_tcpBuf[0], bytes);
                if (bytesRead == bytes)
                {
                    if ((m_spyServerHeader.m_message >= SpyServerProtocol::IQ8MMessage) && (m_spyServerHeader.m_message <= SpyServerProtocol::IQ32Message))
                    {
                        if (!clear)
                        {
                            const int bytesPerIQPair = 2 * m_settings.m_sampleBits / 8;
                            processUncompressedData(&m_tcpBuf[0], bytesRead / bytesPerIQPair);
                        }
                        m_spyServerHeader.m_size -= bytesRead;
                        requiredBytes -= bytesRead;
                        if (m_spyServerHeader.m_size == 0) {
                            m_state = HEADER;
                        }
                        if (requiredBytes <= 0) {
                            done = true;
                        }
                    }
                    else if (m_spyServerHeader.m_message == SpyServerProtocol::StateMessage)
                    {
                        processSpyServerState((SpyServerProtocol::State *) &m_tcpBuf[0], false);
                        m_state = HEADER;
                    }
                    else
                    {
                        qDebug() << "RemoteTCPInputTCPHandler::processSpyServerData: Skipping unsupported message";
                        m_state = HEADER;
                    }
                }
                else
                {
                    qDebug() << "RemoteTCPInputTCPHandler::processSpyServerData: Failed to read:" << bytesRead << "/" << bytes;
                }
            }
            else
            {
                done = true;
            }
        }
    }
}

void RemoteTCPInputTCPHandler::sendSettings(const RemoteTCPInputSettings& settings, const QStringList& settingsKeys)
{
    if (m_messageQueueToInput) {
        m_messageQueueToInput->push(RemoteTCPInput::MsgConfigureRemoteTCPInput::create(settings, settingsKeys));
    }
    if (m_messageQueueToGUI) {
        m_messageQueueToGUI->push(RemoteTCPInput::MsgConfigureRemoteTCPInput::create(settings, settingsKeys));
    }
}

void RemoteTCPInputTCPHandler::processCommands()
{
    bool done = false;

    while (!done)
    {
        if (m_state == HEADER)
        {
            if (m_dataSocket->bytesAvailable() >= 5)
            {
                quint8 buf[5];
                qint64 bytesRead = m_dataSocket->read((char *) buf, sizeof(buf));

                if (bytesRead == sizeof(buf))
                {
                    m_command = (RemoteTCPProtocol::Command) buf[0];

                    switch (m_command)
                    {
                    case RemoteTCPProtocol::setCenterFrequency:
                    {
                        quint32 centerFrequency = (quint32) RemoteTCPProtocol::extractUInt32(&buf[1]);
                        if (centerFrequency != m_settings.m_centerFrequency)
                        {
                            RemoteTCPInputSettings settings = m_settings;
                            settings.m_centerFrequency = centerFrequency;
                            sendSettings(settings, {"centerFrequency"});
                        }
                        break;
                    }
                    case RemoteTCPProtocol::setSampleRate:
                    {
                        int devSampleRate = RemoteTCPProtocol::extractInt32(&buf[1]);
                        if (devSampleRate != m_settings.m_devSampleRate)
                        {
                            RemoteTCPInputSettings settings = m_settings;
                            settings.m_devSampleRate = devSampleRate;
                            sendSettings(settings, {"devSampleRate"});
                        }
                        break;
                    }
                    case RemoteTCPProtocol::setTunerGainMode:
                    {
                        // Currently fixed as 1
                    }
                    case RemoteTCPProtocol::setTunerGain:
                    {
                        int gain = RemoteTCPProtocol::extractUInt32(&buf[1]);
                        if (gain != m_settings.m_gain[0])
                        {
                            RemoteTCPInputSettings settings = m_settings;
                            settings.m_gain[0] = gain;
                            sendSettings(settings, {"gain[0]"});
                        }
                        break;
                    }
                    case RemoteTCPProtocol::setFrequencyCorrection:
                    {
                        qint32 loPpmCorrection = (qint32) RemoteTCPProtocol::extractUInt32(&buf[1]);
                        if (loPpmCorrection != m_settings.m_loPpmCorrection)
                        {
                            RemoteTCPInputSettings settings = m_settings;
                            settings.m_loPpmCorrection = loPpmCorrection;
                            sendSettings(settings, {"loPpmCorrection"});
                        }
                        break;
                    }
                    case RemoteTCPProtocol::setTunerIFGain:
                    {
                        int v = RemoteTCPProtocol::extractUInt32(&buf[1]);
                        int gain = (int)(qint16)(v & 0xffff);
                        int stage = (v >> 16) & 0xffff;
                        if ((stage < RemoteTCPInputSettings::m_maxGains) && (gain != m_settings.m_gain[stage]))
                        {
                            RemoteTCPInputSettings settings = m_settings;
                            settings.m_gain[stage] = gain;
                            sendSettings(settings, {QString("gain[%1]").arg(stage)});
                        }
                        break;
                    }
                    case RemoteTCPProtocol::setAGCMode:
                    {
                        bool agc = (bool) RemoteTCPProtocol::extractUInt32(&buf[1]);
                        if (agc != m_settings.m_agc)
                        {
                            RemoteTCPInputSettings settings = m_settings;
                            settings.m_agc = agc;
                            sendSettings(settings, {"agc"});
                        }
                        break;
                    }
                    case RemoteTCPProtocol::setDirectSampling:
                    {
                        bool directSampling = (bool) RemoteTCPProtocol::extractUInt32(&buf[1]);
                        if (directSampling != m_settings.m_directSampling)
                        {
                            RemoteTCPInputSettings settings = m_settings;
                            settings.m_directSampling = directSampling;
                            sendSettings(settings, {"directSampling"});
                        }
                        break;
                    }
                    case RemoteTCPProtocol::setBiasTee:
                    {
                        bool biasTee = (bool) RemoteTCPProtocol::extractUInt32(&buf[1]);
                        if (biasTee != m_settings.m_biasTee)
                        {
                            RemoteTCPInputSettings settings = m_settings;
                            settings.m_biasTee = biasTee;
                            sendSettings(settings, {"biasTee"});
                        }
                        break;
                    }
                    case RemoteTCPProtocol::setTunerBandwidth:
                    {
                        int rfBW = RemoteTCPProtocol::extractInt32(&buf[1]);
                        if (rfBW != m_settings.m_rfBW)
                        {
                            RemoteTCPInputSettings settings = m_settings;
                            settings.m_rfBW = rfBW;
                            sendSettings(settings, {"rfBW"});
                        }
                        break;
                    }
                    case RemoteTCPProtocol::setDecimation:
                    {
                        int log2Decim = RemoteTCPProtocol::extractInt32(&buf[1]);
                        if (log2Decim != m_settings.m_log2Decim)
                        {
                            RemoteTCPInputSettings settings = m_settings;
                            settings.m_log2Decim = log2Decim;
                            sendSettings(settings, {"log2Decim"});
                        }
                        break;
                    }
                    case RemoteTCPProtocol::setDCOffsetRemoval:
                    {
                        bool dcBlock = (bool) RemoteTCPProtocol::extractUInt32(&buf[1]);
                        if (dcBlock != m_settings.m_dcBlock)
                        {
                            RemoteTCPInputSettings settings = m_settings;
                            settings.m_dcBlock = dcBlock;
                            sendSettings(settings, {"dcBlock"});
                        }
                        break;
                    }
                    case RemoteTCPProtocol::setIQCorrection:
                    {
                        bool iqCorrection = (bool) RemoteTCPProtocol::extractUInt32(&buf[1]);
                        if (iqCorrection != m_settings.m_iqCorrection)
                        {
                            RemoteTCPInputSettings settings = m_settings;
                            settings.m_iqCorrection = iqCorrection;
                            sendSettings(settings, {"iqCorrection"});
                        }
                        break;
                    }
                    case RemoteTCPProtocol::setChannelSampleRate:
                    {
                        qint32 channelSampleRate = RemoteTCPProtocol::extractInt32(&buf[1]);
                        if (channelSampleRate != m_settings.m_channelSampleRate)
                        {
                            RemoteTCPInputSettings settings = m_settings;
                            settings.m_channelSampleRate = channelSampleRate;
                            sendSettings(settings, {"channelSampleRate"});
                        }
                        break;
                    }
                    case RemoteTCPProtocol::setChannelFreqOffset:
                    {
                        qint32 inputFrequencyOffset = (qint32) RemoteTCPProtocol::extractUInt32(&buf[1]);
                        if (inputFrequencyOffset != m_settings.m_inputFrequencyOffset)
                        {
                            RemoteTCPInputSettings settings = m_settings;
                            settings.m_inputFrequencyOffset = inputFrequencyOffset;
                            sendSettings(settings, {"inputFrequencyOffset"});
                        }
                        break;
                    }
                    case RemoteTCPProtocol::setChannelGain:
                    {
                        qint32 channelGain = RemoteTCPProtocol::extractInt32(&buf[1]);
                        if (channelGain != m_settings.m_channelGain)
                        {
                            RemoteTCPInputSettings settings = m_settings;
                            settings.m_channelGain = channelGain;
                            sendSettings(settings, {"channelGain"});
                        }
                        break;
                    }
                    case RemoteTCPProtocol::setSampleBitDepth:
                    {
                        qint32 sampleBits = RemoteTCPProtocol::extractInt32(&buf[1]);
                        if (sampleBits != m_settings.m_sampleBits)
                        {
                            RemoteTCPInputSettings settings = m_settings;
                            settings.m_sampleBits = sampleBits;
                            sendSettings(settings, {"sampleBits"});
                        }
                        break;
                    }
                    case RemoteTCPProtocol::setIQSquelchEnabled:
                    {
                        bool squelchEnabled = (bool) RemoteTCPProtocol::extractUInt32(&buf[1]);
                        if (squelchEnabled != m_settings.m_squelchEnabled)
                        {
                            RemoteTCPInputSettings settings = m_settings;
                            settings.m_squelchEnabled = squelchEnabled;
                            sendSettings(settings, {"squelchEnabled"});
                        }
                        break;
                    }
                    case RemoteTCPProtocol::setIQSquelch:
                    {
                        float squelch = RemoteTCPProtocol::extractFloat(&buf[1]);
                        if (squelch != m_settings.m_squelch)
                        {
                            RemoteTCPInputSettings settings = m_settings;
                            settings.m_squelch = squelch;
                            sendSettings(settings, {"squelch"});
                        }
                        break;
                    }
                    case RemoteTCPProtocol::setIQSquelchGate:
                    {
                        float squelchGate = RemoteTCPProtocol::extractFloat(&buf[1]);
                        if (squelchGate != m_settings.m_squelchGate)
                        {
                            RemoteTCPInputSettings settings = m_settings;
                            settings.m_squelchGate = squelchGate;
                            sendSettings(settings, {"squelchGate"});
                        }
                        break;
                    }
                    default:
                        m_commandLength = RemoteTCPProtocol::extractUInt32(&buf[1]);
                        m_state = DATA;
                    }
                }
                else
                {
                    qDebug() << "RemoteTCPInputTCPHandler::processCommands: Failed to read:" << bytesRead << "/" << sizeof(buf);
                }
            }
            else
            {
                done = true;
            }
        }
        if (m_state == DATA)
        {
            if (m_dataSocket->bytesAvailable() >= m_commandLength)
            {
                try
                {
                    switch (m_command)
                    {


                    case RemoteTCPProtocol::dataIQ:
                    {
                        break;
                    }

                    case RemoteTCPProtocol::dataIQFLAC:
                    {
                        qsizetype s = m_compressedData.size();
                        m_compressedData.resize(s + m_commandLength);
                        qint64 bytesRead = m_dataSocket->read(&m_compressedData.data()[s], m_commandLength);
                        m_compressedFrames++;
                        if (bytesRead == m_commandLength)
                        {
                            // FLAC encoder writes out 4 (fLaC), 38 (STREAMINFO), 51 (?) byte headers, that are transmitted as one command block,
                            // then each command block will be a complete audio block (first two bytes will be 0xfff8)
                            // FLAC__stream_decoder_process_single will keep calling the read callback until it's decoded one metadata or audio block
                            // so we need to make sure there's enough data that it will be able to return

                            bool decodeDone = false;

                            while (!decodeDone)
                            {
                                //qDebug() << "m_compressedFrames" << m_compressedFrames << "m_uncompressedFrames" << m_uncompressedFrames;
                                if (m_compressedFrames - 1 > m_uncompressedFrames)
                                {
                                    if (!FLAC__stream_decoder_process_single(m_decoder))
                                    {
                                        qDebug() << "FLAC decode failed";
                                        decodeDone = true;
                                    }
                                }
                                else
                                {
                                    decodeDone = true;
                                }
                            }
                        }
                        else
                        {
                            qDebug() << "RemoteTCPInputTCPHandler::processCommands: Failed to read:" << bytesRead << "/" << m_commandLength;
                        }
                        break;
                    }

                    case RemoteTCPProtocol::dataIQzlib:
                    {
                        if (m_commandLength > (quint32) m_compressedData.size()) {
                            m_compressedData.resize(m_commandLength);
                        }
                        qint64 bytesRead = m_dataSocket->read(m_compressedData.data(), m_commandLength);
                        if (bytesRead == m_commandLength)
                        {
                            // Decompressing using zlib
                            m_zStream.next_in = (Bytef *) m_compressedData.data();
                            m_zStream.avail_in = m_commandLength;
                            m_zStream.next_out = (Bytef *) m_zOutBuf.data();
                            m_zStream.avail_out = m_zOutBuf.size();

                            int ret = inflate(&m_zStream, Z_NO_FLUSH);

                            if (ret == Z_STREAM_END) {
                                inflateReset(&m_zStream);
                                // Convert and write to uncompressed data FIFO
                                int uncompressedBytes = m_zOutBuf.size() - m_zStream.avail_out;
                                int nbSamples = uncompressedBytes / 2 / (m_settings.m_sampleBits / 8);
                                processDecompressedZlibData(m_zOutBuf.data(), nbSamples);
                            } else if (ret == Z_NEED_DICT) {
                                qDebug() << "zlib needs dict to inflate";
                            } else if (ret == Z_DATA_ERROR) {
                                qDebug() << "zlib data error";
                            } else if (ret == Z_MEM_ERROR) {
                                qDebug() << "zlib mem error";
                            } else {
                                qDebug() << "Unexpected zlib return value" << ret;
                            }
                        }
                        else
                        {
                            qDebug() << "RemoteTCPInputTCPHandler::processCommands: Failed to read:" << bytesRead << "/" << m_commandLength;
                        }
                        break;
                    }

                    case RemoteTCPProtocol::dataPosition:
                    {
                        char pos[4+4+4];
                        qint64 bytesRead = m_dataSocket->read(pos, m_commandLength);
                        if (bytesRead == m_commandLength)
                        {
                            float latitude = RemoteTCPProtocol::extractFloat((const quint8 *) &pos[0]);
                            float longitude = RemoteTCPProtocol::extractFloat((const quint8 *) &pos[4]);
                            float altitude = RemoteTCPProtocol::extractFloat((const quint8 *) &pos[8]);
                            //qDebug() << "RemoteTCPInputTCPHandler::processCommands: Position " << latitude << longitude << altitude;
                            if (m_messageQueueToInput) {
                                m_messageQueueToInput->push(RemoteTCPInput::MsgReportPosition::create(latitude, longitude, altitude));
                            }
                        }
                        else
                        {
                            qDebug() << "RemoteTCPInputTCPHandler::processCommands: Failed to read:" << bytesRead << "/" << m_commandLength;
                        }
                        break;
                    }

                    case RemoteTCPProtocol::dataDirection:
                    {
                        char dir[4+4+4];
                        qint64 bytesRead = m_dataSocket->read(dir, m_commandLength);
                        if (bytesRead == m_commandLength)
                        {
                            float isotropic = RemoteTCPProtocol::extractUInt32((const quint8 *) &dir[0]);
                            float azimuth = RemoteTCPProtocol::extractFloat((const quint8 *) &dir[4]);
                            float elevation = RemoteTCPProtocol::extractFloat((const quint8 *) &dir[8]);
                            //qDebug() << "RemoteTCPInputTCPHandler::processCommands: Direction " << isotropic << azimuth << elevation;
                            if (m_messageQueueToInput) {
                                m_messageQueueToInput->push(RemoteTCPInput::MsgReportDirection::create(isotropic, azimuth, elevation));
                            }
                        }
                        else
                        {
                            qDebug() << "RemoteTCPInputTCPHandler::processCommands: Failed to read:" << bytesRead << "/" << m_commandLength;
                        }
                        break;
                    }

                    case RemoteTCPProtocol::sendMessage:
                    {
                        char *buf = new char[m_commandLength];
                        qint64 bytesRead = m_dataSocket->read(buf, m_commandLength);

                        if (bytesRead == m_commandLength)
                        {
                            bool broadcast = (bool) buf[0];
                            int i;
                            for (i = 1; i < (int) m_commandLength; i++)
                            {
                                if (buf[i] == '\0') {
                                    break;
                                }
                            }
                            QString callsign = QString::fromUtf8(&buf[1]);
                            QString text = QString::fromUtf8(&buf[i+1]);

                            qDebug() << "RemoteTCPInputTCPHandler::processCommands: Message " << m_dataSocket->peerAddress() << m_dataSocket->peerPort() << callsign << broadcast << text;
                            if (m_messageQueueToGUI) {
                                m_messageQueueToGUI->push(RemoteTCPInput::MsgSendMessage::create(callsign, text, broadcast));
                            }
                        }
                        else
                        {
                            qDebug() << "RemoteTCPInputTCPHandler::processCommands: Failed to read:" << bytesRead << "/" << m_commandLength;
                        }
                        delete[] buf;
                        break;
                    }

                    case RemoteTCPProtocol::sendBlacklistedMessage:
                    {
                        qDebug() << "RemoteTCPInputTCPHandler::processCommands: Disconnecting as blacklisted";
                        if (m_messageQueueToGUI) {
                            m_messageQueueToGUI->push(RemoteTCPInput::MsgSendMessage::create("", "Disconnecting as IP address is blacklisted", false));
                        }
                        m_blacklisted = true;
                        qDebug() << "set m_blacklisted" << m_blacklisted;
                        break;
                    }

                    default:
                    {
                        qDebug() << "RemoteTCPInputTCPHandler::processCommands: Unknown command" << m_command;
                        char *buf = new char[m_commandLength];
                        m_dataSocket->read(buf, m_commandLength);
                        delete[] buf;
                        break;
                    }
                    }
                }
                catch(std::bad_alloc&)
                {
                    qDebug() << "RemoteTCPInputTCPHandler::processCommands: Failed to allocate memory";
                    done = true;
                }
                m_state = HEADER;
            }
            else
            {
                done = true;
            }
        }
    }
}

// QTimer::timeout isn't guaranteed to be called on every timeout, so we need to look at the system clock
void RemoteTCPInputTCPHandler::processData()
{
    QMutexLocker mutexLocker(&m_mutex);

    if (m_dataSocket && m_dataSocket->isConnected())
    {
        int sampleRate = m_settings.m_channelSampleRate;
        int bytesPerIQPair = m_iqOnly ?  (2 * m_settings.m_sampleBits / 8) : (2 * sizeof(Sample));
        int bytesPerSecond = sampleRate * bytesPerIQPair;

        qint64 bytesAvailable = m_iqOnly ? m_dataSocket->bytesAvailable() : m_uncompressedData.fill();

        if ((bytesAvailable < (0.1f * m_settings.m_preFill * bytesPerSecond)) && !m_fillBuffer)
        {
            qDebug() << "RemoteTCPInputTCPHandler::processData: Buffering - bytesAvailable:" << bytesAvailable;
            m_fillBuffer = true;
        }

        // Report buffer usage
        // QTcpSockets buffer size should be unlimited - we pretend here it's twice as big as the point we start reading from it
        if (m_messageQueueToGUI)
        {
            qint64 size = std::max(bytesAvailable, (qint64)(m_settings.m_preFill * bytesPerSecond));
            RemoteTCPInput::MsgReportTCPBuffer *report = RemoteTCPInput::MsgReportTCPBuffer::create(
                                                            bytesAvailable, size, bytesAvailable / (float)bytesPerSecond,
                                                            m_sampleFifo->fill(),  m_sampleFifo->size(), m_sampleFifo->fill() / (float)bytesPerSecond
                                                            );
            m_messageQueueToGUI->push(report);
        }

        float factor = 0.0f;
        // Prime buffer, before we start reading
        if (m_fillBuffer)
        {
            if (bytesAvailable >= m_settings.m_preFill * bytesPerSecond)
            {
                qDebug() << "RemoteTCPInputTCPHandler::processData: Buffer primed - bytesAvailable:" << bytesAvailable;
                m_fillBuffer = false;
                m_prevDateTime = QDateTime::currentDateTime();
                factor = 1.0f / 4.0f; // If this is too high, samples can just be dropped downstream
            }
        }
        else
        {
            QDateTime currentDateTime = QDateTime::currentDateTime();
            factor = m_prevDateTime.msecsTo(currentDateTime) / 1000.0f; // FIXME: Close skew.. Actual sample rate may differ
            m_prevDateTime = currentDateTime;
        }

        unsigned int remaining = m_sampleFifo->size() - m_sampleFifo->fill();
        unsigned int maxRequired = (unsigned int) (factor * sampleRate);
        int requiredSamples = (int)std::min(maxRequired, remaining);
        int overflow = maxRequired - requiredSamples;
        if (overflow > 0) {
            qDebug() << "Not enough space in FIFO:" << overflow << maxRequired;
        }

        if (!m_fillBuffer)
        {
            if (!m_iqOnly)
            {
                processDecompressedData(requiredSamples);
            }
            else if (!m_spyServer)
            {
                if (m_dataSocket->bytesAvailable() >= requiredSamples*bytesPerIQPair)
                {
                    // rtl_tcp stream is just IQ samples
                    m_dataSocket->read(&m_tcpBuf[0], requiredSamples*bytesPerIQPair);
                    processUncompressedData(&m_tcpBuf[0], requiredSamples);
                }
            }
            else
            {
                // SpyServer stream is packetized, into a header and body, with multiple packet types
                int requiredBytes = requiredSamples*bytesPerIQPair;
                processSpyServerData(requiredBytes, false);
            }
        }
    }
}

// Copy from decompressed FIFO to replay buffer and sample FIFO
void RemoteTCPInputTCPHandler::processDecompressedData(int requiredSamples)
{
    qint64 requiredBytes = requiredSamples * sizeof(Sample);

    m_replayBuffer->lock();

    while ((requiredBytes > 0) && !m_uncompressedData.empty())
    {
        quint8 *uncompressedPtr;
        qsizetype uncompressedBytes = m_uncompressedData.readPtr(&uncompressedPtr, requiredSamples * sizeof(Sample));
        qsizetype uncompressedSamples = 2 * uncompressedBytes / sizeof(Sample);

        // Save data to replay buffer
	    bool replayEnabled = m_replayBuffer->getSize() > 0;
	    if (replayEnabled) {
		    m_replayBuffer->write((FixReal *) uncompressedPtr, (unsigned int) uncompressedSamples);
        }

        const FixReal *buf = (FixReal *) uncompressedPtr;
        qint32 remaining = uncompressedSamples;

        while (remaining > 0)
        {
            qint32 len;

            // Choose between live data or replayed data
		    if (replayEnabled && m_replayBuffer->useReplay()) {
			    len = m_replayBuffer->read(remaining, buf);
		    } else {
			    len = remaining;
		    }
            remaining -= len;

            calcPower(reinterpret_cast<const Sample*>(buf), len / 2);

            m_sampleFifo->write(reinterpret_cast<const quint8 *>(buf), len * sizeof(FixReal));
        }

        m_uncompressedData.read(uncompressedBytes);
        requiredBytes -= uncompressedBytes;
    }

    m_replayBuffer->unlock();
}

// Convert from uncompressed network format to Samples, then copy to replay buffer and sample FIFO
// The following code assumes host is little endian
void RemoteTCPInputTCPHandler::processUncompressedData(const char *inBuf, int nbSamples)
{
    // Ensure conversion buffer is large enough
    if (nbSamples > (int) m_converterBufferNbSamples)
    {
        if (m_converterBuffer) {
            delete[] m_converterBuffer;
        }
        m_converterBuffer = new int32_t[nbSamples*2];
    }

    // Convert from network format to Sample
    if ((m_settings.m_sampleBits == 32) && (SDR_RX_SAMP_SZ == 24) && m_spyServer)
    {
        const float *in = (const float *) inBuf;
        qint32 *out = (qint32 *) m_converterBuffer;

        for (int is = 0; is < nbSamples*2; is++) {
            out[is] = (qint32)(in[is] * SDR_RX_SCALEF);
        }
    }
    else if ((m_settings.m_sampleBits == 32) && (SDR_RX_SAMP_SZ == 16) && m_spyServer)
    {
        const float *in = (const float *) inBuf;
        qint16 *out = (qint16 *) m_converterBuffer;

        for (int is = 0; is < nbSamples*2; is++) {
            out[is] = (qint16)(in[is] * SDR_RX_SCALEF);
        }
    }
    else if ((m_settings.m_sampleBits == 8) && (SDR_RX_SAMP_SZ == 16))
    {
        const quint8 *in = (const quint8 *) inBuf;
        qint16 *out = (qint16 *) m_converterBuffer;

        for (int is = 0; is < nbSamples*2; is++) {
            out[is] = (((qint16)in[is]) - 128) << 8;
        }
    }
    else if ((m_settings.m_sampleBits == 8) && (SDR_RX_SAMP_SZ == 24))
    {
        const quint8 *in = (const quint8 *) inBuf;
        qint32 *out = (qint32 *) m_converterBuffer;

        for (int is = 0; is < nbSamples*2; is++) {
            out[is] = (((qint32)in[is]) - 128) << 16;
        }
    }
    else if ((m_settings.m_sampleBits == 24) && (SDR_RX_SAMP_SZ == 24))
    {
        const quint8 *in = (const quint8 *) inBuf;
        qint32 *out = (qint32 *) m_converterBuffer;

        for (int is = 0; is < nbSamples*2; is++) {
            out[is] = (((in[3*is+2] << 16) | (in[3*is+1] << 8) | in[3*is]) << 8) >> 8;
        }
    }
    else if ((m_settings.m_sampleBits == 24) && (SDR_RX_SAMP_SZ == 16))
    {
        const quint8 *in = (const quint8 *) inBuf;
        qint16 *out = (qint16 *) m_converterBuffer;

        for (int is = 0; is < nbSamples*2; is++) {
            out[is] = (in[3*is+2] << 8) | in[3*is+1];
        }
    }
    else if ((m_settings.m_sampleBits == 16) && (SDR_RX_SAMP_SZ == 24))
    {
        const qint16 *in = (const qint16 *) inBuf;
        qint32 *out = (qint32 *) m_converterBuffer;

        for (int is = 0; is < nbSamples*2; is++) {
            out[is] = in[is] << 8;
        }
    }
    else if ((m_settings.m_sampleBits == 32) && (SDR_RX_SAMP_SZ == 16))
    {
        const qint32 *in = (const qint32 *) inBuf;
        qint16 *out = (qint16 *) m_converterBuffer;

        for (int is = 0; is < nbSamples*2; is++) {
            out[is] = in[is] >> 8;
        }
    }
    else if ((m_settings.m_sampleBits == 32) && (SDR_RX_SAMP_SZ == 24))
    {
        const qint32 *in = (const qint32 *) inBuf;
        qint32 *out = (qint32 *) m_converterBuffer;

        for (int is = 0; is < nbSamples*2; is++) {
            out[is] = in[is];
        }
    }
    else // invalid size
    {
        qWarning("RemoteTCPInputTCPHandler::convert: unexpected sample size in stream: %d bits", (int) m_settings.m_sampleBits);
        return;
    }

    qint32 len = nbSamples*2;

    // Save data to replay buffer
	m_replayBuffer->lock();
	bool replayEnabled = m_replayBuffer->getSize() > 0;
	if (replayEnabled) {
		m_replayBuffer->write((const FixReal *) m_converterBuffer, len);
    }

    const FixReal *buf = (const FixReal *) m_converterBuffer;
	qint32 remaining = len;

    while (remaining > 0)
    {
        // Choose between live data or replayed data
		if (replayEnabled && m_replayBuffer->useReplay()) {
			len = m_replayBuffer->read(remaining, buf);
		} else {
			len = remaining;
		}
		remaining -= len;

        calcPower(reinterpret_cast<const Sample*>(buf), len / 2);

        m_sampleFifo->write(reinterpret_cast<const quint8*>(buf), len * sizeof(FixReal));
    }

    m_replayBuffer->unlock();
}

void RemoteTCPInputTCPHandler::handleInputMessages()
{
    Message* message;

    while ((message = m_inputMessageQueue.pop()) != 0)
    {
        if (handleMessage(*message)) {
            delete message;
        }
    }
}

bool RemoteTCPInputTCPHandler::handleMessage(const Message& cmd)
{
    if (MsgConfigureTcpHandler::match(cmd))
    {
        qDebug() << "RemoteTCPInputTCPHandler::handleMessage: MsgConfigureTcpHandler";
        const MsgConfigureTcpHandler& notif = (const MsgConfigureTcpHandler&) cmd;
        applySettings(notif.getSettings(), notif.getSettingsKeys(), notif.getForce());
        return true;
    }
    else if (RemoteTCPInput::MsgSendMessage::match(cmd))
    {
        const RemoteTCPInput::MsgSendMessage& msg = (const RemoteTCPInput::MsgSendMessage&) cmd;

        sendMessage(MainCore::instance()->getSettings().getStationName(), msg.getText(), msg.getBroadcast());

        return true;
    }
    else
    {
        return false;
    }
}
