/* -*- c++ -*- */
/*
 * Copyright 2008-2011,2014 Free Software Foundation, Inc.
 *
 * This file is part of GNU Radio
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 *
 */
#include <gnuradio/qtgui/spectrumdisplayform.h>

#include <gnuradio/qtgui/qtgui_types.h>

#include <QColorDialog>
#include <QMessageBox>

#include <cmath>

SpectrumDisplayForm::SpectrumDisplayForm(QWidget* parent) : QWidget(parent)
{
    setupUi(this);

    d_clicked = false;
    d_clicked_freq = 0;

    _systemSpecifiedFlag = false;
    _intValidator = new QIntValidator(this);
    _intValidator->setBottom(0);
    _frequencyDisplayPlot = new FrequencyDisplayPlot(1, FrequencyPlotDisplayFrame);
    _waterfallDisplayPlot = new WaterfallDisplayPlot(1, WaterfallPlotDisplayFrame);
    _timeDomainDisplayPlot = new TimeDomainDisplayPlot(2, TimeDomainDisplayFrame);
    _constellationDisplayPlot =
        new ConstellationDisplayPlot(1, ConstellationDisplayFrame);
    _numRealDataPoints = 1024;
    _realFFTDataPoints = new double[_numRealDataPoints];
    _averagedValues = new double[_numRealDataPoints];
    _historyVector = new std::vector<double*>;

    _timeDomainDisplayPlot->setLineLabel(0, "real");
    _timeDomainDisplayPlot->setLineLabel(1, "imag");

    AvgLineEdit->setRange(0, 500); // Set range of Average box value from 0 to 500
    minHoldCheckBox_toggled(false);
    maxHoldCheckBox_toggled(false);

#if QWT_VERSION < 0x060100
    WaterfallMaximumIntensitySlider->setRange(-200, 0);
    WaterfallMinimumIntensitySlider->setRange(-200, 0);
    WaterfallMinimumIntensitySlider->setValue(-200);
#else  /* QWT_VERSION < 0x060100 */
    WaterfallMaximumIntensitySlider->setScale(-200, 0);
    WaterfallMinimumIntensitySlider->setScale(-200, 0);
    WaterfallMinimumIntensitySlider->setValue(-200);
#endif /* QWT_VERSION < 0x060100 */

    WaterfallMaximumIntensitySlider->setOrientation(Qt::Horizontal);
    WaterfallMinimumIntensitySlider->setOrientation(Qt::Horizontal);

    _peakFrequency = 0;
    _peakAmplitude = -HUGE_VAL;

    _noiseFloorAmplitude = -HUGE_VAL;

    connect(_waterfallDisplayPlot,
            SIGNAL(updatedLowerIntensityLevel(const double)),
            _frequencyDisplayPlot,
            SLOT(setLowerIntensityLevel(const double)));
    connect(_waterfallDisplayPlot,
            SIGNAL(updatedUpperIntensityLevel(const double)),
            _frequencyDisplayPlot,
            SLOT(setUpperIntensityLevel(const double)));

    _frequencyDisplayPlot->setLowerIntensityLevel(-200);
    _frequencyDisplayPlot->setUpperIntensityLevel(-200);

    // Load up the acceptable FFT sizes...
    FFTSizeComboBox->clear();
    for (long fftSize = SpectrumGUIClass::MIN_FFT_SIZE;
         fftSize <= SpectrumGUIClass::MAX_FFT_SIZE;
         fftSize *= 2) {
        FFTSizeComboBox->insertItem(FFTSizeComboBox->count(), QString("%1").arg(fftSize));
    }
    reset();

    toggleTabFrequency(false);
    toggleTabWaterfall(false);
    toggleTabTime(false);
    toggleTabConstellation(false);

    _historyEntry = 0;
    _historyEntryCount = 0;

    // Create a timer to update plots at the specified rate
    displayTimer = new QTimer(this);
    connect(displayTimer, SIGNAL(timeout()), this, SLOT(updateGuiTimer()));

    // Connect double click signals up
    connect(_frequencyDisplayPlot,
            SIGNAL(plotPointSelected(const QPointF)),
            this,
            SLOT(onFFTPlotPointSelected(const QPointF)));

    connect(_waterfallDisplayPlot,
            SIGNAL(plotPointSelected(const QPointF)),
            this,
            SLOT(onWFallPlotPointSelected(const QPointF)));

    connect(_timeDomainDisplayPlot,
            SIGNAL(plotPointSelected(const QPointF)),
            this,
            SLOT(onTimePlotPointSelected(const QPointF)));

    connect(_constellationDisplayPlot,
            SIGNAL(plotPointSelected(const QPointF)),
            this,
            SLOT(onConstPlotPointSelected(const QPointF)));
}

SpectrumDisplayForm::~SpectrumDisplayForm()
{
    // Qt deletes children when parent is deleted

    // Don't worry about deleting Display Plots - they are deleted when parents are
    // deleted
    delete _intValidator;

    delete[] _realFFTDataPoints;
    delete[] _averagedValues;

    for (unsigned int count = 0; count < _historyVector->size(); count++) {
        delete[] _historyVector->operator[](count);
    }

    delete _historyVector;

    displayTimer->stop();
    delete displayTimer;
}

void SpectrumDisplayForm::setSystem(SpectrumGUIClass* newSystem,
                                    const uint64_t numFFTDataPoints,
                                    const uint64_t numTimeDomainDataPoints)
{
    resizeBuffers(numFFTDataPoints, numTimeDomainDataPoints);

    if (newSystem != NULL) {
        _system = newSystem;
        _systemSpecifiedFlag = true;
    } else {
        _systemSpecifiedFlag = false;
    }
}

/***********************************************************************
 * This is kind of gross because we're combining three operations:
 * Conversion from float to double (which is what the plotter wants)
 * Finding the peak and mean
 * Doing the "FFT shift" to put 0Hz at the center of the plot
 * I feel like this might want to be part of the sink block
 **********************************************************************/
static void fftshift_and_sum(double* outFFT,
                             const float* inFFT,
                             uint64_t num_points,
                             double& sum_mean,
                             double& peak_ampl,
                             int& peak_bin)
{
    const float* inptr = inFFT + num_points / 2;
    double* outptr = outFFT;

    sum_mean = 0;
    peak_ampl = -HUGE_VAL;
    peak_bin = 0;

    // Run this twice to perform the fftshift operation on the data here as well
    for (uint64_t point = 0; point < num_points / 2; point++) {
        float pt = (*inptr);
        *outptr = pt;
        if (*outptr > peak_ampl) {
            peak_bin = point;
            peak_ampl = *outptr;
        }
        sum_mean += *outptr;

        inptr++;
        outptr++;
    }

    // This loop takes the first half of the input data and puts it in the
    // second half of the plotted data
    inptr = inFFT;
    for (uint64_t point = 0; point < num_points / 2; point++) {
        float pt = (*inptr);
        *outptr = pt;
        if (*outptr > peak_ampl) {
            peak_bin = point;
            peak_ampl = *outptr;
        }
        sum_mean += *outptr;

        inptr++;
        outptr++;
    }
}

void SpectrumDisplayForm::newFrequencyData(const SpectrumUpdateEvent* spectrumUpdateEvent)
{
    //_lastSpectrumEvent = (SpectrumUpdateEvent)(*spectrumUpdateEvent);
    const float* fftMagDataPoints = spectrumUpdateEvent->getFFTPoints();
    const uint64_t numFFTDataPoints = spectrumUpdateEvent->getNumFFTDataPoints();
    const uint64_t numTimeDomainDataPoints =
        spectrumUpdateEvent->getNumTimeDomainDataPoints();
    const gr::high_res_timer_type dataTimestamp = spectrumUpdateEvent->getDataTimestamp();
    const bool repeatDataFlag = spectrumUpdateEvent->getRepeatDataFlag();
    const bool lastOfMultipleUpdatesFlag =
        spectrumUpdateEvent->getLastOfMultipleUpdateFlag();
    const gr::high_res_timer_type generatedTimestamp =
        spectrumUpdateEvent->getEventGeneratedTimestamp();
    double* realTimeDomainDataPoints =
        (double*)spectrumUpdateEvent->getRealTimeDomainPoints();
    double* imagTimeDomainDataPoints =
        (double*)spectrumUpdateEvent->getImagTimeDomainPoints();

    std::vector<double*> timeDomainDataPoints;
    timeDomainDataPoints.push_back(realTimeDomainDataPoints);
    timeDomainDataPoints.push_back(imagTimeDomainDataPoints);

    // REMEMBER: The dataTimestamp is NOT valid when the repeat data flag is true...
    resizeBuffers(numFFTDataPoints, numTimeDomainDataPoints);

    const double fftBinSize =
        (_stopFrequency - _startFrequency) / static_cast<double>(numFFTDataPoints);

    // this does the fftshift, conversion to double, and calculation of sum, peak
    // amplitude, peak freq.
    double sum_mean, peak_ampl;
    int peak_bin;
    fftshift_and_sum(_realFFTDataPoints,
                     fftMagDataPoints,
                     numFFTDataPoints,
                     sum_mean,
                     peak_ampl,
                     peak_bin);
    double peak_freq = peak_bin * fftBinSize;

    // Don't update the averaging history if this is repeated data
    if (!repeatDataFlag) {
        _averageHistory(_realFFTDataPoints);

        // Only use the local info if we are not repeating data
        _peakAmplitude = peak_ampl;
        _peakFrequency = peak_freq;

        // calculate the spectral mean
        // +20 because for the comparison below we only want to throw out bins
        // that are significantly higher (and would, thus, affect the mean more)
        const double meanAmplitude = (sum_mean / numFFTDataPoints) + 20.0;

        // now throw out any bins higher than the mean
        sum_mean = 0.0;
        uint64_t newNumDataPoints = numFFTDataPoints;
        for (uint64_t number = 0; number < numFFTDataPoints; number++) {
            if (_realFFTDataPoints[number] <= meanAmplitude)
                sum_mean += _realFFTDataPoints[number];
            else
                newNumDataPoints--;
        }

        if (newNumDataPoints == 0)                // in the odd case that all
            _noiseFloorAmplitude = meanAmplitude; // amplitudes are equal!
        else
            _noiseFloorAmplitude = sum_mean / newNumDataPoints;
    }

    if (lastOfMultipleUpdatesFlag) {
        int tabindex = SpectrumTypeTab->currentIndex();
        if (tabindex == d_plot_fft) {
            _frequencyDisplayPlot->plotNewData(_averagedValues,
                                               numFFTDataPoints,
                                               _noiseFloorAmplitude,
                                               _peakFrequency,
                                               _peakAmplitude,
                                               d_update_time);
        }
        if (tabindex == d_plot_time) {
            _timeDomainDisplayPlot->plotNewData(
                timeDomainDataPoints, numTimeDomainDataPoints, d_update_time);
        }
        if (tabindex == d_plot_constellation) {
            _constellationDisplayPlot->plotNewData(realTimeDomainDataPoints,
                                                   imagTimeDomainDataPoints,
                                                   numTimeDomainDataPoints,
                                                   d_update_time);
        }

        // Don't update the repeated data for the waterfall
        if (!repeatDataFlag) {
            if (tabindex == d_plot_waterfall) {
                _waterfallDisplayPlot->plotNewData(
                    _realFFTDataPoints,
                    numFFTDataPoints,
                    d_update_time,
                    dataTimestamp,
                    spectrumUpdateEvent->getDroppedFFTFrames());
            }
        }

        // Tell the system the GUI has been updated
        if (_systemSpecifiedFlag) {
            _system->setLastGUIUpdateTime(generatedTimestamp);
            _system->decrementPendingGUIUpdateEvents();
        }
    }
}

void SpectrumDisplayForm::resizeEvent(QResizeEvent* e)
{
    QSize s;
    s.setWidth(e->size().width() - 40);
    s.setHeight(e->size().height() - 160);
    TimeDomainDisplayFrame->resize(s);
    ConstellationDisplayFrame->resize(s);
    s.setHeight(e->size().height() - 205);
    FrequencyPlotDisplayFrame->resize(s);
    s.setHeight(e->size().height() - 240);
    WaterfallPlotDisplayFrame->resize(s);

    s.setWidth(FrequencyPlotDisplayFrame->width());
    s.setHeight(FrequencyPlotDisplayFrame->height());
    emit _frequencyDisplayPlot->resizeSlot(&s);

    s.setWidth(TimeDomainDisplayFrame->width());
    s.setHeight(TimeDomainDisplayFrame->height());
    emit _timeDomainDisplayPlot->resizeSlot(&s);

    s.setWidth(WaterfallPlotDisplayFrame->width());
    s.setHeight(WaterfallPlotDisplayFrame->height());
    emit _waterfallDisplayPlot->resizeSlot(&s);

    s.setWidth(ConstellationDisplayFrame->width());
    s.setHeight(ConstellationDisplayFrame->height());
    emit _constellationDisplayPlot->resizeSlot(&s);
}

void SpectrumDisplayForm::customEvent(QEvent* e)
{
    if (e->type() == QEvent::User + 3) {
        if (_systemSpecifiedFlag) {
            WindowComboBox->setCurrentIndex(_system->getWindowType());
            FFTSizeComboBox->setCurrentIndex(_system->getFFTSizeIndex());
        }

        waterfallMinimumIntensityChangedCB(WaterfallMinimumIntensitySlider->value());
        waterfallMaximumIntensityChangedCB(WaterfallMaximumIntensitySlider->value());

        // Clear any previous display
        reset();
    } else if (e->type() == SpectrumUpdateEventType) {
        SpectrumUpdateEvent* spectrumUpdateEvent = (SpectrumUpdateEvent*)e;
        newFrequencyData(spectrumUpdateEvent);
    } else if (e->type() == SpectrumWindowCaptionEventType) {
        setWindowTitle(((SpectrumWindowCaptionEvent*)e)->getLabel());
    } else if (e->type() == SpectrumWindowResetEventType) {
        reset();
        if (_systemSpecifiedFlag) {
            _system->resetPendingGUIUpdateEvents();
        }
    } else if (e->type() == SpectrumFrequencyRangeEventType) {
        _startFrequency = ((SpectrumFrequencyRangeEvent*)e)->GetStartFrequency();
        _stopFrequency = ((SpectrumFrequencyRangeEvent*)e)->GetStopFrequency();
        _centerFrequency = ((SpectrumFrequencyRangeEvent*)e)->GetCenterFrequency();

        useRFFrequenciesCB(UseRFFrequenciesCheckBox->isChecked());
    }
}

void SpectrumDisplayForm::updateGuiTimer()
{
    // This is called by the displayTimer and redraws the canvases of
    // all of the plots.
    _frequencyDisplayPlot->canvas()->update();
    _waterfallDisplayPlot->canvas()->update();
    _timeDomainDisplayPlot->canvas()->update();
    _constellationDisplayPlot->canvas()->update();
}


void SpectrumDisplayForm::avgLineEdit_valueChanged(int value) { setAverageCount(value); }


void SpectrumDisplayForm::maxHoldCheckBox_toggled(bool newState)
{
    MaxHoldResetBtn->setEnabled(newState);
    _frequencyDisplayPlot->setMaxFFTVisible(newState);
    maxHoldResetBtn_clicked();
}


void SpectrumDisplayForm::minHoldCheckBox_toggled(bool newState)
{
    MinHoldResetBtn->setEnabled(newState);
    _frequencyDisplayPlot->setMinFFTVisible(newState);
    minHoldResetBtn_clicked();
}


void SpectrumDisplayForm::minHoldResetBtn_clicked()
{
    _frequencyDisplayPlot->clearMinData();
    _frequencyDisplayPlot->replot();
}


void SpectrumDisplayForm::maxHoldResetBtn_clicked()
{
    _frequencyDisplayPlot->clearMaxData();
    _frequencyDisplayPlot->replot();
}


void SpectrumDisplayForm::tabChanged(int index) { update(); }

void SpectrumDisplayForm::setFrequencyRange(const double newCenterFrequency,
                                            const double newStartFrequency,
                                            const double newStopFrequency)
{
    double fcenter;
    if (UseRFFrequenciesCheckBox->isChecked()) {
        fcenter = newCenterFrequency;
    } else {
        fcenter = 0;
    }

    double fdiff = std::max(fabs(newStartFrequency), fabs(newStopFrequency));
    if (fdiff > 0) {
        std::string strunits[4] = { "Hz", "kHz", "MHz", "GHz" };
        std::string strtime[4] = { "sec", "ms", "us", "ns" };
        double units10 = floor(log10(fdiff));
        double units3 = std::max(floor(units10 / 3.0), 0.0);
        d_units = pow(10, (units10 - fmod(units10, 3.0)));
        int iunit = static_cast<int>(units3);

        _startFrequency = newStartFrequency;
        _stopFrequency = newStopFrequency;
        _centerFrequency = newCenterFrequency;

        _frequencyDisplayPlot->setFrequencyRange(
            fcenter, fdiff, d_units, strunits[iunit]);
        _waterfallDisplayPlot->setFrequencyRange(
            fcenter, fdiff, d_units, strunits[iunit]);
        _timeDomainDisplayPlot->setSampleRate(
            (_stopFrequency - _startFrequency) / 2.0, d_units, strtime[iunit]);
    }
}

int SpectrumDisplayForm::getAverageCount() { return _historyVector->size(); }

void SpectrumDisplayForm::setAverageCount(const int newCount)
{
    if (newCount > -1) {
        if (newCount != static_cast<int>(_historyVector->size())) {
            std::vector<double*>::iterator pos;
            while (newCount < static_cast<int>(_historyVector->size())) {
                pos = _historyVector->begin();
                delete[](*pos);
                _historyVector->erase(pos);
            }

            while (newCount > static_cast<int>(_historyVector->size())) {
                _historyVector->push_back(new double[_numRealDataPoints]);
            }
            averageDataReset();
        }
    }
}

void SpectrumDisplayForm::_averageHistory(const double* newBuffer)
{
    if (_numRealDataPoints > 0) {
        if (!_historyVector->empty()) {
            memcpy(_historyVector->operator[](_historyEntry),
                   newBuffer,
                   _numRealDataPoints * sizeof(double));

            // Increment the next location to store data
            _historyEntryCount++;
            if (_historyEntryCount > static_cast<int>(_historyVector->size())) {
                _historyEntryCount = _historyVector->size();
            }
            _historyEntry += 1;
            _historyEntry = _historyEntry % _historyVector->size();

            // Total up and then average the values
            double sum;
            for (uint64_t location = 0; location < _numRealDataPoints; location++) {
                sum = 0;
                for (int number = 0; number < _historyEntryCount; number++) {
                    sum += _historyVector->operator[](number)[location];
                }
                _averagedValues[location] = sum / static_cast<double>(_historyEntryCount);
            }
        } else {
            memcpy(_averagedValues, newBuffer, _numRealDataPoints * sizeof(double));
        }
    }
}

void SpectrumDisplayForm::resizeBuffers(const uint64_t numFFTDataPoints,
                                        const uint64_t /*numTimeDomainDataPoints*/)
{
    // Convert from Complex to Real for certain Displays
    if (_numRealDataPoints != numFFTDataPoints) {
        _numRealDataPoints = numFFTDataPoints;
        delete[] _realFFTDataPoints;
        delete[] _averagedValues;

        _realFFTDataPoints = new double[_numRealDataPoints];
        _averagedValues = new double[_numRealDataPoints];
        memset(_realFFTDataPoints, 0x0, _numRealDataPoints * sizeof(double));

        const int historySize = _historyVector->size();
        setAverageCount(0); // Clear the existing history
        setAverageCount(historySize);

        reset();
    }
}

void SpectrumDisplayForm::reset()
{
    averageDataReset();

    _waterfallDisplayPlot->resetAxis();
}


void SpectrumDisplayForm::averageDataReset()
{
    _historyEntry = 0;
    _historyEntryCount = 0;

    memset(_averagedValues, 0x0, _numRealDataPoints * sizeof(double));

    maxHoldResetBtn_clicked();
    minHoldResetBtn_clicked();
}


void SpectrumDisplayForm::closeEvent(QCloseEvent* e)
{
    if (_systemSpecifiedFlag) {
        _system->setWindowOpenFlag(false);
    }

    qApp->processEvents();

    QWidget::closeEvent(e); // equivalent to e->accept()
}


void SpectrumDisplayForm::windowTypeChanged(int newItem)
{
    if (_systemSpecifiedFlag) {
        _system->setWindowType(newItem);
    }
}


void SpectrumDisplayForm::useRFFrequenciesCB(bool useRFFlag)
{
    setFrequencyRange(_centerFrequency, _startFrequency, _stopFrequency);
}

void SpectrumDisplayForm::toggleRFFrequencies(bool en)
{
    UseRFFrequenciesCheckBox->setChecked(en);
}


void SpectrumDisplayForm::waterfallMaximumIntensityChangedCB(double newValue)
{
    if (newValue > WaterfallMinimumIntensitySlider->value()) {
        WaterfallMaximumIntensityLabel->setText(
            QString("%1 dB").arg(newValue, 0, 'f', 0));
    } else {
        WaterfallMinimumIntensitySlider->setValue(newValue - 2);
    }

    _waterfallDisplayPlot->setIntensityRange(WaterfallMinimumIntensitySlider->value(),
                                             WaterfallMaximumIntensitySlider->value());
}


void SpectrumDisplayForm::waterfallMinimumIntensityChangedCB(double newValue)
{
    if (newValue < WaterfallMaximumIntensitySlider->value()) {
        WaterfallMinimumIntensityLabel->setText(
            QString("%1 dB").arg(newValue, 0, 'f', 0));
    } else {
        WaterfallMaximumIntensitySlider->setValue(newValue + 2);
    }

    _waterfallDisplayPlot->setIntensityRange(WaterfallMinimumIntensitySlider->value(),
                                             WaterfallMaximumIntensitySlider->value());
}

void SpectrumDisplayForm::fftComboBoxSelectedCB(const QString& fftSizeString)
{
    if (_systemSpecifiedFlag) {
        _system->setFFTSize(fftSizeString.toLong());
    }
}


void SpectrumDisplayForm::waterfallAutoScaleBtnCB()
{
    double minimumIntensity = _noiseFloorAmplitude - 5;
    double maximumIntensity = _peakAmplitude + 10;

#if QWT_VERSION < 0x060100
    if (minimumIntensity < WaterfallMinimumIntensitySlider->minValue()) {
        minimumIntensity = WaterfallMinimumIntensitySlider->minValue();
    }
    WaterfallMinimumIntensitySlider->setValue(minimumIntensity);
    if (maximumIntensity > WaterfallMaximumIntensitySlider->maxValue()) {
        maximumIntensity = WaterfallMaximumIntensitySlider->maxValue();
    }
#else  /* QWT_VERSION < 0x060100 */
    if (minimumIntensity < WaterfallMinimumIntensitySlider->lowerBound()) {
        minimumIntensity = WaterfallMinimumIntensitySlider->lowerBound();
    }
    WaterfallMinimumIntensitySlider->setValue(minimumIntensity);
    if (maximumIntensity > WaterfallMaximumIntensitySlider->upperBound()) {
        maximumIntensity = WaterfallMaximumIntensitySlider->upperBound();
    }
#endif /* QWT_VERSION < 0x060100 */

    WaterfallMaximumIntensitySlider->setValue(maximumIntensity);
    waterfallMaximumIntensityChangedCB(maximumIntensity);
}

void SpectrumDisplayForm::waterfallIntensityColorTypeChanged(int newType)
{
    QColor lowIntensityColor;
    QColor highIntensityColor;
    if (newType == gr::qtgui::INTENSITY_COLOR_MAP_TYPE_USER_DEFINED) {
        // Select the Low Intensity Color
        lowIntensityColor = _waterfallDisplayPlot->getUserDefinedLowIntensityColor();
        if (!lowIntensityColor.isValid()) {
            lowIntensityColor = Qt::black;
        }
        QMessageBox::information(this,
                                 "Low Intensity Color Selection",
                                 "In the next window, select the low intensity color for "
                                 "the waterfall display",
                                 QMessageBox::Ok);
        lowIntensityColor = QColorDialog::getColor(lowIntensityColor, this);

        // Select the High Intensity Color
        highIntensityColor = _waterfallDisplayPlot->getUserDefinedHighIntensityColor();
        if (!highIntensityColor.isValid()) {
            highIntensityColor = Qt::white;
        }
        QMessageBox::information(this,
                                 "High Intensity Color Selection",
                                 "In the next window, select the high intensity color "
                                 "for the waterfall display",
                                 QMessageBox::Ok);
        highIntensityColor = QColorDialog::getColor(highIntensityColor, this);
    }

    _waterfallDisplayPlot->setIntensityColorMapType(
        0, newType, lowIntensityColor, highIntensityColor);
}

void SpectrumDisplayForm::toggleTabFrequency(const bool state)
{
    if (state == true) {
        if (d_plot_fft == -1) {
            SpectrumTypeTab->addTab(FrequencyPage, "Frequency Display");
            d_plot_fft = SpectrumTypeTab->count() - 1;
        }
    } else {
        SpectrumTypeTab->removeTab(SpectrumTypeTab->indexOf(FrequencyPage));
        d_plot_fft = -1;
    }
}

void SpectrumDisplayForm::toggleTabWaterfall(const bool state)
{
    if (state == true) {
        if (d_plot_waterfall == -1) {
            SpectrumTypeTab->addTab(WaterfallPage, "Waterfall Display");
            d_plot_waterfall = SpectrumTypeTab->count() - 1;
        }
    } else {
        SpectrumTypeTab->removeTab(SpectrumTypeTab->indexOf(WaterfallPage));
        d_plot_waterfall = -1;
    }
}

void SpectrumDisplayForm::toggleTabTime(const bool state)
{
    if (state == true) {
        if (d_plot_time == -1) {
            SpectrumTypeTab->addTab(TimeDomainPage, "Time Domain Display");
            d_plot_time = SpectrumTypeTab->count() - 1;
        }
    } else {
        SpectrumTypeTab->removeTab(SpectrumTypeTab->indexOf(TimeDomainPage));
        d_plot_time = -1;
    }
}

void SpectrumDisplayForm::toggleTabConstellation(const bool state)
{
    if (state == true) {
        if (d_plot_constellation == -1) {
            SpectrumTypeTab->addTab(ConstellationPage, "Constellation Display");
            d_plot_constellation = SpectrumTypeTab->count() - 1;
        }
    } else {
        SpectrumTypeTab->removeTab(SpectrumTypeTab->indexOf(ConstellationPage));
        d_plot_constellation = -1;
    }
}


void SpectrumDisplayForm::setTimeDomainAxis(double min, double max)
{
    _timeDomainDisplayPlot->setYaxis(min, max);
}

void SpectrumDisplayForm::setConstellationAxis(double xmin,
                                               double xmax,
                                               double ymin,
                                               double ymax)
{
    _constellationDisplayPlot->set_axis(xmin, xmax, ymin, ymax);
}

void SpectrumDisplayForm::setConstellationPenSize(int size)
{
    _constellationDisplayPlot->set_pen_size(size);
}

void SpectrumDisplayForm::setFrequencyAxis(double min, double max)
{
    _frequencyDisplayPlot->setYaxis(min, max);
}

void SpectrumDisplayForm::setUpdateTime(double t)
{
    d_update_time = t;
    // QTimer class takes millisecond input
    displayTimer->start(d_update_time * 1000);
}

void SpectrumDisplayForm::onFFTPlotPointSelected(const QPointF p)
{
    d_clicked = true;
    d_clicked_freq = d_units * p.x();

    setFrequencyRange(d_clicked_freq, _startFrequency, _stopFrequency);
}

void SpectrumDisplayForm::onWFallPlotPointSelected(const QPointF p)
{
    d_clicked = true;
    d_clicked_freq = d_units * p.x();

    setFrequencyRange(d_clicked_freq, _startFrequency, _stopFrequency);
}

void SpectrumDisplayForm::onTimePlotPointSelected(const QPointF p)
{
    // emit plotPointSelected(p, 3);
}

void SpectrumDisplayForm::onConstPlotPointSelected(const QPointF p)
{
    // emit plotPointSelected(p, 4);
}

bool SpectrumDisplayForm::checkClicked()
{
    if (d_clicked) {
        d_clicked = false;
        return true;
    } else {
        return false;
    }
}

float SpectrumDisplayForm::getClickedFreq() const { return d_clicked_freq; }
