#include "adcgraphdialog.h"
#include "ui_adcgraphdialog.h"
#include <QModbusReply>
#include <QDebug>
#include <QMessageBox>
#include <QValueAxis>
#include <QVBoxLayout>  // Добавить этот include
#include <QChartView>   // Добавить этот include

// Адреса регистров для АЦП
#define REG_ADC_ZERO          555
#define REG_ADC_ONE_VOLT      556
#define REG_STABLE_START      557
#define REG_STABLE_END        558
#define REG_TE_NUMBER         564
#define REG_ADC_BUFFER_START  571
#define REG_ADC_BUFFER_END    1070

AdcGraphDialog::AdcGraphDialog(QModbusClient *modbusDevice, QWidget *parent) :
    QDialog(parent),
    ui(new Ui::AdcGraphDialog),
    m_modbusDevice(modbusDevice),
    m_updateTimer(new QTimer(this)),
    m_boardId(-1),
    m_boardAddress(-1),
    m_teNumber(-1),
    m_adcZero(0),
    m_adcOneVolt(4096),
    m_series(new QLineSeries()),
    m_chart(new QChart()),
    m_stableStartLine(new QLineSeries()),
    m_stableEndLine(new QLineSeries()),
    m_stableStartIndex(-1),
    m_stableEndIndex(-1),
    m_startAddress(0),
    m_registerCount(100)
{
    ui->setupUi(this);

    // Настройка основного графика
    m_series->setName("АЦП данные");
    m_chart->addSeries(m_series);

    // Настройка линий стабильного участка
    m_stableStartLine->setName("Начало стаб. участка");
    m_stableStartLine->setColor(Qt::red);
    m_stableStartLine->setPen(QPen(Qt::red, 2, Qt::DashLine));
    m_chart->addSeries(m_stableStartLine);

    m_stableEndLine->setName("Конец стаб. участка");
    m_stableEndLine->setColor(Qt::green);
    m_stableEndLine->setPen(QPen(Qt::green, 2, Qt::DashLine));
    m_chart->addSeries(m_stableEndLine);

    m_chart->setTitle("График АЦП");
    m_chart->legend()->setVisible(true);
    m_chart->legend()->setAlignment(Qt::AlignTop);

    m_axisX = new QValueAxis();
    m_axisX->setTitleText("Отсчеты АЦП");
    m_axisX->setRange(0, m_registerCount);
    m_chart->addAxis(m_axisX, Qt::AlignBottom);
    m_series->attachAxis(m_axisX);
    m_stableStartLine->attachAxis(m_axisX);
    m_stableEndLine->attachAxis(m_axisX);

    m_axisY = new QValueAxis();
    m_axisY->setTitleText("Напряжение, В");
    m_axisY->setRange(-1.3, 1.3);
    m_chart->addAxis(m_axisY, Qt::AlignLeft);
    m_series->attachAxis(m_axisY);
    m_stableStartLine->attachAxis(m_axisY);
    m_stableEndLine->attachAxis(m_axisY);

    QChartView *chartView = new QChartView(m_chart);
    chartView->setRenderHint(QPainter::Antialiasing);

    QVBoxLayout *layout = new QVBoxLayout(ui->plotWidget);
    layout->addWidget(chartView);

    // Подключаем сигналы элементов управления диапазоном
    connect(ui->registerCountSpinBox, QOverload<int>::of(&QSpinBox::valueChanged),
            this, &AdcGraphDialog::on_registerCountChanged);
    connect(ui->rangeApplyButton, &QPushButton::clicked,
            this, &AdcGraphDialog::on_rangeApplyClicked);
    connect(ui->teNumberSpinBox, QOverload<int>::of(&QSpinBox::valueChanged),
            this, &AdcGraphDialog::on_teNumberChanged);

    connect(m_updateTimer, &QTimer::timeout, this, &AdcGraphDialog::onUpdateTimer);
    connect(ui->closeBtn, &QPushButton::clicked, this, &AdcGraphDialog::on_closeBtn_clicked);
}



AdcGraphDialog::~AdcGraphDialog()
{
    stopGraph();
    delete ui;
}



void AdcGraphDialog::setModbusDevice(QModbusClient *device)
{
    m_modbusDevice = device;
}

void AdcGraphDialog::on_teNumberChanged(int value)
{
    if(m_teNumber != value)
        setTENumber(m_boardId, value);
}

void AdcGraphDialog::setTENumber(int boardID, int teNumber)
{
    if (m_boardAddress == -1) return;

    m_teNumber = teNumber;
    m_boardId = boardID;
    m_boardAddress = m_boardId + 1;

    // Обновляем заголовок окна
    setWindowTitle(QString("График АЦП - Плата %1, ТЭ %2 (адр %3-%4)")
                  .arg(m_boardId + 1)
                  .arg(m_teNumber)
                  .arg(m_startAddress)
                  .arg(m_startAddress + m_registerCount - 1));

    // Записываем новый номер ТЭ в устройство
    if (m_modbusDevice) {
        QModbusDataUnit unit(QModbusDataUnit::HoldingRegisters, REG_TE_NUMBER, 1);
        unit.setValue(0, teNumber);

        if (auto *reply = m_modbusDevice->sendWriteRequest(unit, m_boardAddress)) {
            if (!reply->isFinished()) {
                connect(reply, &QModbusReply::finished, this, [this, reply, teNumber]() {
                    if (reply->error() == QModbusDevice::NoError) {
                        qDebug() << "TE number changed to:" << teNumber;
                        // Можно обновить данные сразу после смены ТЭ
                        readAdcDataAndIndices();
                    } else {
                        qDebug() << "Error writing TE number:" << reply->errorString();
                    }
                    reply->deleteLater();
                });
            } else {
                reply->deleteLater();
            }
        }
    }
    ui->teNumberSpinBox->setValue(teNumber);
}

void AdcGraphDialog::readCalibrationValues()
{
    if (!m_modbusDevice || m_boardAddress == -1) return;

    // Читаем калибровочные значения (регистры 555 и 556)
    QModbusDataUnit unit(QModbusDataUnit::InputRegisters, REG_ADC_ZERO, 2);

    if (auto *reply = m_modbusDevice->sendReadRequest(unit, m_boardAddress)) {
        if (!reply->isFinished()) {
            connect(reply, &QModbusReply::finished, this, [this, reply]() {
                if (reply->error() == QModbusDevice::NoError) {
                    const QModbusDataUnit result = reply->result();
                    if (result.valueCount() >= 2) {
                        m_adcZero = result.value(0);
                        m_adcOneVolt = result.value(1);
                        //qDebug() << "Calibration values - Zero:" << m_adcZero << "1V:" << m_adcOneVolt;
                    }
                } else {
                    //qDebug() << "Error reading calibration:" << reply->errorString();
                }
                reply->deleteLater();
            });
        } else {
            reply->deleteLater();
        }
    }
}

void AdcGraphDialog::readStableIndices()
{
    if (!m_modbusDevice || m_boardAddress == -1) return;

    // Читаем индексы стабильного участка (регистры 557 и 558)
    QModbusDataUnit unit(QModbusDataUnit::InputRegisters, REG_STABLE_START, 2);

    if (auto *reply = m_modbusDevice->sendReadRequest(unit, m_boardAddress)) {
        if (!reply->isFinished()) {
            connect(reply, &QModbusReply::finished, this, &AdcGraphDialog::onStableIndicesReady);
        } else {
            reply->deleteLater();
        }
    }
}

void AdcGraphDialog::onStableIndicesReady()
{
    auto *reply = qobject_cast<QModbusReply*>(sender());
    if (!reply) return;

    if (reply->error() == QModbusDevice::NoError) {
        const QModbusDataUnit result = reply->result();
        if (result.valueCount() >= 2) {
            m_stableStartIndex = result.value(0);
            m_stableEndIndex = result.value(1);
            //qDebug() << "Stable indices updated - Start:" << m_stableStartIndex << "End:" << m_stableEndIndex;
            updateStableLines();

            // Обновляем статистику с новыми индексами
            updateStatisticsWithStableInfo();
        }
    } else {
        //qDebug() << "Error reading stable indices:" << reply->errorString();
    }

    reply->deleteLater();
}

void AdcGraphDialog::updateStatistics()
{
    if (m_series->count() > 0) {
        double min = 1000, max = -1000, sum = 0;
        for (const QPointF &point : m_series->points()) {
            double y = point.y();
            if (y < min) min = y;
            if (y > max) max = y;
            sum += y;
        }
        double avg = sum / m_series->count();

        ui->minLabel->setText(QString::number(min, 'f', 3) + " В");
        ui->maxLabel->setText(QString::number(max, 'f', 3) + " В");
        ui->avgLabel->setText(QString::number(avg, 'f', 3) + " В");

        // Обновляем информацию о стабильном участке
        updateStatisticsWithStableInfo();
    }
}



void AdcGraphDialog::updateStatisticsWithStableInfo()
{
    if (m_series->count() > 0) {
        // Используем абсолютные индексы в формате "начальный-конечный"
        ui->samplesLabel->setText(
            QString("%1 отсч. (адр %2-%3) [стаб: %4-%5]")
            .arg(m_series->count())
            .arg(m_startAddress)
            .arg(m_startAddress + m_registerCount - 1)
            .arg(m_stableStartIndex)  // Абсолютный начальный индекс
            .arg(m_stableEndIndex)    // Абсолютный конечный индекс
        );
    }
}


void AdcGraphDialog::updateStableLines()
{
    // Очищаем предыдущие линии
    m_stableStartLine->clear();
    m_stableEndLine->clear();

    // Получаем текущий диапазон оси Y
    double yMin = m_axisY->min();
    double yMax = m_axisY->max();

    // Добавляем вертикальную линию для начала стабильного участка
    // Учитываем текущий диапазон отображения
    if (m_stableStartIndex >= m_startAddress && m_stableStartIndex < m_startAddress + m_registerCount) {
        int relativeStartIndex = m_stableStartIndex - m_startAddress;
        m_stableStartLine->append(relativeStartIndex, yMin);
        m_stableStartLine->append(relativeStartIndex, yMax);
    }

    // Добавляем вертикальную линию для конца стабильного участка
    if (m_stableEndIndex >= m_startAddress && m_stableEndIndex < m_startAddress + m_registerCount) {
        int relativeEndIndex = m_stableEndIndex - m_startAddress;
        m_stableEndLine->append(relativeEndIndex, yMin);
        m_stableEndLine->append(relativeEndIndex, yMax);
    }
}

void AdcGraphDialog::updateGraphRange()
{
    // Обновляем диапазон оси X
    m_axisX->setRange(0, m_registerCount);
    m_axisX->setTitleText(QString("Отсчеты АЦП (%1-%2)").arg(m_startAddress).arg(m_startAddress + m_registerCount - 1));

    // Сбрасываем ось Y к разумному диапазону по умолчанию
    m_axisY->setRange(-1.2, 1.2);

    // Обновляем линии стабильного участка с учетом нового диапазона
    updateStableLines();
}

void AdcGraphDialog::readAdcDataAndIndices()
{
    if (!m_modbusDevice || m_boardAddress == -1) return;

    // Создаем один запрос для данных АЦП + индексов стабильности
    // Адреса: данные АЦП (571+), индексы (557-558)
    int start = m_startAddress + REG_ADC_BUFFER_START;
    int count = m_registerCount;

    // Читаем индексы стабильности (557-558) и данные АЦП одним запросом
    QModbusDataUnit unit(QModbusDataUnit::InputRegisters, REG_STABLE_START,
                         count + (start - REG_STABLE_START));

    //qDebug() << "Reading combined data from address" << REG_STABLE_START << "count:" << unit.valueCount();

    if (auto *reply = m_modbusDevice->sendReadRequest(unit, m_boardAddress)) {
        if (!reply->isFinished()) {
            connect(reply, &QModbusReply::finished, this, &AdcGraphDialog::onCombinedDataReady);
        } else {
            reply->deleteLater();
        }
    } else {
        //qDebug() << "Failed to send combined data read request";
    }
}


void AdcGraphDialog::onCombinedDataReady()
{
    auto *reply = qobject_cast<QModbusReply*>(sender());
    if (!reply) return;

    if((m_adcZero == 0) || (m_adcOneVolt == 0))
    {
        readCalibrationValues();
        return;
    }

    if (reply->error() == QModbusDevice::NoError) {
        const QModbusDataUnit result = reply->result();

        // Обрабатываем индексы стабильности (первые 2 регистра)
        if (result.valueCount() >= 2) {
            m_stableStartIndex = result.value(0);
            m_stableEndIndex = result.value(1);
            //qDebug() << "Stable indices - Start:" << m_stableStartIndex << "End:" << m_stableEndIndex;
        }

        // Обрабатываем данные АЦП (остальные регистры)
        int adcDataStartIndex = (m_startAddress + REG_ADC_BUFFER_START) - REG_STABLE_START;

        // Очищаем предыдущие данные
        m_series->clear();

        // Добавляем новые точки и определяем диапазон
        double minVoltage = 1000, maxVoltage = -1000;
        for (int i = 0; i < m_registerCount; ++i) {
            if (adcDataStartIndex + i < result.valueCount()) {
                double voltage = convertAdcToVoltage(result.value(adcDataStartIndex + i));
                m_series->append(i, voltage);

                // Обновляем мин/макс для автоматического масштабирования
                if (voltage < minVoltage) minVoltage = voltage;
                if (voltage > maxVoltage) maxVoltage = voltage;
            }
        }

        // Обновляем график и статистику
        updateYAxisRange(minVoltage, maxVoltage);
        updateStableLines();
        updateStatistics();

    } else {
      // qDebug() << "Error reading combined data:" << reply->errorString();
    }

    reply->deleteLater();
}


void AdcGraphDialog::readAdcBuffer()
{
    if (!m_modbusDevice || m_boardAddress == -1) return;

    // Читаем выбранный диапазон буфера АЦП
    QModbusDataUnit unit(QModbusDataUnit::InputRegisters, m_startAddress+REG_ADC_BUFFER_START, m_registerCount);

  // qDebug() << "Reading ADC buffer from address" << m_startAddress << "count:" << m_registerCount;

    if (auto *reply = m_modbusDevice->sendReadRequest(unit, m_boardAddress)) {
        if (!reply->isFinished()) {
            connect(reply, &QModbusReply::finished, this, &AdcGraphDialog::onReadReady);
        } else {
            reply->deleteLater();
        }
    } else {
      // qDebug() << "Failed to send ADC buffer read request";
    }
}

void AdcGraphDialog::startGraph(int boardId, int teNumber)
{
    m_boardId = boardId;
    m_teNumber = teNumber;
    m_boardAddress = boardId + 1;

    // Устанавливаем начальное значение в спинбокс
    ui->teNumberSpinBox->setValue(teNumber);

    setWindowTitle(QString("График АЦП - Плата %1, ТЭ %2 (адр %3-%4)")
                  .arg(boardId + 1)
                  .arg(teNumber)
                  .arg(m_startAddress)
                  .arg(m_startAddress + m_registerCount - 1));

    // Очищаем предыдущие данные
    m_series->clear();
    m_stableStartLine->clear();
    m_stableEndLine->clear();

    // Обновляем диапазон графика
    updateGraphRange();

    readCalibrationValues();

    // Записываем начальный номер ТЭ
    setTENumber(m_boardAddress, teNumber);

    m_updateTimer->start(m_timeout);
}



void AdcGraphDialog::setTimeout(int timeout)
{
    m_timeout = timeout;
}

void AdcGraphDialog::stopGraph()
{
    m_updateTimer->stop();
    m_boardId = -1;
    m_boardAddress = -1;

    // Отменяем все pending запросы Modbus
    if (m_modbusDevice) {
        m_modbusDevice->disconnect(this); // Отключаем все сигналы
    }

}

void AdcGraphDialog::onUpdateTimer()
{
    if (m_boardAddress == -1) return;
    // Читаем и буфер АЦП, и индексы стабильности каждый период
    readAdcDataAndIndices();
}

void AdcGraphDialog::onReadReady()
{
    auto *reply = qobject_cast<QModbusReply*>(sender());
    if (!reply) return;

    if (reply->error() == QModbusDevice::NoError) {
        const QModbusDataUnit result = reply->result();

        // Очищаем предыдущие данные
        m_series->clear();

        // Добавляем новые точки и определяем диапазон
        double minVoltage = 1000, maxVoltage = -1000;
        for (int i = 0; i < result.valueCount(); ++i) {
            double voltage = convertAdcToVoltage(result.value(i));
            m_series->append(i, voltage);

            // Обновляем мин/макс для автоматического масштабирования
            if (voltage < minVoltage) minVoltage = voltage;
            if (voltage > maxVoltage) maxVoltage = voltage;
        }

        // Автоматически настраиваем диапазон оси Y
        updateYAxisRange(minVoltage, maxVoltage);

        // Обновляем линии стабильного участка
        updateStableLines();

        // Обновляем статистику
        if (m_series->count() > 0) {
            double min = 1000, max = -1000, sum = 0;
            for (const QPointF &point : m_series->points()) {
                double y = point.y();
                if (y < min) min = y;
                if (y > max) max = y;
                sum += y;
            }
            double avg = sum / m_series->count();

            ui->minLabel->setText(QString::number(min, 'f', 3) + " В");
            ui->maxLabel->setText(QString::number(max, 'f', 3) + " В");
            ui->avgLabel->setText(QString::number(avg, 'f', 3) + " В");

            // Обновляем информацию о стабильном участке
            updateStatisticsWithStableInfo();
        }

    } else {
      // qDebug() << "Error reading ADC buffer:" << reply->errorString();
    }

    reply->deleteLater();
}

void AdcGraphDialog::updateYAxisRange(double minVoltage, double maxVoltage)
{
    // Добавляем запас 10% к диапазону
    double range = maxVoltage - minVoltage;
    double margin = range * 0.1;

    double yMin = minVoltage - margin;
    double yMax = maxVoltage + margin;

    // Если диапазон слишком маленький или слишком большой, устанавливаем разумные пределы
//    if ((range < 0.1) || ((maxVoltage > 0.5) && (minVoltage < -0.5)))
//    {
//        yMin = -1.5;
//        yMax = 1.5;
//    }
//    else if(maxVoltage > 0.5) {
//        yMin = -0.1;
//        yMax = 1.5;
//    }
//    else if(minVoltage < -0.5)
//    {
//        yMin = -1.5;
//        yMax =0.1;
//    }
//    else
    {
        yMin = -1.5;
        yMax = 1.5;
    }

    // Ограничиваем разумными пределами
    yMin = qMax(yMin, -5.0); // Не ниже -5В
    yMax = qMin(yMax, 5.0);  // Не выше 5В

    // Устанавливаем новый диапазон
    m_axisY->setRange(yMin, yMax);

    // Обновляем линии стабильного участка с новым диапазоном
    updateStableLines();
}


double AdcGraphDialog::convertAdcToVoltage(quint16 adcValue)
{
    if (m_adcOneVolt == m_adcZero) return 0;
    return (adcValue - m_adcZero) * 1.1 / (m_adcOneVolt - m_adcZero);
}


void AdcGraphDialog::on_registerCountChanged(int value)
{
    m_registerCount = value;
  // qDebug() << "Register count changed to:" << value;
}

void AdcGraphDialog::on_rangeApplyClicked()
{
  // qDebug() << "Applying new range - Start:" << m_startAddress << "Count:" << m_registerCount;
    updateGraphRange();

    // Немедленно обновляем данные
    if (m_boardAddress != -1) {
        readAdcBuffer();
    }
}

void AdcGraphDialog::on_closeBtn_clicked()
{
    stopGraph();
    reject(); // Или accept() в зависимости от логики
}
