#include "meter.h"
#include <QDebug>

meter::meter(QWidget *parent) : QWidget(parent)
{
    //QPainter painter(this);

    // Colors from qdarkstylesheet:
    //    $COLOR_BACKGROUND_LIGHT: #505F69;
    //    $COLOR_BACKGROUND_NORMAL: #32414B;
    //    $COLOR_BACKGROUND_DARK: #19232D;
    //    $COLOR_FOREGROUND_LIGHT: #F0F0F0; // grey
    //    $COLOR_FOREGROUND_NORMAL: #AAAAAA; // grey
    //    $COLOR_FOREGROUND_DARK: #787878; // grey
    //    $COLOR_SELECTION_LIGHT: #148CD2;
    //    $COLOR_SELECTION_NORMAL: #1464A0;
    //    $COLOR_SELECTION_DARK: #14506E;


    // Colors I found that I liked from VFD images:
    //      3FB7CD
    //      3CA0DB
    //
    // Text in qdarkstylesheet seems to be #EFF0F1

    if(drawLabels)
    {
        mXstart = 32;
    } else {
        mXstart = 0;
    }

    meterType = meterNone;

    currentColor = colorFromString("#148CD2");
    currentColor = currentColor.darker();

    peakColor = colorFromString("#3CA0DB");
    peakColor = peakColor.lighter();

    averageColor = colorFromString("#3FB7CD");

    lowTextColor = colorFromString("#eff0f1");
    lowLineColor = lowTextColor;

    avgLevels.resize(averageBalisticLength, 0);
    peakLevels.resize(peakBalisticLength, 0);

    scaleCache = new QImage(QSize(255+mXstart+15, widgetWindowHeight), QImage::Format_ARGB32);
    scaleCache->fill(Qt::transparent);

    combo = new QComboBox(this);
    combo->addItem("None", meterNone);
    combo->addItem("Sub S-Meter", meterSubS);
    combo->addItem("SWR", meterSWR);
    combo->addItem("ALC", meterALC);
    combo->addItem("Compression", meterComp);
    combo->addItem("Voltage", meterVoltage);
    combo->addItem("Current", meterCurrent);
    combo->addItem("Center", meterCenter);
    combo->addItem("TxRxAudio", meterAudio);
    combo->addItem("RxAudio", meterRxAudio);
    combo->addItem("TxAudio", meterTxMod);

    combo->blockSignals(false);
    connect(combo, SIGNAL(activated(int)), this, SLOT(acceptComboItem(int)));
    //connect(combo, SIGNAL(currentIndexChanged(int)), this, SLOT(acceptComboItem(int)));

    this->setToolTip("");
    combo->hide();
    this->installEventFilter(this);
}

void meter::setCompReverse(bool reverse) {
    if (reverse && meterType != meterComp) {
        this->reverseCompMeter = false;
        return;
    }
    this->reverseCompMeter = reverse;
    recentlyChangedParameters = true; // force scale redraw
}

void meter::setUseGradients(bool useGrads) {
    this->useGradients = useGrads;
}

void meter::setColors(QColor current, QColor peakScale, QColor peakLevel,
                      QColor average, QColor lowLine,
                      QColor lowText)
{
    currentColor = current;

    peakColor = peakLevel; // color for the peak level indicator
    highLineColor = peakScale; // color for the red side of the scale
    highTextColor = peakScale; // color for the red side of the scale's text

    averageColor = average;

    midScaleColor = QColor(Qt::yellow);
    centerTuningColor = QColor(Qt::green);

    lowLineColor = lowLine;
    lowTextColor = lowText;
    this->update();
}

void meter::clearMeterOnPTTtoggle()
{
    // When a meter changes type, such as the fixed S -- TxPo meter,
    // there is automatic clearing. However, some meters do not switch on their own,
    // and thus we are providing this clearing method. We are careful
    // not to clear meters that don't make sense to clear (such as Vd and Id)


    if( (meterType == meterALC) || (meterType == meterSWR)
            || (meterType == meterComp) || (meterType == meterTxMod)
            || (meterType == meterCenter ))
    {
        clearMeter();
    }
}

void meter::clearMeter()
{
    current = 0;
    average = 0;
    peak = 0;

    avgLevels.clear();
    peakLevels.clear();
    avgLevels.resize(averageBalisticLength, 0);
    peakLevels.resize(peakBalisticLength, 0);

    peakPosition = 0;
    avgPosition = 0;
    // re-draw scale:
    update();
}

void meter::setMeterType(meter_t m_type_req)
{
    if(m_type_req == meterType)
        return;


    this->setToolTip("Double-click to select meter type.");

    combo->blockSignals(true);
    combo->clear();
    switch (m_type_req)
    {
    case meterS:
    case meterPower:
    case meterdBm:
    case meterdBu:
    case meterdBuEMF:
        combo->addItem("Main S-Meter", meterS);
        combo->addItem("dBu Meter", meterdBu);
        combo->addItem("dBu EMF", meterdBuEMF);
        combo->addItem("dBm Meter", meterdBm);
        break;
    default:
        combo->addItem("None", meterNone);
        combo->addItem("Sub S-Meter", meterSubS);
        combo->addItem("SWR", meterSWR);
        combo->addItem("ALC", meterALC);
        combo->addItem("Compression", meterComp);
        combo->addItem("Voltage", meterVoltage);
        combo->addItem("Current", meterCurrent);
        combo->addItem("Center", meterCenter);
        combo->addItem("TxRxAudio", meterAudio);
        combo->addItem("RxAudio", meterRxAudio);
        combo->addItem("TxAudio", meterTxMod);
        break;
    }

    int m_index = combo->findData(m_type_req);
    combo->setCurrentIndex(m_index);
    combo->blockSignals(false);

    meterType = m_type_req;
    if (meterType != meterComp) {
        reverseCompMeter = false;
    }
    this->meterShortString = getMeterDebug(meterType);
    recentlyChangedParameters = true;
    // clear average and peak vectors:
    this->clearMeter();
}

void meter::setMeterExtremities(double min, double max, double redline) {
    this->scaleMin = min;
    this->scaleMax = max;
    this->scaleRedline = redline;
    recentlyChangedParameters = true;
    haveExtremities = true;
    qDebug() << "Meter extremities set: shortString: " << meterShortString
             << ", min: " << min << ", max: " << max
             << ", redline: " << redline << "meter: " << meterType;
}

void meter::blockMeterType(meter_t mtype) {
    enableAllComboBoxItems(combo);
    //qDebug() << "Asked to block meter of type: " << getMeterDebug(mtype);
    if(mtype != meterNone) {
        int m_index = combo->findData(mtype);
        setComboBoxItemEnabled(combo, m_index, false);
    }
}

meter_t meter::getMeterType()
{
    return meterType;
}

void meter::setMeterShortString(QString s)
{
    meterShortString = s;
}

QString meter::getMeterShortString()
{
    return meterShortString;
}

void meter::acceptComboItem(int item) {
    qDebug() << "Meter selected combo item number: " << item;

    meter_t meterTypeRequested = static_cast<meter_t>(combo->itemData(item).toInt());
    if(meterType != meterTypeRequested) {
        qDebug() << "Changing meter to type: " << getMeterDebug(meterTypeRequested) << ", with item index: " << item;
        emit configureMeterSignal(meterTypeRequested);
    }

    combo->hide();
    freezeDrawing = false;
}

void meter::handleDoubleClick() {
    freezeDrawing = true;
    combo->show();
}

bool meter::eventFilter(QObject *object, QEvent *event) {

    if( (freezeDrawing) && (event->type() == QEvent::MouseButtonPress) ) {
        combo->hide();
        freezeDrawing = false;
        return true;
    }

    if( (freezeDrawing) && (event->type() == QEvent::MouseButtonDblClick) ) {
        combo->hide();
        freezeDrawing = false;
        return true;
    }

    if(event->type() == QEvent::MouseButtonDblClick) {
        //if( !(meterType == meterS || meterType == meterPower)) {
        handleDoubleClick();
        //}
        return true;
    }

    (void)object;
    return false;
}

void meter::paintEvent(QPaintEvent *)
{
    if(freezeDrawing)
        return;

    if(!haveExtremities) {
        qWarning() << "Cannot draw meter without scale data!";
        return;
    }

    QPainter painter(this);
    // This next line sets up a canvis within the
    // space of the widget, and gives it coordinates.
    // The end effect, is that the drawing functions will all
    // scale to the window size.
    painter.setRenderHint(QPainter::SmoothPixmapTransform);
    painter.setFont(QFont(this->fontInfo().family(), fontSize));
    widgetWindowHeight = this->height();
    painter.setWindow(QRect(0, 0, 255+mXstart+15, widgetWindowHeight));
    barHeight = widgetWindowHeight / 2;

    // We regenerate the scale graphics if:
    // 1. The meter type changed, or,
    // 2. The meter extremities changed

    if( (lastDrawMeterType != meterType)
            || (recentlyChangedParameters)) {
        regenerateScale(&painter);
    }

    if(scaleReady) {
        recallScale(&painter);
    }

    // Guard against drawing empty or garbage data:
    if(haveReceivedSomeData) {
        painter.setPen(currentColor);
        painter.setBrush(currentColor);

        if(meterType == meterCenter)
        {
            drawValue_Center(&painter);
        } else if ( (meterType == meterAudio) ||
                    (meterType == meterTxMod) ||
                    (meterType == meterRxAudio))
        {
            drawValue_Log(&painter);
        } else {
            if(meterType==meterComp)
                drawValue_Linear(&painter, this->reverseCompMeter);
            else
                drawValue_Linear(&painter, false);
        }
        haveUpdatedData = false;
    }
}

void meter::regenerateScale(QPainter *screenPainterHints) {
    // draw a scale and save to scaleCache

    QSize sizeHint = screenPainterHints->window().size();

    if(sizeHint != scaleCache->size()) {
        qDebug() << "Meter Widget painter window does not match cache image size.";
        qDebug() << "Meter Scale cache image size: " << scaleCache->size();
        qDebug() << "Meter Widget size: " << this->size();
        qDebug() << "Meter painter window: " << sizeHint;
        delete scaleCache;
        scaleCache = new QImage(sizeHint, QImage::Format_ARGB32);
    }
#ifdef QT_DEBUG
    QString fontName = screenPainterHints->font().family();
    int fontSize = screenPainterHints->font().pointSize();
    qDebug() << "screenPainterHints: Font: " << fontName << ", size: " << fontSize;
#endif


    scaleCache->fill(Qt::transparent);
    QPainter painter(scaleCache);
    painter.setRenderHints(screenPainterHints->renderHints()); // Copy render hints
    //painter.setRenderHint(QPainter::Antialiasing); // fuzz city
    //painter.setRenderHint(QPainter::HighQualityAntialiasing); // depreciated
    //painter.setRenderHint(QPainter::TextAntialiasing);
    //painter.setRenderHint(QPainter::SmoothPixmapTransform);

    painter.setCompositionMode(QPainter::CompositionMode_Source); // Important for correct alpha blending

    painter.setFont(QFont(this->fontInfo().family(), fontSize));
    painter.fillRect(rect(), Qt::transparent); // Clear the image before redrawing

#ifdef QT_DEBUG
    QString fontNameP = painter.font().family();
    int fontSizeP = painter.font().pointSize();
    qDebug() << "cache painter: Font: " << fontNameP << ", size: " << fontSizeP
             << "infoFamily: " << painter.fontInfo().family();
#endif

    QFontMetrics fm = painter.fontMetrics();

    switch(meterType)
    {
    case meterS:
        label = "S";
        labelWidth = fm.boundingRect(label).width();
        drawScaleS(&painter);
        break;
    case meterSubS:
        label = "Sub";
        labelWidth = fm.boundingRect(label).width();
        drawScaleS(&painter);
        break;
    case meterPower:
        label = "PWR";
        labelWidth = fm.boundingRect(label).width();
        drawScalePo(&painter);
        break;
    case meterALC:
        label = "ALC";
        labelWidth = fm.boundingRect(label).width();
        drawScaleALC(&painter);
        break;
    case meterSWR:
        label = "SWR";
        labelWidth = fm.boundingRect(label).width();
        drawScaleSWR(&painter);
        break;
    case meterCenter:
        label = "CTR";
        labelWidth = fm.boundingRect(label).width();
        drawScaleCenter(&painter);
        break;
    case meterVoltage:
        label = "Vd";
        labelWidth = fm.boundingRect(label).width();
        drawScaleVd(&painter);
        break;
    case meterCurrent:
        label = "Id";
        labelWidth = fm.boundingRect(label).width();
        drawScaleId(&painter);
        break;
    case meterComp:
        label = "CMP(dB)";
        labelWidth = fm.boundingRect(label).width();
        if(reverseCompMeter) {
            drawScaleCompInverted(&painter);
        } else {
            drawScaleComp(&painter);
        }
        break;
    case meterNone:
        label = tr("Double-click to set meter");
        labelWidth = fm.boundingRect(label).width();
        drawLabel(&painter);
        return;
        break;
    case meterAudio:
        label = "dBfs";
        peakRedLevel = 241;
        labelWidth = fm.boundingRect(label).width();
        drawScale_dBFs(&painter);
        break;
    case meterRxAudio:
        label = "Rx(dBfs)";
        peakRedLevel = 241;
        labelWidth = fm.boundingRect(label).width();
        drawScale_dBFs(&painter);
        break;
    case meterTxMod:
        label = "Tx(dBfs)";
        labelWidth = fm.boundingRect(label).width();
        peakRedLevel = 241;
        drawScale_dBFs(&painter);
        break;
    case meterdBu:
        label = "dBu";
        labelWidth = fm.boundingRect(label).width();
        peakRedLevel = 241;
        drawScaledBPositive(&painter);
        break;
    case meterdBuEMF:
        label = "dBu(EMF)";
        labelWidth = fm.boundingRect(label).width();
        peakRedLevel = 241;
        drawScaledBPositive(&painter);
        break;
    case meterdBm:
        label = "dBm";
        labelWidth = fm.boundingRect(label).width();
        peakRedLevel = 241;
        drawScaledBNegative(&painter);
        break;
    default:
        label = "DN";
        labelWidth = fm.boundingRect(label).width();
        peakRedLevel = 241;
        drawScaleRaw(&painter);
        break;
    }
    if(drawLabels)
    {
        // We are tracking the drawLabels parameter,
        // because we may some day want to squeeze this widget
        // into a place where the label cannot fit.

        drawLabel(&painter);
    }

    painter.end();
    recentlyChangedParameters = false;
    lastDrawMeterType = meterType;
    scaleReady = true;
}

void meter::recallScale(QPainter *painter) {
    // read the scaleCache onto the canvas
    //painter->drawImage(0, 0, *scaleCache);
    // This should prevent image scaling, which makest the text less clear:
    painter->drawImage(QPoint(0,0), *scaleCache, scaleCache->rect());
}

void meter::drawLabel(QPainter *qp)
{
    qp->setPen(lowTextColor);
    qp->drawText(0,scaleTextYstart, label );
}

void meter::scaleLinearNumbersForDrawing() {
    // input:  current, average, peak (double)
    // output: currentRect, averageRect, peakRect (int)

    currentRect = getPixelScaleFromValue(current);
    averageRect = getPixelScaleFromValue(average);
    peakRect =    getPixelScaleFromValue(peak);
}

double meter::getValueFromPixelScale(int p) {
    // Give a value 0-255 and receive a scaled value in return
    return (p*(scaleMax-scaleMin)/255) + scaleMin;
}

int meter::getPixelScaleFromValue(double v) {
    // give a value within the scale and receive a pixel scale value (0-255)
    return (v-scaleMin) * (255/(scaleMax-scaleMin));
}

int meter::nearestStep(double d, int stepSize) {
    // This function was only tested with negative numbers,
    // and produces the rounding edge at the correct spot for
    // our dBm (R8600) scale.
    bool flipped = false;
    if(d < 0) {
        d *= -1;
        flipped = true;
    }

    int n = round(d/stepSize);
    // 99 becomes 9.9 becomes 10
    // 95 becomes 9.5 becomes 10
    // 94 becomes 9.4 becomes  9

    if(flipped)
        n*=-stepSize;
    else
        n *= stepSize;

    return n;
}

void meter::scaleLogNumbersForDrawing() {
    currentRect = (int)((1-audiopot[255-(int)current])*255);
    averageRect = (int)((1-audiopot[255-(int)average])*255);
    peakRect = (int)((1-audiopot[255-(int)peak])*255);
}

void meter::drawValue_Linear(QPainter *qp, bool reverse) {
    // Draw a rectangle.

    // The parameters current, peak, and average need to be scaled
    // to 0-255.

    // Data input:  scaleMin ---- scaleMax
    // Data output:     0    ----    255
    scaleLinearNumbersForDrawing();

    this->setAccessibleName(QString("Meter: %1 percent").arg( (int)(100*(double)(currentRect/255.0))));

    if(currentRect < 0) {
        return;
    }

    if (this->meterType == meterdBuEMF || this->meterType == meterdBm || this->meterType == meterdBu)
    {
        qp->setPen(lowTextColor);
        uchar prec=1;
        if (current >= 100.0 || current <= -100.0)
            prec=0;
        qp->drawText(0,scaleTextYstart+20, QString("%0").arg(current,0,'f',prec,'0'));

    }
    // And then, we can just plot them, since we already scaled
    // the scales, right?
    if(useGradients) {
        QLinearGradient grad(QPointF(0, 0), QPointF(255, 0));
        if(reverse) {
            grad.setColorAt(1, currentColor.darker().darker());
            grad.setColorAt(0, currentColor.lighter());
        } else {
            grad.setColorAt(0, currentColor.darker().darker());
            grad.setColorAt(1, currentColor.lighter());
        }
        qp->setBrush(grad);
    } else {
        qp->setBrush(currentColor);
    }
    qp->setPen(currentColor);
    if(reverse) {
        qp->drawRect(255+mXstart,mYstart,-currentRect,barHeight);
    } else {
        qp->drawRect(mXstart,mYstart,currentRect,barHeight);
    }

    // Average:
    qp->setPen(averageColor);
    qp->setBrush(averageColor);
    if(reverse) {
        qp->drawRect(255+mXstart-averageRect,mYstart,-1,barHeight); // bar is 1 pixel wide, height = meter start?
    } else {
        qp->drawRect(mXstart+averageRect-1,mYstart,1,barHeight); // bar is 1 pixel wide, height = meter start?
    }

    // Peak:
    qp->setPen(peakColor);
    qp->setBrush(peakColor);
    if(peak > peakRedLevel)
    {
        qp->setBrush(Qt::red);
        qp->setPen(Qt::red);
    }
    if(reverse) {
        qp->drawRect(255+mXstart-peakRect+1,mYstart,-1,barHeight);
    } else {
        qp->drawRect(mXstart+peakRect-1,mYstart,2,barHeight);
    }
}

void meter::drawValue_Center(QPainter *qp) {
    // Draw a center-referenced rectangle

    // First, scale the data for 0-255:
    // Not exactly needed for this since only icom data uses this function currently and that data are already 0-255
    // But we are going to call it anyway to prove that our code is good:
    scaleLinearNumbersForDrawing();

    // Current value:
    // starting at the center (mXstart+128) and offset by (current-128)
    if(useGradients) {
        QLinearGradient grad(QPointF(0, 0), QPointF(255, 0));
        grad.setColorAt(0, currentColor.darker().darker());
        grad.setColorAt(0.5, currentColor.lighter());
        grad.setColorAt(1, currentColor.darker().darker());
        qp->setBrush(grad);
    } else {
        qp->setBrush(currentColor);
    }

    qp->setPen(currentColor);
    qp->drawRect(mXstart+128,mYstart,currentRect-128,barHeight);

    // Average:
    qp->setPen(averageColor);
    qp->setBrush(averageColor);
    qp->drawRect(mXstart+averageRect-1,mYstart,1,barHeight); // bar is 1 pixel wide, height = meter start?

    // Peak: (what does peak mean for off-center deviation?)
    qp->setPen(peakColor);
    qp->setBrush(peakColor);
    if((peak > 191) || (peak < 63))
    {
        qp->setBrush(Qt::red);
        qp->setPen(Qt::red);
    }

    qp->drawRect(mXstart+peakRect-1,mYstart,1,barHeight);
}

void meter::drawValue_Log(QPainter *qp) {
    // Log scale but still 0-255:
    scaleLogNumbersForDrawing();

    // Current value:
    // X, Y, Width, Height
    if(useGradients) {
        QLinearGradient grad(QPointF(0, 0), QPointF(255, 0));
        grad.setColorAt(0, currentColor.darker().darker());
        grad.setColorAt(1, currentColor.lighter());

        qp->setBrush(grad);
    } else {
        qp->setBrush(currentColor);
    }
    qp->setPen(currentColor);

    qp->drawRect(mXstart,mYstart,currentRect,barHeight);

    // Average:
    qp->setPen(averageColor);
    qp->setBrush(averageColor);
    qp->drawRect(mXstart+averageRect-1,mYstart,1,barHeight); // bar is 1 pixel wide, height = meter start?

    // Peak:
    qp->setPen(peakColor);
    qp->setBrush(peakColor);
    if(peak > peakRedLevel)
    {
        qp->setBrush(Qt::red);
        qp->setPen(Qt::red);
    }

    qp->drawRect(mXstart+peakRect-1,mYstart,2,barHeight);
}

void meter::setLevel(double current)
{
    // "current" means "now", ie, the value at this moment.

    this->current = current;

    avgLevels[(avgPosition++)%averageBalisticLength] = current;
    peakLevels[(peakPosition++)%peakBalisticLength] = current;

    double sum=0.0;

    for(unsigned int i=0; i < (unsigned int)std::min(avgPosition, (int)avgLevels.size()); i++)
    {
        sum += avgLevels[i];
    }

    // This never divides by zero because the above section of this code
    // inserts data, thus assuring that we will never have a zero for both
    // the position and the size.
    this->average = sum / std::min(avgPosition, (int)avgLevels.size());

    this->peak = (-1)*UINT16_MAX;

    for(unsigned int i=0; i < peakLevels.size(); i++)
    {
        if( peakLevels[i] >  this->peak)
            this->peak = peakLevels[i];
    }

    haveUpdatedData = true;
    haveReceivedSomeData = true;

    if(meterType==meterS) {
        // This is just so I can catch it in the debugger
        sum++;
        volatile int ooo = 0;
        (void)ooo;
    }

    this->update();
}

void meter::setLevels(double current, double peak)
{
    this->current = current;
    this->peak = peak;

    avgLevels[(avgPosition++)%averageBalisticLength] = current;
    double sum=0;
    for(unsigned int i=0; i < (unsigned int)std::min(avgPosition, (int)avgLevels.size()); i++)
    {
        sum += avgLevels.at(i);
    }
    this->average = sum / std::min(avgPosition, (int)avgLevels.size());

    haveUpdatedData = true;
    haveReceivedSomeData = true;
    this->update(); // place repaint event on the event queue
}

void meter::setLevels(double current, double peak, double average)
{
    this->current = current;
    this->peak = peak;
    this->average = average;

    haveUpdatedData = true;
    haveReceivedSomeData = true;
    this->update(); // place repaint event on the event queue
}

void meter::updateDrawing(int num)
{
    fontSize = num;
    length = num;
}

// The drawScale functions draw the numbers and number unerline for each type of meter

void meter::drawScaleRaw(QPainter *qp)
{
    qp->setPen(lowTextColor);
    //qp->setFont(QFont("Arial", fontSize));
    int i=mXstart;
    for(; i<mXstart+256; i+=25)
    {
        qp->drawText(i,scaleTextYstart, QString("%1").arg(i) );
    }

    // Now the lines:
    qp->setPen(lowLineColor);

    // Line: X1, Y1 -->to--> X2, Y2
    qp->drawLine(mXstart,scaleLineYstart,255/2+mXstart,scaleLineYstart);
    qp->setPen(highLineColor);
    qp->drawLine(255/2+mXstart,scaleLineYstart,255+mXstart,scaleLineYstart);
}

void meter::drawScaledBPositive(QPainter *qp)
{
    // dB scale which is mostly (or all) positive numbers
    // used for dBu EMF and dBu



    int redLevelRect = getPixelScaleFromValue(scaleRedline);

    qp->setPen(lowTextColor);

    int scaleSpan = scaleMax - scaleMin;
    scaleSpan = (scaleSpan/10) * 10;
    int stepDelta = 0;
    if(scaleSpan < 100) {
        stepDelta = 10;
    } else {
        stepDelta = 20;
    }

    QFontMetrics fm = qp->fontMetrics();
    QRect textRect = fm.boundingRect(QString::number(scaleMax));
    int textMaxLength = textRect.width();
    int scaleCacheWidth = scaleCache->size().width();

    int db = 0;
    int dbPrior = -10;
    double val = 0;

    QString formattedString;
    for(int p=mXstart; p < mXstart+255; p++) {
        val = getValueFromPixelScale(p-mXstart);
        db = ((int)round(val)/stepDelta) * stepDelta;
        //db = ((int)(round(val/10.0))*10 / stepDelta) * stepDelta; // round to nearest step

        if(db != dbPrior) {
            if(db > scaleRedline) {
                qp->setPen(highTextColor);
            } else {
                qp->setPen(lowTextColor);
            }
            formattedString = QString::number(db);
//            qDebug() << "p: " << p << "val: " << val
//                     << "dB: " << db << "dBprior: " << dbPrior;
            if( (p + textMaxLength < scaleCacheWidth)
                && (p > labelWidth ) ){
                qp->drawText(p,scaleTextYstart, formattedString );
            }
            qp->drawLine(p,scaleTextYstart, p, scaleTextYstart+5);
            dbPrior = db;
        }
    }

    // Now the lines:
    qp->setPen(lowLineColor);

    // Line: X1, Y1 -->to--> X2, Y2
    qp->drawLine(mXstart,scaleLineYstart,redLevelRect+mXstart,scaleLineYstart);
    qp->setPen(highLineColor);
    qp->drawLine(redLevelRect+mXstart,scaleLineYstart,255+mXstart,scaleLineYstart);

}

void meter::drawScaledBNegative(QPainter *qp)
{
    // dB scale which is mostly (or all) negative numbers
    // used for dBm

    int redLevelRect = getPixelScaleFromValue(scaleRedline);
    QFontMetrics fm = qp->fontMetrics();
    QRect textRect = fm.boundingRect(QString::number(-100));
    int textMaxLength = textRect.width();
    int scaleCacheWidth = scaleCache->size().width();

    qp->setPen(lowTextColor);

    int scaleSpan = scaleMax - scaleMin;
    scaleSpan = (scaleSpan*10) / 10;
    int stepDelta = 0;
    if(scaleSpan < 100) {
        stepDelta = 10;
    } else {
        stepDelta = 20;
    }

    int db = 0;
    int dbPrior = -10;
    double val = 0;

    QString formattedString;
    for(int p=mXstart; p < mXstart+255; p++) {
        val = getValueFromPixelScale(p-mXstart);
        db = nearestStep(val, stepDelta);

        if( (db != dbPrior) && ((int)round(val)%10==0)) {
            if(db > scaleRedline) {
                qp->setPen(highTextColor);
            } else {
                qp->setPen(lowTextColor);
            }
            formattedString = QString::number(db);
    //            qDebug() << "p: " << p << "val: " << val
    //                     << "dB: " << db << "dBprior: " << dbPrior;
            if ( (p + textMaxLength < scaleCacheWidth)
            && (p > labelWidth) ){
                qp->drawText(p,scaleTextYstart, formattedString );
            }
            qp->drawLine(p,scaleTextYstart, p, scaleTextYstart+5);
            dbPrior = db;
        }
    }

    // Now the lines:
    qp->setPen(lowLineColor);

    // Line: X1, Y1 -->to--> X2, Y2
    qp->drawLine(mXstart,scaleLineYstart,redLevelRect+mXstart,scaleLineYstart);
    qp->setPen(highLineColor);
    qp->drawLine(redLevelRect+mXstart,scaleLineYstart,255+mXstart,scaleLineYstart);

}

void meter::drawScale_dBFs(QPainter *qp)
{
    qp->setPen(lowTextColor);
    peakRedLevel = 193;

    if(meterType==meterAudio)
        qp->drawText(20+mXstart,scaleTextYstart, QString("-30"));
    qp->drawText(38+mXstart+2,scaleTextYstart, QString("-24"));
    qp->drawText(71+mXstart,scaleTextYstart, QString("-18"));
    qp->drawText(124+mXstart,scaleTextYstart, QString("-12"));
    qp->drawText(193+mXstart,scaleTextYstart, QString("-6"));
    qp->drawText(255+mXstart,scaleTextYstart, QString("0"));

    // Low ticks:
    qp->setPen(lowLineColor);
    qp->drawLine(20+mXstart,scaleTextYstart, 20+mXstart, scaleTextYstart+5);
    qp->drawLine(38+mXstart,scaleTextYstart, 38+mXstart, scaleTextYstart+5);
    qp->drawLine(71+mXstart,scaleTextYstart, 71+mXstart, scaleTextYstart+5);
    qp->drawLine(124+mXstart,scaleTextYstart, 124+mXstart, scaleTextYstart+5);


    // High ticks:
    qp->setPen(highLineColor);
    qp->drawLine(193+mXstart,scaleTextYstart, 193+mXstart, scaleTextYstart+5);
    qp->drawLine(255+mXstart,scaleTextYstart, 255+mXstart, scaleTextYstart+5);

    // Now the lines:
    qp->setPen(lowLineColor);

    // Line: X1, Y1 -->to--> X2, Y2
    qp->drawLine(mXstart,scaleLineYstart,peakRedLevel+mXstart,scaleLineYstart);
    qp->setPen(highLineColor);
    qp->drawLine(peakRedLevel+mXstart,scaleLineYstart,255+mXstart,scaleLineYstart);
}

void meter::drawScaleVd(QPainter *qp)
{
    int redLevelRect = getPixelScaleFromValue(scaleRedline);

    qp->setPen(lowTextColor);

    int scaleSpan = scaleMax - scaleMin;
    scaleSpan = (scaleSpan*10) / 10;
    int stepDelta = 0;
    if(scaleSpan < 20) {
        stepDelta = 1;
    } else {
        stepDelta = 2;
    }

    int volt = int(scaleMin);
    int voltPrior = int(scaleMin); // causes the zero to be skipped
    double val = 0;

    QString formattedString;
    for(int p=mXstart; p < mXstart+255; p++) {
        val = getValueFromPixelScale(p-mXstart);
        volt = ((int)val / stepDelta) * stepDelta; // round to nearest step

        if(volt != voltPrior) {
            //qDebug() << "val: " << val << ", power: " << power << ", powerPrior: " << powerPrior << " scaleSpan:" << scaleSpan << "stepDelta: " << stepDelta;
            if(volt > scaleRedline) {
                qp->setPen(highTextColor);
            } else {
                qp->setPen(lowTextColor);
            }
            formattedString = QString::number(volt);
            qp->drawText(p,scaleTextYstart, formattedString );
            qp->drawLine(p,scaleTextYstart, p, scaleTextYstart+5);
            voltPrior = volt;

        }
    }

    // Now the lines:
    qp->setPen(lowLineColor);

    // Line: X1, Y1 -->to--> X2, Y2
    qp->drawLine(mXstart,scaleLineYstart,redLevelRect+mXstart,scaleLineYstart);
    qp->setPen(highLineColor);
    qp->drawLine(redLevelRect+mXstart,scaleLineYstart,255+mXstart,scaleLineYstart);
}

void meter::drawScaleCenter(QPainter *qp)
{
    // No known units
    qp->setPen(lowLineColor);
    qp->drawText(60+mXstart,scaleTextYstart, QString("-"));

    qp->setPen(centerTuningColor);
    // Attempt to draw the zero at the actual center
    qp->drawText(128-2+mXstart,scaleTextYstart, QString("0"));

    qp->setPen(lowLineColor);
    qp->drawText(195+mXstart,scaleTextYstart, QString("+"));

    qp->setPen(lowLineColor);
    qp->drawLine(mXstart,scaleLineYstart,128-32+mXstart,scaleLineYstart);

    qp->setPen(centerTuningColor);
    qp->drawLine(128-32+mXstart,scaleLineYstart,128+32+mXstart,scaleLineYstart);

    qp->setPen(lowLineColor);
    qp->drawLine(128+32+mXstart,scaleLineYstart,255+mXstart,scaleLineYstart);
}


void meter::drawScalePo(QPainter *qp)
{
    int redLevelRect = getPixelScaleFromValue(scaleRedline);

    qp->setPen(lowTextColor);

    int scaleSpan = scaleMax - scaleMin;
    scaleSpan = (scaleSpan*10) / 10;
    int stepDelta = 0;
    if(scaleSpan < 20) {
        // Low power radio, show each 1 watt on the scale
        stepDelta = 1;
    } else {
        // High power radio, show every 10 watts on the scale.
        stepDelta = 20;
    }

    int power = 0;
    int powerPrior = 0; // causes the zero to be skipped
    double val = 0;

    QString formattedString;
    for(int p=mXstart; p < mXstart+255; p++) {
        val = getValueFromPixelScale(p-mXstart);
        power = ((int)val / stepDelta) * stepDelta; // round to nearest step


        if(power != powerPrior) {
            //qDebug() << "val: " << val << ", power: " << power << ", powerPrior: " << powerPrior << " scaleSpan:" << scaleSpan << "stepDelta: " << stepDelta;
            if(power > scaleRedline) {
                qp->setPen(highTextColor);
            } else {
                qp->setPen(lowTextColor);
            }
            formattedString = QString::number(power);
            qp->drawText(p,scaleTextYstart, formattedString );
            qp->drawLine(p,scaleTextYstart, p, scaleTextYstart+5);
            powerPrior = power;

        }
    }

    // Now the lines:
    qp->setPen(lowLineColor);

    // Line: X1, Y1 -->to--> X2, Y2
    qp->drawLine(mXstart,scaleLineYstart,redLevelRect+mXstart,scaleLineYstart);
    qp->setPen(highLineColor);
    qp->drawLine(redLevelRect+mXstart,scaleLineYstart,255+mXstart,scaleLineYstart);
}

void meter::drawScaleRxdB(QPainter *qp)
{
    // TODO: remove
    (void)qp;
}

void meter::drawScaleALC(QPainter *qp)
{
    int redLevelRect = getPixelScaleFromValue(scaleRedline);

    qp->setPen(lowTextColor);

    int scaleSpan = scaleMax - scaleMin;
    scaleSpan = (scaleSpan*10) / 10;
    int stepDelta = 25;


    int alc = 0;
    int alcPrior = -1; // causes the zero to be skipped
    double val = 0;

    QString formattedString;
    for(int p=mXstart; p < mXstart+255; p++) {
        val = getValueFromPixelScale(p-mXstart)*100; // values are 0-1-2, with 1 being 100%
        alc = ((int)val / stepDelta) * stepDelta; // round to nearest step


        if(alc != alcPrior) {
            //qDebug() << "val: " << val << ", alc: " << alc << ", alcPrior: " << alcPrior << " scaleSpan:" << scaleSpan << "stepDelta: " << stepDelta;
            if(alc > scaleRedline*99) {
                qp->setPen(highTextColor);
            } else {
                qp->setPen(lowTextColor);
            }
            formattedString = QString::number(alc);
            qp->drawText(p,scaleTextYstart, formattedString );
            qp->drawLine(p,scaleTextYstart, p, scaleTextYstart+5);
            //            qp->drawLine(p,scaleTextYstart, p, scaleTextYstart+5);

            alcPrior = alc;
        }
    }

    // Now the lines:
    qp->setPen(lowLineColor);

    // Line: X1, Y1 -->to--> X2, Y2
    qp->drawLine(mXstart,scaleLineYstart,redLevelRect+mXstart,scaleLineYstart);
    qp->setPen(highLineColor);
    qp->drawLine(redLevelRect+mXstart,scaleLineYstart,255+mXstart,scaleLineYstart);
}

void meter::drawScaleComp(QPainter *qp)
{
    int redLevelRect = getPixelScaleFromValue(scaleRedline);

    qp->setPen(lowTextColor);

    int scaleSpan = scaleMax - scaleMin;
    scaleSpan = (scaleSpan*10) / 10;
    int stepDelta = 0;
    if(scaleSpan <= 30) {
        stepDelta = 3;
    } else {
        stepDelta = 12;
    }

    int comp = 0;
    int compPrior = 0; // causes the zero to be skipped
    double val = 0;

    QString formattedString;
    // Adding stepDelta to the start means we skip the first step.
    for(int p=mXstart; p < mXstart+255; p++) {
        val = getValueFromPixelScale(p-mXstart);
        comp = ((int)val / stepDelta) * stepDelta; // round to nearest step
        if(comp != compPrior) {
            //qDebug() << "val: " << val << ", comp: " << comp << ", compPrior: " << compPrior << " scaleSpan:" << scaleSpan << "stepDelta: " << stepDelta;
            if(compPrior != 0) {
                if(comp > scaleRedline) {
                    qp->setPen(highTextColor);
                } else {
                    qp->setPen(lowTextColor);
                }
                formattedString = QString::number(comp);
                qp->drawText(p,scaleTextYstart, formattedString );
                qp->drawLine(p,scaleTextYstart, p, scaleTextYstart+5);
            }
            compPrior = comp;
        }
    }

    // Now the lines:
    qp->setPen(lowLineColor);

    // Line: X1, Y1 -->to--> X2, Y2
    qp->drawLine(mXstart,scaleLineYstart,redLevelRect+mXstart,scaleLineYstart);
    qp->setPen(highLineColor);
    qp->drawLine(redLevelRect+mXstart,scaleLineYstart,255+mXstart,scaleLineYstart);
}

void meter::drawScaleCompInverted(QPainter *qp) {
    // inverted scale

    int redLevelRect = getPixelScaleFromValue(scaleRedline);

    qp->setPen(lowTextColor);

    int scaleSpan = scaleMax - scaleMin;
    scaleSpan = (scaleSpan*10) / 10;
    int stepDelta = 0;
    if(scaleSpan <= 30) {
        stepDelta = 3;
    } else {
        stepDelta = 12;
    }

    int comp = 0;
    int compPrior = -1; // -1 allows the first value to be drawn
    double val = 0;
    QString formattedString;
    // Adding stepDelta to the start means we skip the first step.
    for(int p=mXstart; p < mXstart+255-64; p++) {
        val = getValueFromPixelScale(p-mXstart);
        comp = ((int)val / stepDelta) * stepDelta; // round to nearest step
        if(comp != compPrior) {
            //qDebug() << "p: " << p << ", val: " << val << ", comp: " << comp << ", compPrior: " << compPrior << " scaleSpan:" << scaleSpan << "stepDelta: " << stepDelta;
            if(comp > scaleRedline-3) {
                qp->setPen(highTextColor);
            } else {
                qp->setPen(lowTextColor);
            }
            formattedString = QString::number(comp);
            qp->drawText(255+mXstart-p,scaleTextYstart, formattedString );
            qp->drawLine(255+mXstart-p,scaleTextYstart, 255+mXstart-p, scaleTextYstart+5);

            compPrior = comp;
        }
    }

    // Now the lines:
    qp->setPen(lowLineColor);

    // Line: X1, Y1 -->to--> X2, Y2
    qp->drawLine(255+mXstart,scaleLineYstart,255-redLevelRect+mXstart,scaleLineYstart);
    qp->setPen(highLineColor);
    qp->drawLine(255-redLevelRect+mXstart,scaleLineYstart,mXstart,scaleLineYstart);
}

void meter::drawScaleSWR(QPainter *qp)
{
    int redLevelRect = getPixelScaleFromValue(scaleRedline);

    int scaleSpan = scaleMax - scaleMin;
    scaleSpan = (scaleSpan*10) / 10;
    bool smallSteps = false;
    // Icom SWR scale goes up to 20:1
    // Kenwood SWR scale goes up to 5:1
    if(scaleSpan <= 6) {
        smallSteps = true;
    }

    // Draw vertical tic marks and scale numbers:
    qp->setPen(lowTextColor);
    int swr = 0;
    int swrPrior = -1;
    double val = 0;
    int fives = 0;
    int tens = 0;
    int decimals = 0;
    QString formattedString;
    for(int p=mXstart; p < mXstart+255; p++) {
        val = getValueFromPixelScale(p-mXstart);
        if(smallSteps) {
            swr = (val * 50)/50;
            swr = swr * 10;
            fives = ((int)(val*10))%5;
            tens = ((int)(val*10))%10;
            if( (fives==0) && (tens!=0)) {
                swr = swr + 5;
                decimals = 1;
            } else {
                decimals = 0;
            }
        } else {
            swr = (val * 50)/50;
            swr *= 10;
        }

        if( (swr != swrPrior) && (swr > swrPrior) ) {
            if( (swr > 50) && (swr - swrPrior < 20) )
                    continue;

            // qDebug() << "SWR meter: p: " << p << ", val: " << val << "swr: " << swr << "swrPrior: " << swrPrior << "fives: " << fives << "tens:" << tens;
            if(val >= scaleRedline) {
                qp->setPen(highTextColor);
            } else {
                qp->setPen(lowTextColor);
            }
            formattedString = QString::number(swr/10.0, 'f', decimals);
            qp->drawText(p,scaleTextYstart, formattedString );
            qp->drawLine(p,scaleTextYstart, p, scaleTextYstart+5);
            swrPrior = swr;
        }
    }

    // Draw horizontal underline:
    qp->setPen(lowLineColor);
    qp->drawLine(mXstart,scaleLineYstart,redLevelRect+mXstart,scaleLineYstart);
    qp->setPen(highLineColor);
    qp->drawLine(redLevelRect+mXstart,scaleLineYstart,255+mXstart,scaleLineYstart);
}

void meter::drawScaleId(QPainter *qp)
{
    int redLevelRect = getPixelScaleFromValue(scaleRedline);

    qp->setPen(lowTextColor);

    int scaleSpan = scaleMax - scaleMin;
    scaleSpan = (scaleSpan*10) / 10;
    int stepDelta = 0;
    if(scaleSpan < 20) {
        stepDelta = 1;
    } else if (scaleSpan < 40 ){
        stepDelta = 2;
    } else {
        stepDelta = 5;
    }

    int current = 0;
    int currentPrior = 0; // causes the zero to be skipped
    double val = 0;

    QString formattedString;
    for(int p=mXstart; p < mXstart+255; p++) {
        val = getValueFromPixelScale(p-mXstart);
        current = ((int)val / stepDelta) * stepDelta; // round to nearest step

        if(current != currentPrior) {
            //qDebug() << "val: " << val << ", power: " << power << ", powerPrior: " << powerPrior << " scaleSpan:" << scaleSpan << "stepDelta: " << stepDelta;
            if(current > scaleRedline) {
                qp->setPen(highTextColor);
            } else {
                qp->setPen(lowTextColor);
            }
            formattedString = QString::number(current);
            qp->drawText(p,scaleTextYstart, formattedString );
            qp->drawLine(p,scaleTextYstart, p, scaleTextYstart+5);
            currentPrior = current;

        }
    }

    // Now the lines:
    qp->setPen(lowLineColor);

    // Line: X1, Y1 -->to--> X2, Y2
    qp->drawLine(mXstart,scaleLineYstart,redLevelRect+mXstart,scaleLineYstart);
    qp->setPen(highLineColor);
    qp->drawLine(redLevelRect+mXstart,scaleLineYstart,255+mXstart,scaleLineYstart);
}

void meter::drawScaleS(QPainter *qp)
{
    // double --> S-Unit:
    // -54 = S0
    // -48 = S1
    // -42 = S2
    // -36 = S3
    // -30 = S4
    // -24 = S5
    // -18 = S6
    // -12 = S4
    // -6 = S8
    // 0 = S9
    // 10 = +10
    // 20 = +20
    // 30 = +30 ... etc
    int redLevelRect = getPixelScaleFromValue(scaleRedline);

    qp->setPen(lowTextColor);

    // We must find the pixel values (0-255) which correspond to those s-units
    // and we are assuming a linear scale because 6dB per s-unit is a constant rate

    int sunit = 0;
    int sunitPrior = -1;
    double val = 0;
    bool plus = false;
    for(int p=mXstart; p < mXstart+255; p++) {
        val = getValueFromPixelScale(p-mXstart);
        // Due to low precision, we have to set this threshold at 1 instead of 0.
        // We also have to be carefuly not to double-write at S9
        if(val <=1) {
            sunit = (val-scaleMin)/6;
        } else if (val >= 10 ){
            sunit = ((int)val/10) * 10; // round to nearest ten
            if(sunit==10) {
                plus = true;
            } else {
                plus = false;
            }
        }

        if(sunit != sunitPrior) {
            if(val >= scaleRedline) {
                qp->setPen(highTextColor);
                //qDebug() << "pval: " << p-mXstart << "val: " << val <<   ", s-unit: " << sunit << ", sunit prior: " << sunitPrior;
            } else {
                qp->setPen(lowTextColor);
            }
            if(plus)
                qp->drawText(p-10,scaleTextYstart, QString("+%1").arg(sunit) );
            else
                qp->drawText(p,scaleTextYstart, QString("%1").arg(sunit) );
            qp->drawLine(p,scaleTextYstart, p, scaleTextYstart+5);
            sunitPrior = sunit;
        }
    }

    // Horizontal lines:
    qp->setPen(lowLineColor);
    qp->drawLine(mXstart,scaleLineYstart,redLevelRect+mXstart,scaleLineYstart);
    qp->setPen(highLineColor);
    qp->drawLine(redLevelRect+mXstart,scaleLineYstart,255+mXstart,scaleLineYstart);
}

void meter::muteSingleComboItem(QComboBox *comboBox, int index) {
    enableAllComboBoxItems(comboBox);
    setComboBoxItemEnabled(comboBox, index, false);
}

void meter::enableAllComboBoxItems(QComboBox *combobox, bool en) {
    for(int i=0; i < combobox->count(); i++) {
        setComboBoxItemEnabled(combobox, i, en);
    }
}

void meter::setComboBoxItemEnabled(QComboBox * comboBox, int index, bool enabled)
{
    auto * model = qobject_cast<QStandardItemModel*>(comboBox->model());
    assert(model);
    if(!model) return;

    auto * item = model->item(index);
    assert(item);
    if(!item) return;
    item->setEnabled(enabled);
}

