// File: host/src/MainWindow_Serial.cpp
#include "MainWindow.h"
#include <QDateTime>
#include <complex>

void MainWindow::refreshPorts() {
  portSelector->clear();
  const auto infos = QSerialPortInfo::availablePorts();
  bool foundTarget = false;
  QString targetPort;

  for (const QSerialPortInfo &info : infos) {
    portSelector->addItem(info.portName());
    bool isCafe =
        (info.hasVendorIdentifier() && info.vendorIdentifier() == 0xCAFE);
    bool isUsbModem = info.portName().contains("usbmodem", Qt::CaseInsensitive);
    if ((isCafe || isUsbModem) && !foundTarget) {
      targetPort = info.portName();
      foundTarget = true;
      logWidget->append(">> Found Target Device: " + targetPort);
    }
  }
  if (foundTarget) {
    portSelector->setCurrentText(targetPort);
    if (!serial->isOpen())
      connectToPort();
  }
}

void MainWindow::connectToPort() {
  if (serial->isOpen()) {
    serial->close();
    connectBtn->setText("Connect");
    logWidget->append("--- Disconnected ---");

    // 1. Disable Global Controls
    checkIdBtn->setEnabled(false);
    calibrateBtn->setEnabled(false);
    comboTiaRange->setEnabled(false);

    // 2. Disable Tabs
    mainTabWidget->widget(0)->setEnabled(false);
    mainTabWidget->widget(1)->setEnabled(false);
    mainTabWidget->widget(2)->setEnabled(false);

    isMeasuringImp = false;
    isMeasuringAmp = false;
    isSweeping = false;
    lsvState = LSV_IDLE;
    currentSequence = SEQ_IDLE;
    setButtonBlinking(nullptr, false);

    measureBtn->setText("Measure");
    ampBtn->setText("Start Amp");
    lsvBlankBtn->setText("Run Blank");
    lsvSampleBtn->setText("Run Sample");
    return;
  }

  if (portSelector->currentText().isEmpty())
    return;

  serial->setPortName(portSelector->currentText());
  serial->setBaudRate(500000);

  if (serial->open(QIODevice::ReadWrite)) {
    connectBtn->setText("Disconnect");
    logWidget->append("--- Connected and Synchronized ---");

    // 1. Enable Global Controls
    checkIdBtn->setEnabled(true);
    calibrateBtn->setEnabled(true);
    comboTiaRange->setEnabled(true);

    // 2. Enable Tabs
    mainTabWidget->widget(0)->setEnabled(true);
    mainTabWidget->widget(1)->setEnabled(true);
    mainTabWidget->widget(2)->setEnabled(true);

    // Sync LPF
    onLPFChanged(comboLPF->currentIndex());
  } else {
    logWidget->append(">> Connection Error: " + serial->errorString());
  }
}

void MainWindow::onPortError(QSerialPort::SerialPortError error) {
  if (error == QSerialPort::ResourceError) {
    logWidget->append(">> Critical Error: Connection Lost.");
    serial->close();
    connectBtn->setText("Connect");

    checkIdBtn->setEnabled(false);
    calibrateBtn->setEnabled(false);
    comboTiaRange->setEnabled(false);

    mainTabWidget->widget(0)->setEnabled(false);
    mainTabWidget->widget(1)->setEnabled(false);
    mainTabWidget->widget(2)->setEnabled(false);

    setButtonBlinking(nullptr, false);
    currentSequence = SEQ_IDLE;
  }
}

void MainWindow::handleSerialData() {
  while (serial->canReadLine()) {
    QByteArray line = serial->readLine();
    QString str = QString::fromUtf8(line).trimmed();
    if (str.isEmpty())
      continue;

    QString timestamp = QDateTime::currentDateTime().toString("HH:mm:ss.zzz");
    logWidget->append(QString("[%1] %2").arg(timestamp, str));
    logWidget->moveCursor(QTextCursor::End);

    // --- Sequence State Machine ---
    if (currentSequence == SEQ_WAIT_BOOT) {
      if (str.contains("AD5940LIB Version:v0.2.1")) {
        logWidget->append(">> Sequence: Boot detected. Configuring...");

        // 1. Restore Settings
        int hpVal = comboTiaRange->currentData().toInt();
        serial->write(QString("r %1\n").arg(hpVal).toUtf8());

        serial->write(QString("t 0\n").toUtf8());

        // 2. Start Calibration
        logWidget->append(">> Sequence: Calibrating...");
        serial->write("c\n");

        currentSequence = SEQ_WAIT_CALIB;
      }
    } else if (currentSequence == SEQ_WAIT_CALIB) {
      if (str.contains("RCAL,HSTIA")) {
        logWidget->append(">> Sequence: Calibration Done. Sending Command.");
        serial->write(pendingCommand.toUtf8());
        serial->write("\n");
        currentSequence = SEQ_IDLE;
      }
    }
    // ------------------------------

    if (str.startsWith("DATA,")) {
      parseData(str);
    } else if (str.startsWith("RCAL,")) {
      parseData(str);
    } else if (str.startsWith("AMP,")) {
      parseData(str);
    } else if (str.startsWith("RAMP,")) {
      parseData(str);
    } else if (str == "STOPPED") {
      // Reset UI state
      if (lsvState != LSV_IDLE)
        stopLSV();
      if (isSweeping) {
        isSweeping = false;
        sweepBtn->setText("Sweep");
        setButtonBlinking(nullptr, false);
      }
      if (isMeasuringImp) {
        isMeasuringImp = false;
        measureBtn->setText("Measure");
        setButtonBlinking(nullptr, false);
      }
      if (isMeasuringAmp) {
        isMeasuringAmp = false;
        ampBtn->setText("Start Amp");
        setButtonBlinking(nullptr, false);
      }
    }
  }
}

void MainWindow::parseData(const QString &data) {
  QStringList parts = data.split(',');

  if (parts[0] == "AMP" && parts.size() >= 3) {
    bool okIdx, okCurr;
    double index = parts[1].toDouble(&okIdx);
    double current = parts[2].toDouble(&okCurr);
    if (okIdx && okCurr)
      ampGraph->addAmperometricData(index, current);
    return;
  }

  if (parts[0] == "RAMP" && parts.size() >= 3) {
    bool okIdx, okCurr;
    double index = parts[1].toDouble(&okIdx);
    double current = parts[2].toDouble(&okCurr);

    if (okIdx && okCurr) {
      // Calculate Voltage based on UI parameters (Host-side calculation)
      double start = spinLsvStart->value();
      double stop = spinLsvStop->value();
      int steps = spinLsvSteps->value();

      double voltage = start + (index * (stop - start) / steps);

      if (lsvState == LSV_RUNNING_BLANK) {
        lsvGraph->addLSVData(voltage, current, GraphWidget::LSV_BLANK);
        lsvBlankData.append({voltage, current});
      } else if (lsvState == LSV_RUNNING_SAMPLE) {
        lsvGraph->addLSVData(voltage, current, GraphWidget::LSV_SAMPLE);
        lsvSampleData.append({voltage, current});
      }
    }
    return;
  }

  if (parts[0] == "RCAL" && parts.size() >= 4) {
    QString type = parts[1];
    if (type == "FAIL") {
      logWidget->append(QString(">> RCAL FAIL: %1").arg(data));
      return;
    }
    bool okM, okP;
    double mag = parts[2].toDouble(&okM);
    double phase = parts[3].toDouble(&okP);

    if (okM && okP) {
      logWidget->append(QString(">> Calibration [%1]: Mag=%.2f Phase=%.2f")
                            .arg(type)
                            .arg(mag)
                            .arg(phase));
      // TODO: Store this if needed for UI display
    }
    return;
  }

  if (parts[0] == "DATA" && parts.size() >= 6) {
    bool okF, okM, okP;
    double freq = parts[1].toDouble(&okF);
    double mag = parts[4].toDouble(&okM);
    double phase = parts[5].toDouble(&okP);

    if (okF && okM && okP) {
      // 1. Plot Bode (Magnitude/Phase)
      // Note: Phase from AD5940 is in Radians.
      rawGraph->addBodeData(freq, mag, phase);

      // 2. Convert to Cartesian for Nyquist (Real/Imaginary)
      // Z = Mag * e^(j*Phase) = Mag * (cos(Phase) + j*sin(Phase))
      // Note: Nyquist Plot expects Z' vs -Z''.
      std::complex<double> z = std::polar(mag, phase);
      double real = z.real();
      double imag =
          z.imag(); // This is Z''. GraphWidget negates it for the plot.

      nyquistGraph->addNyquistData(real, imag, real, imag, false);

      if (sweepFreqs.size() >= expectedSweepPoints) {
        // Discard extra points (Artifact removal)
        return;
      }

      sweepFreqs.append(freq);
      sweepReals.append(real);
      sweepImags.append(imag);

      if (sweepReals.size() > 10 && sweepReals.size() % 10 == 0) {
        computeHilbert();
        performCircleFit();
      }
    }
  }
}