/// @file polarplotwidget.cpp
/// @brief Construct polar plot widget with default configuration
/// (implementation)
/// @author Andrés Martínez Mera - andresmmera@protonmail.com
/// @date Jan 4, 2026
/// @copyright Copyright (C) 2026 Andrés Martínez Mera
/// @license GPL-3.0-or-later

#include "polarplotwidget.h"
#include <QDebug>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <cmath>

PolarPlotWidget::PolarPlotWidget(QWidget *parent)
    : QWidget(parent), fMin(1e20), fMax(-1) {
  // Initialize the QCustomPlot widget
  plot = new QCustomPlot(this);
  plot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom);

  // Clear default plot layout and set up polar plot
  plot->plotLayout()->clear();

  // Create angular axis and add to layout
  angularAxis = new QCPPolarAxisAngular(plot);
  plot->plotLayout()->addElement(0, 0, angularAxis);

  // Get the radial axis from angular axis
  radialAxis = angularAxis->radialAxis();

  // Configure angular axis (angle lines)
  angularAxis->setRange(0, 360);
  angularAxis->setRangeDrag(false);
  angularAxis->setTickLabelMode(QCPPolarAxisAngular::lmUpright);

  // Configure radial axis (concentric circles)
  radialAxis->setRange(0, 1.0);
  radialAxis->setTickLabelMode(QCPPolarAxisRadial::lmUpright);
  radialAxis->setTickLabelRotation(0);
  radialAxis->setAngle(45);

  // Configure grid
  angularAxis->grid()->setAngularPen(
      QPen(QColor(200, 200, 200), 1, Qt::SolidLine));
  angularAxis->grid()->setRadialPen(
      QPen(QColor(200, 200, 200), 1, Qt::SolidLine));
  angularAxis->grid()->setSubGridType(QCPPolarGrid::gtAll);

  // Set up the frequency units
  frequencyUnits << "Hz" << "kHz" << "MHz" << "GHz";

  // Clear containers
  markerItems.clear();
  markerLabels.clear();
  traceGraphs.clear();

  // Create the main layout
  QVBoxLayout *mainLayout = new QVBoxLayout(this);
  mainLayout->addWidget(plot);

  // Add the axis settings layout
  mainLayout->addLayout(setupAxisSettings());
  setLayout(mainLayout);

  // Configure radial axis
  radialAxis->setRange(0, 1.0);
  radialAxis->setTickLabelMode(QCPPolarAxisRadial::lmUpright);
  radialAxis->setTickLabelRotation(0);
  radialAxis->setAngle(45);

  // Connect range change signals to update the widgets
  connect(plot, &QCustomPlot::mouseMove, this,
          static_cast<void (PolarPlotWidget::*)(QMouseEvent *)>(
              &PolarPlotWidget::checkAxisRanges));
  connect(plot, &QCustomPlot::mouseRelease, this,
          static_cast<void (PolarPlotWidget::*)(QMouseEvent *)>(
              &PolarPlotWidget::checkAxisRanges));
  connect(plot, &QCustomPlot::afterReplot, this,
          static_cast<void (PolarPlotWidget::*)()>(
              &PolarPlotWidget::checkAxisRanges));
  connect(radialAxis,
          QOverload<const QCPRange &>::of(&QCPPolarAxisRadial::rangeChanged),
          this, &PolarPlotWidget::onRadialRangeChanged);

  // Initial plot update
  plot->replot();
}

void PolarPlotWidget::addTrace(const QString &name, const Trace &trace) {
  traces[name] = trace;
  updateFrequencyRange(); // Update frequency range based on new trace
  updatePlot();
}

void PolarPlotWidget::updateFrequencyRange() {
  fMin = 1e20;
  fMax = -1;

  const auto &constTraces = traces; // const view, no detach (no warning)
  for (const auto &trace : constTraces) {
    if (!trace.frequencies.isEmpty()) {
      double traceMinFreq = trace.frequencies.first();
      double traceMaxFreq = trace.frequencies.last();

      if (traceMinFreq < fMin) {
        fMin = traceMinFreq;
      }
      if (traceMaxFreq > fMax) {
        fMax = traceMaxFreq;
      }
    }
  }

  // Update spin box values
  double freqMultiplier = getFrequencyMultiplier();
  fMinSpinBox->setValue(fMin / freqMultiplier);
  fMinSpinBox->setMinimum(fMin / freqMultiplier);

  fMaxSpinBox->setValue(fMax / freqMultiplier);
  fMaxSpinBox->setMaximum(fMax / freqMultiplier);
}

void PolarPlotWidget::removeTrace(const QString &name) {
  traces.remove(name);

  // Remove associated polar graphs if they exist
  if (traceGraphs.contains(name)) {
    for (QCPPolarGraph *graph : traceGraphs[name]) {
      angularAxis->removeGraph(graph);
    }
    traceGraphs.remove(name);
  }
  updatePlot();
}

void PolarPlotWidget::clearTraces() {
  traces.clear();

  // Clear all polar graphs
  for (auto &graphList : traceGraphs) {
    for (QCPPolarGraph *graph : graphList) {
      angularAxis->removeGraph(graph);
    }
  }
  traceGraphs.clear();

  updatePlot();
}

QPen PolarPlotWidget::getTracePen(const QString &traceName) const {
  if (traces.contains(traceName)) {
    return traces[traceName].pen;
  }
  return QPen();
}

void PolarPlotWidget::setTracePen(const QString &traceName, const QPen &pen) {
  if (traces.contains(traceName)) {
    traces[traceName].pen = pen;
    updatePlot();
  }
}

QMap<QString, QPen> PolarPlotWidget::getTracesInfo() const {
  QMap<QString, QPen> penMap;
  for (auto it = traces.constBegin(); it != traces.constEnd(); ++it) {
    penMap.insert(it.key(), it.value().pen);
  }
  return penMap;
}

bool PolarPlotWidget::addMarker(const QString &markerId, double frequency,
                                const QPen &pen) {
  if (markers.contains(markerId)) {
    return false;
  }

  bool frequencyInRange = false;
  for (auto it = traces.constBegin(); it != traces.constEnd(); ++it) {
    const Trace &trace = it.value();
    if (!trace.frequencies.isEmpty() &&
        frequency >= trace.frequencies.first() &&
        frequency <= trace.frequencies.last()) {
      frequencyInRange = true;
      break;
    }
  }

  if (!frequencyInRange) {
    return false;
  }

  Marker marker;
  marker.id = markerId;
  marker.frequency = frequency;
  marker.pen = pen;

  markers.insert(markerId, marker);
  updatePlot();
  return true;
}

bool PolarPlotWidget::removeMarker(const QString &markerId) {
  if (!markers.contains(markerId)) {
    return false;
  }

  markers.remove(markerId);
  updatePlot();
  return true;
}

QMap<QString, double> PolarPlotWidget::getMarkers() const {
  QMap<QString, double> markerFrequencies;
  for (auto it = markers.constBegin(); it != markers.constEnd(); ++it) {
    markerFrequencies.insert(it.key(), it.value().frequency);
  }
  return markerFrequencies;
}

void PolarPlotWidget::updatePlot() {
  clearGraphicsItems();

  // Remove existing polar graphs
  for (auto &graphList : traceGraphs) {
    for (QCPPolarGraph *graph : graphList) {
      angularAxis->removeGraph(graph);
    }
  }
  traceGraphs.clear();

  const double PHASE_WRAP_THRESHOLD = 180.0; // Degrees

  for (auto it = traces.constBegin(); it != traces.constEnd(); ++it) {
    const QString &name = it.key();
    const Trace &trace = it.value();

    QList<QCPPolarGraph *> graphsForTrace;

    // Create initial polar graph for this trace
    QCPPolarGraph *currentGraph = new QCPPolarGraph(angularAxis, radialAxis);
    currentGraph->setPen(trace.pen);
    currentGraph->setName(name);
    graphsForTrace.append(currentGraph);

    double prevPhase = -1e3; // Initialize with impossible value

    for (int i = 0; i < trace.values.size() && i < trace.frequencies.size();
         ++i) {
      double frequency = trace.frequencies[i];
      if (frequency >= fMin && frequency <= fMax) {
        std::complex<double> value = trace.values[i];
        double magnitude = std::abs(value);
        double phase = std::arg(value) * 180.0 / M_PI;
        if (phase < 0) {
          phase += 360;
        }

        // Check for phase wrap (only after first point)
        if (prevPhase != -1e3 &&
            std::abs(phase - prevPhase) > PHASE_WRAP_THRESHOLD) {
          // Create new polar graph for next segment
          currentGraph = new QCPPolarGraph(angularAxis, radialAxis);
          currentGraph->setPen(trace.pen);
          currentGraph->setName(name);
          graphsForTrace.append(currentGraph);
        }

        // Add data point to current graph
        currentGraph->addData(phase, magnitude);
        prevPhase = phase;
      }
    }

    // Store all graphs for this trace
    traceGraphs[name] = graphsForTrace;
  }

  drawCustomMarkers();
  plot->replot();
}

void PolarPlotWidget::updateRAxis() {
  double rMin =
      qMax(0.0, rAxisMin->value()); // Ensure minimum is not less than 0
  double rMax = rAxisMax->value();

  // Ensure max is greater than min
  if (rMax <= rMin) {
    rMax = rMin + 0.1;
    rAxisMax->blockSignals(true);
    rAxisMax->setValue(rMax);
    rAxisMax->blockSignals(false);
  }

  // Update the spin box if we had to correct the minimum
  if (rMin != rAxisMin->value()) {
    rAxisMin->blockSignals(true);
    rAxisMin->setValue(rMin);
    rAxisMin->blockSignals(false);
  }

  radialAxis->setRange(rMin, rMax);
  updatePlot();
}

bool PolarPlotWidget::updateMarkerFrequency(const QString &markerId,
                                            double newFrequency) {
  // Check if marker exists
  if (!markers.contains(markerId)) {
    return false;
  }

  // Check if any trace contains this frequency
  bool frequencyInRange = false;
  for (auto it = traces.constBegin(); it != traces.constEnd(); ++it) {
    const Trace &trace = it.value();
    if (!trace.frequencies.isEmpty() &&
        newFrequency >= trace.frequencies.first() &&
        newFrequency <= trace.frequencies.last()) {
      frequencyInRange = true;
      break;
    }
  }

  if (!frequencyInRange) {
    return false;
  }

  // Update the marker's frequency
  markers[markerId].frequency = newFrequency;

  // Trigger repaint
  updatePlot();
  return true;
}

void PolarPlotWidget::clearGraphicsItems() {
  // Remove all marker items
  const auto &items =
      markerItems; // const view, no detach (otherwise -> warning)
  for (QCPItemEllipse *item : items) {
    plot->removeItem(item);
  }
  markerItems.clear();

  // Remove all marker labels
  const auto &labels =
      markerLabels; // const view, no detach (otherwise -> warning)
  for (QCPItemText *label : labels) {
    plot->removeItem(label);
  }
  markerLabels.clear();
}

QGridLayout *PolarPlotWidget::setupAxisSettings() {
  QGridLayout *axisLayout = new QGridLayout();

  fMin = 1e20;
  fMax = -1;

  QLabel *FreqLabel = new QLabel("<b>Frequency</b>");
  axisLayout->addWidget(FreqLabel, 0, 0);

  // Create frequency controls
  fMinSpinBox = new CustomDoubleSpinBox;
  fMinSpinBox->setRange(0, 1e12);
  fMinSpinBox->setSingleStep(10);
  fMinSpinBox->setToolTip("Minimum frequency");
  connect(fMinSpinBox,
          QOverload<double>::of(&CustomDoubleSpinBox::valueChanged), this,
          &PolarPlotWidget::onFMinChanged);
  axisLayout->addWidget(fMinSpinBox, 0, 1);

  fMaxSpinBox = new CustomDoubleSpinBox;
  fMaxSpinBox->setRange(0, 1e12);
  fMaxSpinBox->setSingleStep(10);
  fMaxSpinBox->setToolTip("Maximum frequency");
  connect(fMaxSpinBox,
          QOverload<double>::of(&CustomDoubleSpinBox::valueChanged), this,
          &PolarPlotWidget::onFMaxChanged);
  axisLayout->addWidget(fMaxSpinBox, 0, 2);

  fUnitComboBox = new QComboBox;
  fUnitComboBox->addItems(frequencyUnits);
  fUnitComboBox->setCurrentIndex(2); // MHz
  connect(fUnitComboBox, &QComboBox::currentIndexChanged, this,
          &PolarPlotWidget::onFUnitChanged);

  axisLayout->addWidget(fUnitComboBox, 0, 3);

  // Radius axis settings
  QLabel *rAxisLabel = new QLabel("<b>Radius</b>");
  axisLayout->addWidget(rAxisLabel, 1, 0);

  rAxisMin = new CustomDoubleSpinBox();
  rAxisMin->setMinimum(0.0); // Ensure minimum cannot be set below 0
  rAxisMin->setMaximum(1000000);
  rAxisMin->setValue(0.0);
  rAxisMin->setDecimals(2);
  rAxisMin->setSingleStep(0.1);
  rAxisMin->setToolTip("Minimum radius");
  connect(rAxisMin, QOverload<double>::of(&CustomDoubleSpinBox::valueChanged),
          this, &PolarPlotWidget::updateRAxis);
  axisLayout->addWidget(rAxisMin, 1, 1);

  rAxisMax = new CustomDoubleSpinBox();
  rAxisMax->setMinimum(0.1); // Ensure maximum is always positive
  rAxisMax->setMaximum(1000000);
  rAxisMax->setValue(1.0);
  rAxisMax->setDecimals(2);
  rAxisMax->setSingleStep(0.1);
  rAxisMax->setToolTip("Maximum radius");
  connect(rAxisMax, QOverload<double>::of(&CustomDoubleSpinBox::valueChanged),
          this, &PolarPlotWidget::updateRAxis);
  axisLayout->addWidget(rAxisMax, 1, 2);

  rAxisDiv = new CustomDoubleSpinBox();
  rAxisDiv->setMinimum(0.1);
  rAxisDiv->setMaximum(1000000);
  rAxisDiv->setValue(0.2);
  rAxisDiv->setDecimals(2);
  rAxisDiv->setSingleStep(0.1);
  rAxisDiv->setToolTip("Radius step");
  connect(rAxisDiv, QOverload<double>::of(&CustomDoubleSpinBox::valueChanged),
          this, &PolarPlotWidget::updateRAxis);
  axisLayout->addWidget(rAxisDiv, 1, 3);

  // Display mode
  QLabel *modeLabel = new QLabel("<b>Marker format</b>");
  axisLayout->addWidget(modeLabel, 2, 0);

  displayModeCombo = new QComboBox();
  displayModeCombo->addItem("Magnitude/Phase");
  displayModeCombo->addItem("Real/Imaginary");
  connect(displayModeCombo, &QComboBox::currentIndexChanged, this,
          &PolarPlotWidget::toggleDisplayMode);

  axisLayout->addWidget(displayModeCombo, 2, 1, 1, 2);

  return axisLayout;
}

std::complex<double>
PolarPlotWidget::getComplexValueAtFrequency(const Trace &trace,
                                            double frequency) {
  // Find the closest frequency points in the trace
  int lowerIndex = -1;
  for (int i = 0; i < trace.frequencies.size() - 1; ++i) {
    if (trace.frequencies[i] <= frequency &&
        frequency <= trace.frequencies[i + 1]) {
      lowerIndex = i;
      break;
    }
  }

  // If we found an interval containing the marker frequency
  if (lowerIndex >= 0) {
    // Linear interpolation to find the complex value at marker frequency
    double f1 = trace.frequencies[lowerIndex];
    double f2 = trace.frequencies[lowerIndex + 1];
    std::complex<double> v1 = trace.values[lowerIndex];
    std::complex<double> v2 = trace.values[lowerIndex + 1];

    // Linear interpolation formula applied to both real and imaginary parts
    double t = (frequency - f1) / (f2 - f1);
    double real = v1.real() + t * (v2.real() - v1.real());
    double imag = v1.imag() + t * (v2.imag() - v1.imag());

    return std::complex<double>(real, imag);
  }

  // If frequency is exactly at the first or last point
  if (std::abs(frequency - trace.frequencies.first()) < 1e-10 &&
      !trace.values.isEmpty()) {
    return trace.values.first();
  }
  if (std::abs(frequency - trace.frequencies.last()) < 1e-10 &&
      !trace.values.isEmpty()) {
    return trace.values.last();
  }

  // Return zero if not found (shouldn't happen if checks are proper)
  return std::complex<double>(0, 0);
}

void PolarPlotWidget::drawCustomMarkers() {
  if (markers.isEmpty() || traces.isEmpty()) {
    return;
  }

  // Clear existing marker graphics items
  clearGraphicsItems();

  // Get the current radial range for better positioning
  double rMin = radialAxis->range().lower;
  double rMax = radialAxis->range().upper;

  // Iterate through each trace
  for (auto traceIt = traces.constBegin(); traceIt != traces.constEnd();
       ++traceIt) {
    const QString &traceName = traceIt.key();
    const Trace &trace = traceIt.value();

    // Skip traces with no frequency data
    if (trace.frequencies.isEmpty() || trace.values.isEmpty()) {
      continue;
    }

    // Draw markers for this trace
    for (auto markerIt = markers.constBegin(); markerIt != markers.constEnd();
         ++markerIt) {
      const QString &markerId = markerIt.key();
      const Marker &marker = markerIt.value();
      double markerFreq = marker.frequency;

      // Check if marker frequency is within the trace frequency range
      if (markerFreq < trace.frequencies.first() ||
          markerFreq > trace.frequencies.last()) {
        continue;
      }

      // Get interpolated complex value at marker frequency
      std::complex<double> value =
          getComplexValueAtFrequency(trace, markerFreq);

      // Convert to display format based on current mode
      double angle, radius;
      int displayMode = displayModeCombo->currentIndex();

      if (displayMode == 0) {
        // Magnitude/Phase mode
        radius = std::abs(value);
        angle = std::arg(value) * 180.0 / M_PI;
        if (angle < 0) {
          angle += 360;
        }
      } else {
        // Real/Imaginary mode
        angle = std::atan2(value.imag(), value.real()) * 180.0 / M_PI;
        if (angle < 0) {
          angle += 360;
        }
        radius = std::sqrt(value.real() * value.real() +
                           value.imag() * value.imag());
      }

      // Use QCustomPlot's built-in coordinate transformation for polar plots
      // coordToPixel for polar axes takes both angle and radius
      QPointF pixelPos = angularAxis->coordToPixel(angle, radius);
      double xPixel = pixelPos.x();
      double yPixel = pixelPos.y();

      // Create a marker point using QCPItemEllipse
      QCPItemEllipse *markerPoint = new QCPItemEllipse(plot);

      // Set marker point size in pixels
      double markerSize = 3.0; // pixels

      markerPoint->topLeft->setType(QCPItemPosition::ptAbsolute);
      markerPoint->topLeft->setCoords(xPixel - markerSize, yPixel - markerSize);

      markerPoint->bottomRight->setType(QCPItemPosition::ptAbsolute);
      markerPoint->bottomRight->setCoords(xPixel + markerSize,
                                          yPixel + markerSize);

      // Set marker appearance
      markerPoint->setPen(marker.pen);
      markerPoint->setBrush(QBrush(marker.pen.color()));

      markerItems.append(markerPoint);

      // Determine frequency unit and scaling
      QString freqUnit = "Hz";
      double freqValue = markerFreq;
      if (markerFreq >= 1e9) {
        freqUnit = "GHz";
        freqValue = markerFreq / 1e9;
      } else if (markerFreq >= 1e6) {
        freqUnit = "MHz";
        freqValue = markerFreq / 1e6;
      } else if (markerFreq >= 1e3) {
        freqUnit = "kHz";
        freqValue = markerFreq / 1e3;
      }

      // Create label with marker ID, value, and frequency
      QString labelText;
      if (displayMode == 0) {
        // Magnitude/Phase format
        labelText =
            QString("%1 [%2]: %3 %4\n%5∠ %6°")
                .arg(markerId, traceName, QString::number(freqValue, 'g', 3),
                     freqUnit, QString::number(radius, 'f', 2),
                     QString::number(angle, 'f', 2));
      } else {
        // Real/Imaginary format
        labelText =
            QString("%1 [%2]: %3 %4\n%5%6j%7")
                .arg(markerId, traceName, QString::number(freqValue, 'g', 3),
                     freqUnit, QString::number(value.real(), 'f', 2),
                     value.imag() >= 0 ? "+" : "",
                     QString::number(value.imag(), 'f', 2));
      }

      // Create and position the label using QCPItemText
      QCPItemText *markerLabel = new QCPItemText(plot);

      // Calculate label offset in pixels
      double labelOffset = 10.0; // pixels away from marker

      // Determine label position based on angle to avoid edges
      double labelXPixel = xPixel;
      double labelYPixel = yPixel;
      Qt::Alignment alignment = Qt::AlignCenter;

      // Position label based on quadrant
      if (angle >= 45 && angle < 135) {
        // Top quadrant - label above
        labelYPixel = yPixel - labelOffset;
        alignment = Qt::AlignBottom | Qt::AlignHCenter;
      } else if (angle >= 135 && angle < 225) {
        // Left quadrant - label to left
        labelXPixel = xPixel - labelOffset;
        alignment = Qt::AlignVCenter | Qt::AlignRight;
      } else if (angle >= 225 && angle < 315) {
        // Bottom quadrant - label below
        labelYPixel = yPixel + labelOffset;
        alignment = Qt::AlignTop | Qt::AlignHCenter;
      } else {
        // Right quadrant - label to right
        labelXPixel = xPixel + labelOffset;
        alignment = Qt::AlignVCenter | Qt::AlignLeft;
      }

      // If marker is near the outer edge, move label inward
      double normalizedRadius = (radius - rMin) / (rMax - rMin);
      if (normalizedRadius > 0.8) {
        if (angle >= 45 && angle < 135) {
          labelYPixel = yPixel + labelOffset / 2;
          alignment = Qt::AlignTop | Qt::AlignHCenter;
        } else if (angle >= 135 && angle < 225) {
          labelXPixel = xPixel + labelOffset / 2;
          alignment = Qt::AlignVCenter | Qt::AlignLeft;
        } else if (angle >= 225 && angle < 315) {
          labelYPixel = yPixel - labelOffset / 2;
          alignment = Qt::AlignBottom | Qt::AlignHCenter;
        } else {
          labelXPixel = xPixel - labelOffset / 2;
          alignment = Qt::AlignVCenter | Qt::AlignRight;
        }
      }

      markerLabel->position->setType(QCPItemPosition::ptAbsolute);
      markerLabel->position->setCoords(labelXPixel, labelYPixel);
      markerLabel->setPositionAlignment(alignment);

      // Label text
      markerLabel->setText(labelText);

      // Set text font
      QFont font("Arial");
      font.setPointSize(8);
      font.setBold(true);
      font.setWeight(QFont::Normal);
      markerLabel->setFont(font);

      markerLabel->setBrush(QBrush(QColor(255, 255, 255, 230)));

      // Outer frame of the label
      markerLabel->setPen(QPen(Qt::black, 2));
      markerLabel->setPadding(QMargins(4, 4, 4, 4));

      markerLabels.append(markerLabel);
    }
  }
}

double PolarPlotWidget::getFrequencyMultiplier() const {
  int index = fUnitComboBox->currentIndex();
  switch (index) {
  case 0:
    return 1.0; // Hz
  case 1:
    return 1e3; // kHz
  case 2:
    return 1e6; // MHz
  case 3:
    return 1e9; // GHz
  default:
    return 1.0;
  }
}

void PolarPlotWidget::onRadialRangeChanged(const QCPRange &newRange) {
  // If the lower bound is negative, correct it
  if (newRange.lower < 0.0) {
    double shift = -newRange.lower;
    QCPRange correctedRange(0.0, newRange.upper + shift);

    // Temporarily disconnect to avoid infinite recursion
    disconnect(
        radialAxis,
        QOverload<const QCPRange &>::of(&QCPPolarAxisRadial::rangeChanged),
        this, &PolarPlotWidget::onRadialRangeChanged);

    // Set the corrected range
    radialAxis->setRange(correctedRange);

    // Reconnect
    connect(radialAxis,
            QOverload<const QCPRange &>::of(&QCPPolarAxisRadial::rangeChanged),
            this, &PolarPlotWidget::onRadialRangeChanged);

    // Update the spin boxes
    rAxisMin->blockSignals(true);
    rAxisMin->setValue(0.0);
    rAxisMin->blockSignals(false);

    rAxisMax->blockSignals(true);
    rAxisMax->setValue(correctedRange.upper);
    rAxisMax->blockSignals(false);
  }
}

// Send settings to the main program
PolarPlotWidget::AxisSettings PolarPlotWidget::getSettings() const {
  AxisSettings settings;
  settings.freqMin = fMinSpinBox->value();
  settings.freqMax = fMaxSpinBox->value();
  settings.freqUnit = fUnitComboBox->currentText();

  settings.radius_min = rAxisMin->value();
  settings.radius_max = rAxisMax->value();
  settings.radius_div = rAxisDiv->value();

  settings.marker_format = displayModeCombo->currentText();

  return settings;
}

// Get settings from the main program
void PolarPlotWidget::setSettings(const AxisSettings &settings) {
  fMinSpinBox->setValue(settings.freqMin);
  fMaxSpinBox->setValue(settings.freqMax);
  fUnitComboBox->setCurrentText(settings.freqUnit);

  rAxisMin->setValue(settings.radius_min);
  rAxisMax->setValue(settings.radius_max);
  rAxisDiv->setValue(settings.radius_div);

  displayModeCombo->setCurrentText(settings.marker_format);

  updatePlot();
}

/*
  FUNCTIONS TO UPDATE AXIS SETTING WIDGETS WHEN THE USER PANS OR ZOOMS
 */
void PolarPlotWidget::checkAxisRanges() {
  // Get current radial axis range
  QCPRange radialRange = radialAxis->range();

  // Ensure minimum radius is not less than 0
  double newMin = qMax(0.0, radialRange.lower);
  double newMax = radialRange.upper;

  // If minimum is negative, we need to shift the entire range
  if (radialRange.lower < 0.0) {
    double shift = -radialRange.lower; // Amount to shift up
    newMin = 0.0;
    newMax = radialRange.upper + shift;

    // Update the axis with the corrected range
    radialAxis->setRange(newMin, newMax);
    plot->replot();
  }

  // Update the spin boxes without triggering their signals
  if (qAbs(rAxisMin->value() - newMin) > 1e-6) {
    rAxisMin->blockSignals(true);
    rAxisMin->setValue(newMin);
    rAxisMin->blockSignals(false);
  }

  if (qAbs(rAxisMax->value() - newMax) > 1e-6) {
    rAxisMax->blockSignals(true);
    rAxisMax->setValue(newMax);
    rAxisMax->blockSignals(false);
  }
}
