#include <QHBoxLayout>
#include <QRandomGenerator>
#include <cmath>
#include <limits>
#include <qcustomplot.h>
#include <QMessageBox>
#include <qpoint.h>

#include "oitautoanalysis.h"
#include "analysisoperationrecorder.h"
#include "centralwidget.h"
#include "filemanager.h"
#include "pointcalculate.h"
#include "logger.h"
#include "xlsxhandler.h"
#include "itemmanager.h"

namespace AnaOpRecorder      = AnalysisOperationRecorder;
using AnaOpRecorderOperation = AnalysisOperationRecorder::AnalysisOperation;
using namespace Global;

CentralWidget::CentralWidget(QWidget *parent)
    : QWidget(parent), _customPlot(new LocalCustomPlot(this))
      //    ,_customPlot(new QCustomPlot(this))
      ,
      _analysisMode(AnalysisMode::Null), _currentCurve(nullptr) {
    setMouseTracking(true);

    setStyleSheet("background-color: lightgray;");
    resize(888, 666);

    QHBoxLayout *layout = new QHBoxLayout();
    layout->setMargin(0);
    layout->addWidget(_customPlot);
    this->setLayout(layout);

    // 创建两条竖线
    _line1 = new QCPItemStraightLine(_customPlot);
    _line1->point1->setCoords(20, _customPlot->yAxis->range().lower);
    _line1->point2->setCoords(20, _customPlot->yAxis->range().upper);
    _line1->setPen(QPen(Qt::red));
    _line1->setSelectable(true);

    _line2 = new QCPItemStraightLine(_customPlot);
    _line2->point1->setCoords(40, _customPlot->yAxis->range().lower);
    _line2->point2->setCoords(40, _customPlot->yAxis->range().upper);
    _line2->setPen(QPen(Qt::red));
    _line2->setSelectable(true);

    // 安装事件过滤器
    //    _graph = _customPlot->addGraph(0);
    //    _eventHandler = new EventHandler(_customPlot, _line1, _line2, _graph, nullptr);

    _eventHandler = new EventHandler(_customPlot, _line1, _line2, nullptr);
    _eventHandler->setEnable(true);

    _customPlot->installEventFilter(_eventHandler);
    _customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables);
    //    _customPlot->setInteractions( QCP::iRangeZoom | QCP::iSelectPlottables);
    //    _customPlot->setInteractions(QCP::iSelectPlottables);

    connect(_eventHandler, &EventHandler::sigSendLineXCoord,
            this, &CentralWidget::sigSendLineXCoord);
    connect(_eventHandler, &EventHandler::sigDelCurve,
            this, &CentralWidget::slotDelCurve);

    connect(_customPlot, &QCustomPlot::selectionChangedByUser,
            this, &CentralWidget::slotSelectionChangedByUser);

    setEventHandlerEnable(false);
    //
#if 0
    // init data
    QVector<double> x(101), y(101);
    for (int i = 0; i < 101; i++) {
        x[i] = i / 50.0 - 1;//设置x的范围为-1~1
        y[i] = x[i] * x[i];
    }

    _customPlot->graph(0)->setData(x, y);
    //设置坐标轴标签
    _customPlot->xAxis->setLabel("x coordinates");
    _customPlot->yAxis->setLabel("y coordinates");
    //设置坐标轴范围，以便我们可以看到全部数据
    _customPlot->xAxis->setRange(0, 1);
    _customPlot->yAxis->setRange(0, 100);
    _customPlot->replot();
#endif

    // 设置坐标轴标签
    _customPlot->xAxis->setLabel(AxisTemperature);
    _customPlot->yAxis->setLabel(AxisDSC);

    // 设置坐标轴范围，以便我们可以看到全部数据
    _customPlot->xAxis->setRange(0, 100);
    _customPlot->yAxis->setRange(0, 20);

    // startTimer(300);
}

CentralWidget::~CentralWidget() {
}

void CentralWidget::switchAxisMode() {
    if (Global::_mode != Global::Mode::Analysis) {
        return;
    }

    if (_axisMode == AxisMode::SingleY) {
        _axisMode = AxisMode::DoubleY;
    } else {
        _axisMode = AxisMode::SingleY;
    }

    clearData(ClearDataMode::JustUi);

    uiLoadXlsxFileData();
}

bool CentralWidget::isCurrentCurve(QCPCurve *curve) {
    return _currentCurve == curve;
}

#if 0
void CentralWidget::deleteCurrentCurve()
{
    slotDelCurve(_currentCurve);
}

void CentralWidget::deleteAllExperimentCurve()
{
    deleteCurve(Global::ObjectNameExperiemnt);
}
#endif

QCPCurve *CentralWidget::addCurveData(
    const QVector<ExperimentData> &dataVtr, const QString objectName) {
    logde << "addCurveData...";
    logde << "data vtr size:" << dataVtr.size();

    PointCalculate::setAnalysisData(dataVtr);

    // Load data.
    QVector<double> tVtr, xVtr, yVtr;
    int             index = 0;
    for (const Global::ExperimentData &ed : dataVtr) {
        tVtr.push_back(index++);
        if (_axisMode == AxisMode::SingleY) {
            xVtr.push_back(ed.sampleTemp);
        } else {
            xVtr.push_back(ed.runTime);
        }
        yVtr.push_back(ed.dsc);
    }

    _currentCurve = new QCPCurve(_customPlot->xAxis, _customPlot->yAxis);
    _currentCurve->setData(tVtr, xVtr, yVtr);
    _currentCurve->setSelectable(QCP::stWhole);       // 设置曲线可选
    _currentCurve->setLineStyle(QCPCurve::lsLine);    // 线性连接
    _currentCurve->setVisible(true);

    _currentCurve->setObjectName(objectName);

    // Set axis range.
    QPair<float, float> minMaxXAxisPair;
    if (_axisMode == AxisMode::SingleY) {
        // Axis x is temperature value.
        minMaxXAxisPair = PointCalculate::getMinAndMaxOfSampleTemp();
    } else {
        // Axis x is time value.
        minMaxXAxisPair = PointCalculate::getMinAndMaxOfRunTime();
    }
#if 1
    QPair<float, float> newXAxisPair = PointCalculate::getMinAndMaxOfAxis(
        minMaxXAxisPair.first, minMaxXAxisPair.second);
    _customPlot->xAxis->setRange(newXAxisPair.first, newXAxisPair.second);

    QPair<float, float> minMaxYAxisPair = PointCalculate::getMinAndMaxOfDSC();
    QPair<float, float> newYAxisPair    = PointCalculate::getMinAndMaxOfAxis(
        minMaxYAxisPair.first, minMaxYAxisPair.second);
    _customPlot->yAxis->setRange(newYAxisPair.first,
                                 newYAxisPair.second);
#endif

    // Add analysis operation data.
#if 0
    if(cfd.analysisOperationVtr.size() > 0){
        for(AnaOpRecorder::AnalysisOperation& ao
            :cfd.analysisOperationVtr){
            loadAnalysisData(ao.mode,ao.x1,ao.x2,cfd.fileName);
        }
    }
#endif

    // Refresh ui.
    _customPlot->replot();
    _customPlot->update();

    return _currentCurve;
}

void CentralWidget::startExperiment() {
    if (Global::_experimentOITFlag) {
        setAxisMode(Global::AxisMode::DoubleY);
    } else {
        setAxisMode(Global::AxisMode::SingleY);
    }

    if (_yAxis2Curve != nullptr) {
        _yAxis2Curve = nullptr;
    }
    _customPlot->xAxis->setRange(-5, 60);
    _customPlot->yAxis->setRange(-20, 20);
    _customPlot->yAxis2->setRange(0, 400);

    _customPlot->replot();
}

void CentralWidget::setVerticalLineRange(const double x1, const double x2) {
    _line1->point1->setCoords(x1, _line1->point1->coords().y());
    _line1->point2->setCoords(x1, _line1->point2->coords().y());

    _line2->point1->setCoords(x2, _line2->point1->coords().y());
    _line2->point2->setCoords(x2, _line2->point2->coords().y());

    slotAnalysisSettingConfirm();
}

void CentralWidget::setAnalysisMode(const AnalysisMode mode) {
    _analysisMode = mode;

    if (mode == AnalysisMode::Null) {
        _customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables);
    } else {
        _customPlot->setInteractions(QCP::iSelectPlottables);
        setEventHandlerEnable(true);
    }

#if 0
    switch (mode)
    {
    case AnalysisMode::Null:
        _customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables);
        break;
    case AnalysisMode::NumericalLabel:
    case AnalysisMode::StartPoint:
    case AnalysisMode::StopPoint:
    case AnalysisMode::PeakSynthesisAnalysis:
    case AnalysisMode::GlassTransition:
    case AnalysisMode::OnsetTemperaturePoint:
    case AnalysisMode::EndsetTemperaturePoint:
        _customPlot->setInteractions(QCP::iSelectPlottables);
        setEventHandlerEnable(true);
        break;
    default:
        break;
    }
#endif
}

void CentralWidget::slotModeModify(const Global::Mode mode) {
    if (Global::Mode::Experiment == mode) {
#if 0
        if (_customPlot->graphCount() > 0 && _currentGraph)
        {
            // 清除第一个图表上的数据
            _currentGraph->setData(QVector<double>(), QVector<double>());
        }
#endif

        // 创建画布,设置画布上的点数据
        //        _graph = _customPlot->addGraph();

        // 设置坐标轴标签
        _customPlot->xAxis->setLabel("Temp/℃");
        _customPlot->yAxis->setLabel("DSC/mW");
        // 设置坐标轴范围，以便我们可以看到全部数据
        _customPlot->xAxis->setRange(0, 400);
        _customPlot->yAxis->setRange(-20, 20);
    } else if (Global::Mode::Analysis == mode) {
        qDebug() << "file close...";
    }
}

void CentralWidget::slotRecvCommonData(const CommonData &cd) {
    logde << "slotRecvCommonData, cd:" << cd.add_run_time;

    // Record data.
    if (!Global::_currentCurveExperimentDataPtr) {
        loger << "_currentCurveExperimentDataPtr is nullptr.Soft exit.";
        exit(0);
    } else {
        // 跳过运行时间为0 的数据
        if (cd.add_run_time == 0) {
            return;
        }
        //
        Global::ExperimentData ed;
        ed.dsc              = cd.dsc;
        ed.sampleTemp       = cd.sample_temp;
        ed.runTime          = cd.add_run_time;
        ed.constantTempTime = cd.add_constan_temp_time;

        if (Global::_currentCurveExperimentDataPtr->dataVtr.empty()) {
            logde << "change phase, index:"
                  << Global::_currentCurveExperimentDataPtr->phaseIndex;
            _currentCurve = nullptr;
        }

        ed.phaseIndex = Global::_currentCurveExperimentDataPtr->phaseIndex;

        Global::_currentCurveExperimentDataPtr->dataVtr.push_back(ed);

        OITAutoAnalysis::getInstance()->analysis();
    }

    // Update ui.
    static double index = 0.0;

    if (!_currentCurve) {
        logde << "_currentCurve is nullptr";
        _currentCurve = new QCPCurve(_customPlot->xAxis, _customPlot->yAxis);
        _currentCurve->setObjectName(Global::ObjectNameExperiemnt
                                     + Global::Separator
                                     + QString::number(Global::_currentCurveExperimentDataPtr->phaseIndex));

        Global::_currentCurveExperimentDataPtr->curve = _currentCurve;
    }

    if (!Global::_experimentOITFlag) {
        _currentCurve->addData(index++, cd.sample_temp, cd.dsc);
    } else {
        if (_yAxis2Curve == nullptr) {
            _yAxis2Curve = new QCPCurve(_customPlot->xAxis, _customPlot->yAxis2);
            _yAxis2Curve->setSelectable(QCP::stWhole);       // 设置曲线可选
            _yAxis2Curve->setLineStyle(QCPCurve::lsLine);    // 线性连接

            _yAxis2Curve->setObjectName(Global::ObjectNameExperiemnt);

            // 设置曲线颜色
            QPen pen(Qt::red);            // 创建一个红色的画笔
            pen.setWidth(1);              // 设置画笔宽度
            _yAxis2Curve->setPen(pen);    // 将画笔应用到曲线
        }
        _yAxis2Curve->addData(index++, cd.add_run_time, cd.sample_temp);

        _currentCurve->addData(index++, cd.add_run_time, cd.dsc);
    }

    //
    _customPlot->replot();
}

void CentralWidget::slotRecvAnalysisFileName(const QString &filePath) {
    logde << "slotRecvAnalysisFileName, filePath:" << filePath.toStdString();

    if (_analysisFilePathVtr.contains(filePath)) {
        return;
    } else {
        _analysisFilePathVtr.push_back(filePath);
    }

    // Read xlsx file.
    Global::CurveFileData cfd;
    if (XlsxHandler::readFile(filePath, cfd) != 0) {
        QMessageBox::warning((QWidget *)this->parent(), "warnning", "File parse error.");
        return;
    }

    // 把文件数据存储到全局变量中。
    for (int i = Global::_curveFileDataVtr.size() - 1; i >= 0; --i) {
        if (Global::_curveFileDataVtr[i].filePath == filePath) {
            // 已经存在该文件数据，直接返回
            logde << "file exist...return.";
            return;
        }
    }

    Global::_curveFileDataVtr.push_back(cfd);

    //
    uiLoadXlsxFileData();
}

void CentralWidget::slotAnalysisSettingApply() {
    // Set the analysis data corresponding current curve.
    if (!_currentCurve) {
        logde << "failed. current curve is nullptr.";
        return;
    }

    QString selectedCurveObjectName = _currentCurve->objectName();
    logde << "slotAnalysisSettingApply objectName:" << selectedCurveObjectName.toStdString();

    // Set curve data to PointCalculate.
    QVector<ExperimentData> totalDataVtr;
    if (Global::_curveFileDataVtr.empty()) {
        // Load experiment data.
        logde << "experiment vtr size:" << Global::_curveExperimentDataVtr.size();

        for (Global::CurveExperimentData &ced : Global::_curveExperimentDataVtr) {
            if (ced.curve == _currentCurve) {
                logde << "load experiment data.";
                if (ced.smoothDataVtr.empty()) {
                    //                    PointCalculate::setAnalysisData(ced.dataVtr);
                    totalDataVtr.append(ced.dataVtr);

                } else {
                    //                    PointCalculate::setAnalysisData(ced.smoothDataVtr);
                    totalDataVtr.append(ced.smoothDataVtr);
                }

                break;
            }
        }
    } else {
        // Load xlsx file data.
#if 0
        for(Global::CurveFileData& cfd:Global::_curveFileDataVtr){
            for(Global::PhaseTotalInfo& pti:cfd.phaseTotalVtr){
                if(pti.curve == _currentCurve){
                    if(pti.smoothDataVtr.empty()){
                        PointCalculate::setAnalysisData(pti.dataVtr);
                    }else{
                        PointCalculate::setAnalysisData(pti.smoothDataVtr);
                    }
                    break;
                }
            }
        }
#endif
        for (Global::CurveFileData &cfd : Global::_curveFileDataVtr) {
            for (Global::PhaseTotalInfo &pti : cfd.phaseTotalVtr) {
                if (pti.curve == _currentCurve) {

                    for (Global::PhaseTotalInfo &pti : cfd.phaseTotalVtr) {
                        if (pti.smoothDataVtr.empty()) {
                            totalDataVtr.append(pti.dataVtr);

                            logde << " data phase index:" << pti.dataVtr.first().phaseIndex;
                        } else {
                            totalDataVtr.append(pti.smoothDataVtr);
                        }
                    }

                    break;
                }
            }
        }
    }

    PointCalculate::setAnalysisData(totalDataVtr);

    //
    double x1 = _line1->point1->coords().x();
    double x2 = _line2->point1->coords().x();

    if (_x1Record == x1 && _x2Record == x2) {
        return;
    } else {
        _x1Record = x1;
        _x2Record = x2;
    }

    //
    calculateAnalysisResult(_analysisMode, x1, x2, _currentCurve->objectName());

    if (Global::_mode == Global::Mode::Analysis) {
        AnalysisOperation ao;
        ao.mode = _analysisMode;
        ao.x1   = x1;
        ao.x2   = x2;

        AnaOpRecorder::_analysisOperationVtr.push_back(ao);
    }
}

void CentralWidget::slotAnalysisSettingConfirm() {
    ItemManager::confirm();

    slotAnalysisSettingApply();

    setAnalysisMode(AnalysisMode::Null);

    _line1->setVisible(false);
    _line2->setVisible(false);

    emit sigRightDockWidgetHide();
}

void CentralWidget::slotAnalysisSettingUndo() {
    clearData(ClearDataMode::Undo);

    _x1Record = 0.0;
    _x2Record = 0.0;
}

void CentralWidget::slotAnalysisSettingCancel() {
    clearData(ClearDataMode::Undo);

    setAnalysisMode(AnalysisMode::Null);

    _line1->setVisible(false);
    _line2->setVisible(false);

    emit sigRightDockWidgetHide();
}

void CentralWidget::slotAnalysisSettingLineXPoint(const int index, const double) {
}

void CentralWidget::slotDrawCustomText(const QString str) {
    QCPItemText *customLegendItem = new QCPItemText(_customPlot);
    customLegendItem->position->setTypeX(QCPItemPosition::ptAbsolute);
    customLegendItem->position->setTypeY(QCPItemPosition::ptAbsolute);
    customLegendItem->position->setCoords(150, 50);    // 稍微向下移动
    customLegendItem->setText(str);

    QFont font("Arial", 10);
    customLegendItem->setFont(font);

    // 设置边框和背景
    customLegendItem->setPen(QPen(Qt::black));
    customLegendItem->setBrush(QBrush(Qt::white));

    // 禁止点选
    customLegendItem->setSelectable(false);
    customLegendItem->setObjectName("fixed");

    _customPlot->replot();
}

void CentralWidget::slotDelCurve(QCPCurve *curve) {
    if (!curve) {
        logde << "current curve is nullptr.";
        return;
    }

    logde << "delete curve object name:" << curve->objectName().toStdString();

    deleteCurveByObjectName(curve->objectName());
}

void CentralWidget::slotGetAxisInfo() {
    QVector<AxisInfo> vtr;

    auto func = [&](QCPAxis *axis) {
        AxisInfo ai;
        ai.visiable = axis->visible();
        ai.lower    = axis->range().lower;
        ai.upper    = axis->range().upper;

        vtr.push_back(ai);
    };

    func(_customPlot->xAxis);
    func(_customPlot->yAxis);
    func(_customPlot->yAxis2);

    emit sigGetAxisInfoWithData(vtr);
}

void CentralWidget::slotSetAxisSettings(const QVector<double> vtr) {
    _customPlot->xAxis->setRange(vtr.at(0), vtr.at(1));
    _customPlot->yAxis->setRange(vtr.at(2), vtr.at(3));

    if (_customPlot->yAxis2->visible()) {
        _customPlot->yAxis2->setRange(vtr.at(4), vtr.at(5));
    }

    _customPlot->replot();
}

void CentralWidget::timerEvent(QTimerEvent *event) {
    _customPlot->replot();

#if 0
    // key的单位是秒
    double key = QDateTime::currentDateTime().toMSecsSinceEpoch() / 1000.0;
    // 添加数据
    // 使用随机数产生一条曲线
    // double value0 = realDataI();
    double value0 = QRandomGenerator::global()->bounded(10.123);
    _customPlot->graph(0)->addData(key, value0); // 添加数据到曲线

    // 删除8秒之前的数据。这里的8要和下面设置横坐标宽度的8配合起来
    // 才能起到想要的效果，可以调整这两个值，观察显示的效果。
    _customPlot->graph(0)->data()->remove(key - 80);
    // 自动设定graph(1)曲线y轴的范围，如果不设定，有可能看不到图像
    // 也可以用ui->customPlot->yAxis->setRange(up,low)手动设定y轴范围
    _customPlot->graph(0)->rescaleValueAxis(true);

    // 这里的8，是指横坐标时间宽度为8秒，如果想要横坐标显示更多的时间
    // 就把8调整为比较大到值，比如要显示60秒，那就改成60。
    // 这时removeDataBefore(key-8)中的8也要改成60，否则曲线显示不完整。

    _customPlot->yAxis->setRange(0, 20);                          // 设定x轴的范围
    _customPlot->xAxis->setRange(key + 0.25, 80, Qt::AlignRight); // 设定x轴的范围
    _customPlot->replot();
#endif
}

void CentralWidget::slotSelectionChangedByUser() {
    // 获取所有被选中的绘图对象
    QList<QCPAbstractPlottable *> selectedPlottables = _customPlot->selectedPlottables();

    // 遍历选中的绘图对象
    for (QCPAbstractPlottable *plottable : selectedPlottables) {
        // 检查是否是 QCPCurve
        if (QCPCurve *curve = dynamic_cast<QCPCurve *>(plottable)) {
            logde << "selected Curve:" << curve->objectName().toStdString();
            _currentCurve = curve;    // 更新当前选中的曲线
        }
    }
    // 输出xy轴最值
    auto  *ax   = _customPlot->xAxis;
    auto  *ay   = _customPlot->yAxis;
    double xMin = ax->range().lower;
    double xMax = ax->range().upper;
    double yMin = ay->range().lower;
    double yMax = ay->range().upper;

    logde << "min/max:" << xMin << "," << xMax << "," << yMin << "," << yMax;
}

void CentralWidget::uiLoadXlsxFileData() {
#if 1
    // Set axis.
    _customPlot->yAxis->setVisible(true);
    _customPlot->yAxis->setLabel(AxisDSC);

    if (_axisMode == AxisMode::SingleY) {
        _customPlot->xAxis->setLabel(AxisTemperature);

        _customPlot->yAxis2->setVisible(false);
    } else {
        _customPlot->xAxis->setLabel(AxisTime);

        _customPlot->yAxis2->setVisible(true);
        _customPlot->yAxis2->setLabel(AxisTemperature);
    }

    _customPlot->replot();
#endif

#if 1
    if (Global::_curveFileDataVtr.empty()) {
        return;
    }
    // Load xlsx file data.
    QVector<ExperimentData> allDataVtr;
    for (Global::CurveFileData &cfd : Global::_curveFileDataVtr) {
        for (int i = 0; i < cfd.phaseTotalVtr.size(); i++) {
            Global::PhaseTotalInfo &pti = cfd.phaseTotalVtr[i];

            allDataVtr.append(pti.dataVtr);

            // Load data.
            QVector<double> tVtr, tempVtr, timeVtr, dscVtr;
            int             index = 0;
            for (Global::ExperimentData &ed : pti.dataVtr) {
                tVtr.push_back(index++);
#if 0
                if(_axisMode == AxisMode::SingleY){
                    xTempVtr.push_back(ed.sampleTemp);
                }else{
                    xTimeVtr.push_back(ed.runTime);
                }
#endif
                tempVtr.push_back(ed.sampleTemp);
                timeVtr.push_back(ed.runTime);

                dscVtr.push_back(ed.dsc);
            }

            _currentCurve = new QCPCurve(_customPlot->xAxis, _customPlot->yAxis);
            if (_axisMode == AxisMode::SingleY) {
                _currentCurve->setData(tVtr, tempVtr, dscVtr);
            } else {
                _currentCurve->setData(tVtr, timeVtr, dscVtr);
            }
            _currentCurve->setSelectable(QCP::stWhole);       // 设置曲线可选
            _currentCurve->setLineStyle(QCPCurve::lsLine);    // 线性连接
            _currentCurve->setObjectName(cfd.filePath);

            // add the second axis y curve data.
            if (_axisMode == AxisMode::DoubleY) {
                QCPCurve *secondCurve = new QCPCurve(_customPlot->xAxis, _customPlot->yAxis2);
                secondCurve->setData(tVtr, timeVtr, tempVtr);
                secondCurve->setSelectable(QCP::stWhole);       // 设置曲线可选
                secondCurve->setLineStyle(QCPCurve::lsLine);    // 线性连接

                secondCurve->setObjectName(cfd.filePath);

                // 设置曲线颜色
                QPen pen(Qt::red);           // 创建一个红色的画笔
                pen.setWidth(1);             // 设置画笔宽度
                secondCurve->setPen(pen);    // 将画笔应用到曲线
            }
#if 0
            // Set axis range.
            QPair<float, float> minMaxXAxisPair;
            if(_axisMode == AxisMode::SingleY){
                // Axis x is temperature value.
                minMaxXAxisPair = PointCalculate::getMinAndMaxOfSampleTemp();
            }else{
                // Axis x is time value.
                minMaxXAxisPair = PointCalculate::getMinAndMaxOfRunTime();
            }
            QPair<float, float>newXAxisPair = PointCalculate::getMinAndMaxOfAxis(
                        minMaxXAxisPair.first,minMaxXAxisPair.second);
            _customPlot->xAxis->setRange(newXAxisPair.first, newXAxisPair.second);

            QPair<float, float> minMaxYAxisPair = PointCalculate::getMinAndMaxOfDSC();
            QPair<float, float>newYAxisPair = PointCalculate::getMinAndMaxOfAxis(
                        minMaxYAxisPair.first,minMaxYAxisPair.second);
            _customPlot->yAxis->setRange(newYAxisPair.first ,
                                         newYAxisPair.second);
#endif
            //
            pti.curve = _currentCurve;
        }

        // Axis setting.
        PointCalculate::setAnalysisData(allDataVtr);
        // Set axis range.
        QPair<float, float> minMaxXAxisPair;
        if (_axisMode == AxisMode::SingleY) {
            // Axis x is temperature value.
            minMaxXAxisPair = PointCalculate::getMinAndMaxOfSampleTemp();
        } else {
            // Axis x is time value.
            minMaxXAxisPair = PointCalculate::getMinAndMaxOfRunTime();

            // Set yAxis2 range.
            QPair<float, float> minAndMaxPair = PointCalculate::getMinAndMaxOfSampleTemp();
            QPair<float, float> y2AxisPair    = PointCalculate::getMinAndMaxOfAxis(
                minAndMaxPair.first, minAndMaxPair.second);

            _customPlot->yAxis2->setRange(y2AxisPair.first, y2AxisPair.second);
        }
        QPair<float, float> newXAxisPair = PointCalculate::getMinAndMaxOfAxis(
            minMaxXAxisPair.first, minMaxXAxisPair.second);
        _customPlot->xAxis->setRange(newXAxisPair.first, newXAxisPair.second);

        QPair<float, float> minMaxYAxisPair = PointCalculate::getMinAndMaxOfDSC();
        QPair<float, float> newYAxisPair    = PointCalculate::getMinAndMaxOfAxis(
            minMaxYAxisPair.first, minMaxYAxisPair.second);
        _customPlot->yAxis->setRange(newYAxisPair.first,
                                     newYAxisPair.second);

        // Add analysis operation data.
#if 0
        logde << "analysis operation vtr size:"
              << cfd.analysisOperationVtr.size();
#endif

        if (!cfd.analysisOperationVtr.empty()) {
            for (AnaOpRecorder::AnalysisOperation &ao : cfd.analysisOperationVtr) {
                calculateAnalysisResult(ao.mode, ao.x1, ao.x2, cfd.filePath);
            }
        }
    }
    // Refresh ui.
    _customPlot->replot();
#endif
}

void CentralWidget::glassTransitionHandle(const double x1, const double x2, const QString objectName) {
    QVector<double> tickPositions = _customPlot->xAxis->tickVector();
    double          step          = tickPositions.at(1) - tickPositions.at(0);
    logde << "glassTransitionHandle step:" << step;
    double extendedLineStep = step / 2;


    QPointF point1 = PointCalculate::getClosestPointByX(x1);
    QPointF point2 = PointCalculate::getClosestPointByX(x2);

    logde << "point1:" << point1.x() << "," << point1.y();
    logde << "point2:" << point2.x() << "," << point2.y();

    // 获取与选择点接近的10个点
    QVector<QPointF> point1Vtr = PointCalculate::getNearbyPointGroupByX(point1.x());
    QVector<QPointF> point2Vtr = PointCalculate::getNearbyPointGroupByX(point2.x());

    QVector<double> xVtr, yVtr;
    for (QPointF &p : point1Vtr) {
        xVtr.append(p.x());
        yVtr.append(p.y());
    }

    PointCalculate::Line line1 = calculateLinearRegression(xVtr, yVtr);

#if 1
    // line1
    xVtr.clear();
    yVtr.clear();

    xVtr.push_back(point1.x());
    yVtr.push_back(point1.y());

    xVtr.push_back(point1.x() + extendedLineStep);
    double y1 = line1.slope * (point1.x() + extendedLineStep) + line1.intercept;
    yVtr.push_back(y1);

    QCPGraph *lineGraph1 = _customPlot->addGraph();
    lineGraph1->setData(xVtr, yVtr);

    ItemManager::addTemporaryQCPGraph(lineGraph1, objectName);

    QPen pen;
    pen.setColor(Qt::darkGreen);
    lineGraph1->setPen(pen);
#endif

    // line2
    xVtr.clear();
    yVtr.clear();
    for (QPointF &p : point2Vtr) {
        xVtr.append(p.x());
        yVtr.append(p.y());
    }

    PointCalculate::Line line2 = calculateLinearRegression(xVtr, yVtr);

#if 0
    QVector<double> xVtr2,yVtr2;
    xVtr2.push_back(point2.x());
    yVtr2.push_back(point2.y());

    xVtr2.push_back(point2.x() - 10);
    double y2 = line2.slope * (point2.x() - 10) + line2.intercept;
    yVtr2.push_back(y2);

    QCPGraph *lineGraph2 = _customPlot->addGraph();
    lineGraph2->setData(xVtr2, yVtr2);
    lineGraph2->setPen(pen);

    ItemManager::addTemporaryQCPGraph(lineGraph2,objectName);

    _customPlot->replot();
#endif

    // inflection point.
    QVector<QPointF> originalPointVtr = PointCalculate::getPointVtrInXRange(point1.x(), point2.x());

    QVector<QPointF> pointVtr = originalPointVtr;

    xVtr.clear();
    yVtr.clear();
    for (QPointF &p : pointVtr) {
        xVtr.append(p.x());
        yVtr.append(p.y());
    }
    QMap<double, PointCalculate::Line> tangentLines =
        PointCalculate::calculateTangentLine(xVtr, yVtr);

    double               targetX = 0.0;
    PointCalculate::Line targetLine;
    double               maxSlopeAbs = 0.0;    // 初始化最大斜率绝对值为0

    //
    for (auto it = tangentLines.begin(); it != tangentLines.end(); ++it) {
        if (std::fabs(it.value().slope) > maxSlopeAbs) {
            maxSlopeAbs = std::fabs(it.value().slope);    // 更新最大斜率绝对值

            targetX    = it.key();
            targetLine = it.value();
        }
    }

    int     index = 0;
    QPointF prePoint, currentPoint, lastPoint;
    for (int i = 0; i < pointVtr.size(); i++) {
        currentPoint = pointVtr[i];
        if (currentPoint.x() == targetX) {
            index = i;
            break;
        }
    }

    prePoint  = pointVtr[index - 1];
    lastPoint = pointVtr[index + 1];

    targetLine.slope     = PointCalculate::calculateSlope(lastPoint.x(), lastPoint.y(), prePoint.x(), prePoint.y());
    targetLine.intercept = currentPoint.y() - targetLine.slope * currentPoint.x();
    //

    QPointF intersection1 = PointCalculate::getIntersection(targetLine, line1);
    QPointF intersection2 = PointCalculate::getIntersection(targetLine, line2);

    // line 3
    xVtr.clear();
    yVtr.clear();

#if 1
    xVtr.push_back(intersection1.x());
    yVtr.push_back(intersection1.y());

    xVtr.push_back(intersection2.x());
    yVtr.push_back(intersection2.y());
#endif

    QCPGraph *lineGraph3 = _customPlot->addGraph();
    lineGraph3->setData(xVtr, yVtr);
    lineGraph3->setPen(pen);

    ItemManager::addTemporaryQCPGraph(lineGraph3, objectName);

    _customPlot->replot();

    // draw line 2
    QVector<double> xVtr2, yVtr2;
    xVtr2.push_back(point2.x());
    yVtr2.push_back(point2.y());

#if 0
    xVtr2.push_back(point2.x() - 10);
    double y2 = line2.slope * (point2.x() - 10) + line2.intercept;
    yVtr2.push_back(y2);
#endif

    double minDistance = PointCalculate::calculateDistance(point2, intersection2);
    // 计算延长线另一个端点，使线长度超过minDistance.
    double  halfOfExtendedLineStep = extendedLineStep / 2;
    double  distance               = 0.0;
    int     power                  = 0;
    QPointF line2Endpoint;
    while (distance < minDistance) {
        power++;

        line2Endpoint.setX(point2.x() - halfOfExtendedLineStep * power);
        line2Endpoint.setY(line2.slope * (point2.x() - halfOfExtendedLineStep * power)
                           + line2.intercept);

        distance = PointCalculate::calculateDistance(
            point2, line2Endpoint);
    }

    xVtr2.push_back(line2Endpoint.x());
    yVtr2.push_back(line2Endpoint.y());

    QCPGraph *lineGraph2 = _customPlot->addGraph();
    lineGraph2->setData(xVtr2, yVtr2);
    lineGraph2->setPen(pen);

    ItemManager::addTemporaryQCPGraph(lineGraph2, objectName);

    _customPlot->replot();

    // point value
    double averageY = (intersection1.y() + intersection2.y()) / 2;

    QPointF averagePoint = PointCalculate::getClosestPointByY(point1.x(), point2.x(), averageY);

    QString str = PointCalculate::textFormatGlassTranstion(intersection1.x(),
                                                           averagePoint.x(),
                                                           intersection2.x());
    if (Global::_axisMode == Global::AxisMode::DoubleY) {
        Global::ExperimentData intersection1Ed =
            PointCalculate::getClosestDataByTemperature(intersection1.x());
        Global::ExperimentData averageEd =
            PointCalculate::getClosestDataByTemperature(averagePoint.x());
        Global::ExperimentData intersection2Ed =
            PointCalculate::getClosestDataByTemperature(intersection2.x());

        averagePoint = QPointF(averageEd.runTime, averageEd.dsc);

        str = PointCalculate::textFormatGlassTranstionWithTime(
            intersection1Ed.runTime,
            averageEd.runTime,
            intersection2Ed.runTime);
    }

    drawText(averagePoint, str);
}
// 使用最小二乘法计算线性回归
PointCalculate::Line CentralWidget::calculateLinearRegression(const QVector<double> &x, const QVector<double> &y) {
    PointCalculate::Line line;
    double               sum_x = 0, sum_y = 0, sum_xy = 0, sum_xx = 0;
    size_t               n = x.size();

    // 计算所需的总和
    for (size_t i = 0; i < n; ++i) {
        sum_x += x[i];
        sum_y += y[i];
        sum_xy += x[i] * y[i];
        sum_xx += x[i] * x[i];
    }

    // 计算斜率和截距
    double x_mean      = sum_x / n;
    double y_mean      = sum_y / n;
    double numerator   = n * sum_xy - sum_x * sum_y;
    double denominator = n * sum_xx - sum_x * sum_x;
    line.slope         = numerator / denominator;
    line.intercept     = y_mean - line.slope * x_mean;

    return line;
}

QPointF CentralWidget::onsetTemperaturePointHandle(const double x1, const double x2) {
    Global::ExperimentData ed = PointCalculate::findOnSetDataByTemperature(x1, x2);

    if (Global::_axisMode == Global::AxisMode::SingleY) {
        return QPointF(ed.sampleTemp, ed.dsc);
    } else {
        return QPointF(ed.runTime, ed.dsc);
    }
}

QPointF CentralWidget::endSetTemperaturePointHandle(const double x1, const double x2) {
    Global::ExperimentData ed = PointCalculate::findEndSetDataByTemperature(x1, x2);

    if (Global::_axisMode == Global::AxisMode::SingleY) {
        return QPointF(ed.sampleTemp, ed.dsc);
    } else {
        return QPointF(ed.runTime, ed.dsc);
    }
}

// 二次多项式拟合函数
void CentralWidget::quadraticFit(const QVector<QPointF> &points, double &a, double &b, double &c) {
    int n = points.size();
    if (n < 3) {
        throw std::invalid_argument("At least three points are required for quadratic fit.");
    }

    double sumX = 0, sumY = 0, sumXX = 0, sumXY = 0, sumX2Y = 0;
    for (const QPointF &p : points) {
        sumX += p.x();
        sumY += p.y();
        sumXX += p.x() * p.x();
        sumXY += p.x() * p.y();
        sumX2Y += p.x() * p.x() * p.y();
    }

    double det = n * (sumXX * sumY - sumX * sumXY) - sumX * sumX * sumY + sumX * sumX2Y;
    a          = (sumXX * sumY - sumX * sumXY) / det;
    b          = (sumX2Y - n * sumXY) / det;
    c          = (sumY - a * sumX - b * sumX) / n;
}

// 计算二次多项式拟合曲线的导数（切线斜率）
double CentralWidget::derivativeAt(const double a, const double b, const double x) {
    return 2 * a * x + b;
}


void CentralWidget::setEventHandlerEnable(const bool flag) {
    logde << "setEventHandlerEnable..." << flag;

    // line visiable func
    auto lineVisiableFunc = [&](QCPItemStraightLine *line) {
        line->setSelected(flag);
        line->setVisible(flag);
    };

    // todo. 当竖线隐藏时，需要设置不可选择模式。
    //    _eventHandler->setEnable(flag);

#if 1
    // move line to suitable position.
    double xMax = _customPlot->xAxis->range().upper;
    double xMin = _customPlot->xAxis->range().lower;

    //    logde<<"xMax:"<<xMax;

    //    QVector<double> ticks = _customPlot->xAxis->tickVector();
    //    int numTicks = ticks.size();
    //    logde<<"ticks:"<<numTicks;

    double range  = xMax - xMin;
    double xLeft  = xMin + range / 3;
    double xRight = xMin + range * 2 / 3;

    _line1->point1->setCoords(xLeft, _line1->point1->coords().y());
    _line1->point2->setCoords(xLeft, _line1->point2->coords().y());

    _line2->point1->setCoords(xRight, _line2->point1->coords().y());
    _line2->point2->setCoords(xRight, _line2->point2->coords().y());
#endif

    lineVisiableFunc(_line1);
    if (AnalysisMode::NumericalLabel != _analysisMode) {
        lineVisiableFunc(_line2);
    }
    //
    _customPlot->replot();
}

QPointF CentralWidget::getTheCoordinatesOfTheTextBox(const QPointF point) {
#if 0
    double xMax = _customPlot->xAxis->range().upper;
    double xMin = _customPlot->xAxis->range().lower;

    logde << "xMax:" << xMax;

    QVector<double> ticks    = _customPlot->xAxis->tickVector();
    int             numTicks = ticks.size();
    //    logde<<"ticks:"<<numTicks;

    double distance = (xMax - xMin) / ticks.size();

    return QPointF(point.x() + distance, point.y());
#endif
    // 输出xy轴最值
    auto  *ax   = _customPlot->xAxis;
    auto  *ay   = _customPlot->yAxis;
    double xMin = ax->range().lower;
    double xMax = ax->range().upper;
    double yMin = ay->range().lower;
    double yMax = ay->range().upper;

    logde << "min/max:" << xMin << "," << xMax << "," << yMin << "," << yMax;

    double xDistance = (xMax - xMin);
    double yDistance = (yMax - yMin);

    return QPointF(xMin + xDistance / 5 * 4,
                   yMin + yDistance / 5);
}

void CentralWidget::drawText(const QPointF point, const QString text, const QString objectName) {
    QPointF textBoxPoint = getTheCoordinatesOfTheTextBox(point);

    // 创建标注文字（QCPItemText）
    QCPItemText *textLabel = new QCPItemText(_customPlot);
    textLabel->setPositionAlignment(Qt::AlignCenter);                      // 对齐方式
    textLabel->position->setType(QCPItemPosition::ptPlotCoords);           // 使用数据坐标
    textLabel->position->setCoords(textBoxPoint.x(), textBoxPoint.y());    // 设置文本位置在指定点上方

    textLabel->setText(text);    // 设置文本内容
    QFont font(QFont("Arial", 10));
    textLabel->setFont(font);
    textLabel->setSelectedFont(font);
    textLabel->setSelectable(true);    // 设置为可选

    // 设置文本项的位置类型为屏幕坐标比例
    textLabel->position->setTypeX(QCPItemPosition::ptAxisRectRatio);
    textLabel->position->setTypeY(QCPItemPosition::ptAxisRectRatio);

    ItemManager::addTemporaryQCPItemText(textLabel, objectName);

    // 创建指向点的线段（QCPItemLine）
    QCPItemLine *arrow = new QCPItemLine(_customPlot);
    arrow->start->setParentAnchor(textLabel->left);    // 线段起点绑定到标注文字底部
    arrow->end->setCoords(point.x(), point.y());       // 线段终点设置为指定的点
    arrow->setHead(QCPLineEnding::esSpikeArrow);       // 添加箭头
    arrow->setPen(QPen(Qt::red, 1));

    ItemManager::addTemporaryQCPItemLine(arrow, objectName);

    // 重绘图表以显示文本标签和箭头
    _customPlot->replot();
}

void CentralWidget::fillGraph(const double x1, const double x2, const QString objectName) {
    Global::ExperimentData x1Ed = PointCalculate::getClosestDataByTemperature(x1);
    Global::ExperimentData x2Ed = PointCalculate::getClosestDataByTemperature(x2);
    QVector<double>        xVtr, yVtr;
    xVtr.push_back(x1Ed.sampleTemp);
    xVtr.push_back(x2Ed.sampleTemp);

    yVtr.push_back(x1Ed.dsc);
    yVtr.push_back(x2Ed.dsc);

    if (_axisMode == AxisMode::DoubleY) {
        x1Ed = PointCalculate::getClosestDataByTime(x1);
        x2Ed = PointCalculate::getClosestDataByTime(x2);

        xVtr.clear();

        xVtr.push_back(x1Ed.runTime);
        xVtr.push_back(x2Ed.runTime);

        yVtr.clear();
        yVtr.push_back(x1Ed.dsc);
        yVtr.push_back(x2Ed.dsc);
    }

    QCPGraph *mainGraph = _customPlot->addGraph();
    mainGraph->setData(xVtr, yVtr);
    mainGraph->setPen(QPen(Qt::red, 1));

    //
    QVector<Global::ExperimentData> curveDataVtr =
        PointCalculate::getDataInXRange(x1, x2);
    if (_axisMode == AxisMode::DoubleY) {
        curveDataVtr = PointCalculate::getDataInXRangeByTime(x1, x2);
    }

    QCPGraph       *fillGraph = _customPlot->addGraph();
    QVector<double> fillX, fillY;

    for (int i = 0; i < curveDataVtr.size(); i++) {
        Global::ExperimentData &ed = curveDataVtr[i];
        if (_axisMode == AxisMode::DoubleY) {
            fillX << ed.runTime;
        } else {
            fillX << ed.sampleTemp;
        }
        fillY << ed.dsc;
    }

    fillGraph->setData(fillX, fillY);

    fillGraph->setBrush(QBrush(Qt::green, Qt::SolidPattern));
    fillGraph->setChannelFillGraph(mainGraph);

    //
    ItemManager::addTemporaryQCPGraph(mainGraph, objectName);
    ItemManager::addTemporaryQCPGraph(fillGraph, objectName);

    _customPlot->replot();
}

void CentralWidget::clearData(const CentralWidget::ClearDataMode mode) {
    switch (mode) {
        case ClearDataMode::All:
            _analysisFilePathVtr.clear();
            // Clear data.
            logde << "clearExperimentData...";
            Global::clearExperimentData();

            Global::_curveFileDataVtr.clear();

            AnalysisOperationRecorder::_analysisOperationVtr.clear();

            // Set lines visiable false.
            _line1->setVisible(false);
            _line2->setVisible(false);

            _currentCurve = nullptr;

            setAxisMode(Global::AxisMode::SingleY);
        case ClearDataMode::JustUi:
            // ui
            clearAllUiData();
            break;
        case ClearDataMode::Undo: {
            // Clear the data of graph.
            for (int i = _customPlot->plottableCount() - 1; i >= 0; --i) {
                QCPAbstractPlottable *plottable = _customPlot->plottable(i);
                if (auto curve = dynamic_cast<QCPCurve *>(plottable)) {
                    logde << "clear data,curve object Name:" << curve->objectName().toStdString();
                    if (curve->objectName().contains(ItemManager::TemporaryStr)) {
                        _customPlot->removePlottable(curve);

                        ItemManager::removeItem(curve);
                    }
                }
            }

            // Clear graph on plot.
            for (int i = _customPlot->graphCount() - 1; i >= 0; --i) {
                QCPGraph *graph = _customPlot->graph(i);
                logde << "clear data,graph object Name:" << graph->objectName().toStdString();
                if (graph && graph->objectName().contains(ItemManager::TemporaryStr)) {
                    _customPlot->removeGraph(graph);

                    ItemManager::removeItem(graph);
                }
            }

            // Delete items.
            QList<QCPAbstractItem *> itemsToKeep;
            itemsToKeep << _line1 << _line2;

            for (int i = _customPlot->itemCount() - 1; i >= 0; --i) {
                QCPAbstractItem *item = _customPlot->item(i);
                logde << "item data,graph object Name:" << item->objectName().toStdString();
                if (item && !itemsToKeep.contains(item)
                    && item->objectName().contains(ItemManager::TemporaryStr)) {
                    _customPlot->removeItem(item);

                    ItemManager::removeItem(item);
                }
            }

            //
            AnalysisOperationRecorder::removeTheLastAnalysisOperation();
        } break;
        default:
            break;
    }

    _customPlot->replot();
}

void CentralWidget::clearAllUiData() {
    // Clear the data of graph.
    for (int i = _customPlot->plottableCount() - 1; i >= 0; --i) {
        QCPAbstractPlottable *plottable = _customPlot->plottable(i);
        if (auto curve = dynamic_cast<QCPCurve *>(plottable)) {
            _customPlot->removePlottable(curve);

            ItemManager::removeItem(curve);
        }
    }

    // Clear graph on plot.
    for (int i = _customPlot->graphCount() - 1; i >= 0; --i) {
        QCPGraph *graph = _customPlot->graph(i);
        _customPlot->removeGraph(graph);

        ItemManager::removeItem(graph);
    }

    // Delete items.
    QList<QCPAbstractItem *> itemsToKeep;
    itemsToKeep << _line1 << _line2;

    for (int i = _customPlot->itemCount() - 1; i >= 0; --i) {
        QCPAbstractItem *item = _customPlot->item(i);
        if (!itemsToKeep.contains(item)) {
            _customPlot->removeItem(item);

            ItemManager::removeItem(item);
        }
    }
}

void CentralWidget::deleteCurveByObjectName(const QString objectName) {
    logde << "deleteCurveByObjectName..." << objectName.toStdString();

    for (int i = _analysisFilePathVtr.size() - 1; i >= 0; --i) {
        if (_analysisFilePathVtr[i].startsWith(objectName)) {
            _analysisFilePathVtr.removeAt(i);    // 从后往前删除避免索引错乱
        }
    }

    ItemManager::removeItemsByObjectName(objectName);

    AnalysisOperationRecorder::removeAnalysisOperationByObjectName(objectName);

    // Global::_curveFileDataVtr 从全局文件数据中删除制定文件数据。
    for (int i = Global::_curveFileDataVtr.size() - 1; i >= 0; --i) {
        if (Global::_curveFileDataVtr[i].filePath == objectName) {
            Global::_curveFileDataVtr.removeAt(i);    // 从后往前删除避免索引错乱
        }
    }

    // Clear the data of graph.
    for (int i = _customPlot->plottableCount() - 1; i >= 0; --i) {
        QCPAbstractPlottable *plottable = _customPlot->plottable(i);
        if (auto curve = dynamic_cast<QCPCurve *>(plottable)) {
            if (curve->objectName().contains(objectName)) {
                logde << "1 clear data,curve object Name:" << curve->objectName().toStdString();

                _customPlot->removePlottable(curve);

                ItemManager::removeItem(curve);
            }
        }
    }

    // Clear graph on plot.
    for (int i = _customPlot->graphCount() - 1; i >= 0; --i) {
        QCPGraph *graph = _customPlot->graph(i);
        logde << "clear data,graph object Name:" << graph->objectName().toStdString();
        if (graph && graph->objectName().contains(objectName)) {
            _customPlot->removeGraph(graph);

            ItemManager::removeItem(graph);
        }
    }

    // Delete items.
    QList<QCPAbstractItem *> itemsToKeep;
    itemsToKeep << _line1 << _line2;

    for (int i = _customPlot->itemCount() - 1; i >= 0; --i) {
        QCPAbstractItem *item = _customPlot->item(i);
        logde << "item data,graph object Name:" << item->objectName().toStdString();
        if (item && !itemsToKeep.contains(item)
            && item->objectName().contains(objectName)) {
            _customPlot->removeItem(item);

            ItemManager::removeItem(item);
        }
    }

    _customPlot->replot();
}

void CentralWidget::calculateAnalysisResult(
    const AnalysisMode mode, const double x1, const double x2, const QString objectName) {
    logde << "calculateAnalysisResult...";
    logde << "x1,x2:" << x1 << "," << x2;

    switch (mode) {
        case AnalysisMode::NumericalLabel: {
            QPointF selectPoint;
            QString str;
            if (_axisMode == AxisMode::SingleY) {
                Global::ExperimentData ed = PointCalculate::getClosestDataByTemperature(x1);

                if (Global::isZero(ed.dsc)) {
                    QMessageBox::warning((QWidget *)this->parent(), "warnning", "曲线选择错误.");
                    return;
                }

                selectPoint = QPointF(ed.sampleTemp, ed.dsc);
                str         = PointCalculate::textFormatNumbericalLabel(selectPoint);
            } else {
                Global::ExperimentData ed = PointCalculate::getClosestDataByTime(x1);

                if (Global::isZero(ed.dsc)) {
                    QMessageBox::warning((QWidget *)this->parent(), "warnning", "曲线选择错误.");
                    return;
                }
                selectPoint = QPointF(ed.runTime, ed.dsc);
                str         = PointCalculate::textFormatNumbericalLabelWithTime(selectPoint);
            }

            drawText(selectPoint, str, objectName);

            break;
        }
        case AnalysisMode::StartPoint:
        case AnalysisMode::StopPoint: {
            PointCalculate::setRegionPointX(x1, x2);

            //        QPair<QPointF, QPointF> startEndPointPair =
            //                PointCalculate::calculateStartAndEndPoint();

            QPair<Global::ExperimentData, Global::ExperimentData>
                startEndDataPair = PointCalculate::calculateStartAndEndData();

            //
            QPointF point;
            QString str;

            if (mode == AnalysisMode::StartPoint) {
                if (_axisMode == AxisMode::SingleY) {
                    point = QPointF(startEndDataPair.first.sampleTemp, startEndDataPair.first.dsc);
                    str   = PointCalculate::textFormatStartPoint(point);
                } else {
                    point = QPointF(startEndDataPair.first.runTime, startEndDataPair.first.dsc);
                    str   = PointCalculate::textFormatStartPointWithTime(point);
                }
            } else {
                if (_axisMode == AxisMode::SingleY) {
                    point = QPointF(startEndDataPair.second.sampleTemp, startEndDataPair.second.dsc);
                    str   = PointCalculate::textFormatEndPoint(point);
                } else {
                    point = QPointF(startEndDataPair.second.runTime, startEndDataPair.second.dsc);
                    str   = PointCalculate::textFormatEndPointWithTime(point);
                }
            }

            drawText(point, str, objectName);

            //
            break;
        }
        case AnalysisMode::PeakSynthesisAnalysis: {
            logde << "PeakSynthesisAnalysis...";

            fillGraph(x1, x2, objectName);

            PointCalculate::setRegionPointX(x1, x2);

            // enthalpy
            double sampleWeight = 1.0f;
            if (Global::_curveFileDataVtr.empty()) {
                sampleWeight = Global::converStrToDouble(Global::_experimentInfo.sampleWeight);
            } else {
                for (Global::CurveFileData &cfd : Global::_curveFileDataVtr) {
                    for (Global::PhaseTotalInfo &pti : cfd.phaseTotalVtr) {
                        if (_currentCurve) {
                            if (_currentCurve == pti.curve) {
                                sampleWeight = cfd.ei.sampleWeight.toDouble();
                            }
                        }
                    }
                }
            }

            if (sampleWeight <= 0) {
                logde << "sample weight set value 1," << sampleWeight;
                sampleWeight = 1;
            }
            logde << "sample weight:" << sampleWeight;

            double enthalpyValue = PointCalculate::calculateArea(x1, x2) / sampleWeight;
            logde << "enthalpyValue:" << enthalpyValue;

            // peak
            QPointF peakPoint = PointCalculate::getPeakPoint();
            if (_axisMode == AxisMode::DoubleY) {
                peakPoint = PointCalculate::getPeakPointByTime();
            }

            // start point and end point
            QPair<QPointF, QPointF> startEndPointPair =
                PointCalculate::calculateStartAndEndPoint();

            logde << "start,end:" << startEndPointPair.first.x() << ","
                  << startEndPointPair.second.x();

            QString str;
            if (_axisMode == AxisMode::DoubleY) {
                QPair<QPointF, QPointF> selectedPointsPair = PointCalculate::getSelectedPoints();

                str = PointCalculate::textFormatPeakPointWithTime(
                    enthalpyValue,
                    peakPoint.x(),
                    selectedPointsPair.first.x(),
                    selectedPointsPair.second.x());

                drawText(peakPoint, str, objectName);
            } else if (Global::_displayTimeValue) {
                double peakPointTime  = PointCalculate::obtainTimeValueBasedOnTemperatureValue(peakPoint.x());
                double startPointTime = PointCalculate::obtainTimeValueBasedOnTemperatureValue(startEndPointPair.first.x());
                double endPointTime   = PointCalculate::obtainTimeValueBasedOnTemperatureValue(startEndPointPair.second.x());

                str = PointCalculate::textFormatPeakPointWithTime(
                    enthalpyValue,
                    peakPointTime,
                    startPointTime,
                    endPointTime);

                drawText(peakPoint, str, objectName);

            } else {
                str = PointCalculate::textFormatPeakPoint(
                    enthalpyValue,
                    peakPoint.x(),
                    startEndPointPair.first.x(),
                    startEndPointPair.second.x());
                drawText(peakPoint, str, objectName);
            }

            //
            break;
        }
        case AnalysisMode::GlassTransition: {
            glassTransitionHandle(x1, x2, objectName);

            break;
        }
        case AnalysisMode::OnsetTemperaturePoint: {
            QPointF point = onsetTemperaturePointHandle(x1, x2);
            logde << "onset point:" << point.x() << "," << point.y();

            QString str = QString::number(point.x(), 'f', 3);
            drawText(point, str, objectName);

            break;
        }
        case AnalysisMode::EndsetTemperaturePoint: {
            QPointF point = endSetTemperaturePointHandle(x1, x2);

            QString str = QString::number(point.x(), 'f', 3);
            logde << "object name:" << objectName.toStdString();
            drawText(point, str, objectName);

            break;
        }
        case AnalysisMode::OIT: {
            logde << "oit x1,x2:" << x1 << "," << x2;

#if 0
        //
        QVector<double> tickPositions = _customPlot->yAxis->tickVector();
        double step = tickPositions.at(1) - tickPositions.at(0);

        //
        Global::ExperimentData startData = PointCalculate::getOITStartData(x1,x2);
        //        Global::ExperimentData endData = PointCalculate::findOnSetDataByTime(x1,x2);

        PointCalculate::setRegionPointX(x1,x2);
        QPair<Global::ExperimentData,Global::ExperimentData>
                startEndDataPair =  PointCalculate::calculateStartAndEndData();

        //        Global::ExperimentData ed = PointCalculate::findOnSetDataByTemperature(x1,x2);
        //        PointCalculate::setRegionPointX(x1,x2);


        //        QPair<Global::ExperimentData,Global::ExperimentData>
        //                startEndDataPair =  PointCalculate::calculateStartAndEndData();

        logde<<"start data time:"<<startData.runTime;
        logde<<"end data time:"<<startEndDataPair.second.runTime;
#endif
            QVector<double> tickPositions = _customPlot->yAxis->tickVector();
            double          step          = tickPositions.at(1) - tickPositions.at(0);

            QPair<QPointF, QPointF> startEndPointPair =
                PointCalculate::calculateOITStartAndEndDataByTime(x1, x2);

            Global::ExperimentData startData;
            startData.runTime = startEndPointPair.first.x();
            startData.dsc     = startEndPointPair.first.y();

            Global::ExperimentData endData;
            endData.runTime = startEndPointPair.second.x();
            endData.dsc     = startEndPointPair.second.y();

            logde << "start runtime:" << startData.runTime;
            logde << "end runtime:" << endData.runTime;

            drawOITLine(startData, endData, step, objectName);

            break;
        }
        default:
            break;
    }
}

void CentralWidget::drawOITLine(
    const Global::ExperimentData startData,
    const Global::ExperimentData endData,
    const int                    step,
    const QString                objectName) {
    logde << "step:" << step;

    double localStep = static_cast<double>(step) / 5 / 2;

    double startOffset = localStep;
    double endOffset   = localStep * 2;

    logde << "start/end offset:"
          << startOffset << "," << endOffset;

    double verticalLineStartY = startData.dsc - startOffset;
    double verticalLineEndY   = startData.dsc - endOffset;

    double totalLineDsc = startData.dsc - (endOffset - startOffset) * 1.5;

    // Draw start line.
    QCPItemLine *startLine = new QCPItemLine(_customPlot);
    startLine->start->setCoords(startData.runTime, verticalLineStartY);
    startLine->end->setCoords(startData.runTime, verticalLineEndY);
    startLine->setPen(QPen(Qt::red, 1));
    ItemManager::addTemporaryQCPItemLine(startLine, objectName);

    // Draw end line.
    QCPItemLine *endLine = new QCPItemLine(_customPlot);
    endLine->start->setCoords(endData.runTime, verticalLineStartY);
    endLine->end->setCoords(endData.runTime, verticalLineEndY);
    endLine->setPen(QPen(Qt::red, 1));
    ItemManager::addTemporaryQCPItemLine(endLine, objectName);

    // Draw total line.
    QCPItemLine *totalLine = new QCPItemLine(_customPlot);
    totalLine->start->setCoords(startData.runTime, totalLineDsc);
    totalLine->end->setCoords(endData.runTime, totalLineDsc);
    totalLine->setPen(QPen(Qt::red, 1));
    ItemManager::addTemporaryQCPItemLine(totalLine, objectName);

    // Draw title.
    double textX = (endData.runTime - startData.runTime) / 3 + startData.runTime;
    double textY = verticalLineEndY;

    QCPItemText *textLabel = new QCPItemText(_customPlot);
    textLabel->setPositionAlignment(Qt::AlignLeft);                 // 对齐方式
    textLabel->position->setType(QCPItemPosition::ptPlotCoords);    // 使用数据坐标
    textLabel->position->setCoords(textX, textY);                   // 设置文本位置在指定点上方

    double  elapseMin    = endData.runTime - startData.runTime;
    QString elapseMinStr = QString("OIT: ") + Global::converDoubleToStr(elapseMin)
                           + QString(" min");
    textLabel->setText(elapseMinStr);    // 设置文本内容
    QFont font(QFont("Arial", 10));
    textLabel->setFont(font);
    textLabel->setSelectedFont(font);
    textLabel->setSelectable(true);    // 设置为可选
    ItemManager::addTemporaryQCPItemText(textLabel, objectName);


    _customPlot->replot();
}

void CentralWidget::setAxisMode(AxisMode mode) {
    if (_axisMode != mode) {
        _axisMode = mode;

#if 0
        // Set axis.
        _customPlot->yAxis->setVisible(true);
        _customPlot->yAxis->setLabel(AxisDSC);

        if(_axisMode == AxisMode::SingleY){
            _customPlot->xAxis->setLabel(AxisTemperature);

            _customPlot->yAxis2->setVisible(false);
        }else{
            _customPlot->xAxis->setLabel(AxisTime);

            _customPlot->yAxis2->setVisible(true);
            _customPlot->yAxis2->setLabel(AxisTemperature);
        }

        _customPlot->replot();
#endif

        uiLoadXlsxFileData();
    }
}

#if 0
void CentralWidget::peakSynthesisDoubleAxisY(const double, const double,const QString objectName)
{

}

void CentralWidget::peakSynthesisSingleAxisY(const double x1, const double x2,const QString objectName)
{
    fillGraph(x1,x2,objectName);

    PointCalculate::setRegionPointX(x1,x2);

    //enthalpy
    double sampleWeight = 1.0f;
    if(Global::_curveFileDataVtr.empty()){
        sampleWeight = Global::converStrToDouble(Global::_experimentInfo.sampleWeight);
    }else{
        for(Global::CurveFileData& cfd:Global::_curveFileDataVtr){
            for(Global::PhaseTotalInfo& pti:cfd.phaseTotalVtr){
                if(_currentCurve){
                    if(_currentCurve == pti.curve){
                        sampleWeight = cfd.ei.sampleWeight.toDouble();
                    }else{
                    }
                }else{
                    //                    logde<<"current curve nullptr.";
                }
            }
        }
    }

    if(sampleWeight <= 0){
        logde<<"sample weight set value 1,"<<sampleWeight;
        sampleWeight = 1;
    }

    double enthalpyValue = PointCalculate::calculateArea(x1,x2) / sampleWeight;
    logde<<"enthalpyValue:"<<enthalpyValue;

    // peak
    QPointF peakPoint = PointCalculate::getPeakPoint();

    // start point and end point
    QPair<QPointF, QPointF> startEndPointPair =
            PointCalculate::calculateStartAndEndPoint();

    logde<<"start,end:"<<startEndPointPair.first.x()<<","
        <<startEndPointPair.second.x();

    QString str;
    if(_axisMode == AxisMode::DoubleY){
        double peakPointTime = PointCalculate::obtainTimeValueBasedOnTemperatureValue(peakPoint.x());
        double startPointTime = PointCalculate::obtainTimeValueBasedOnTemperatureValue(startEndPointPair.first.x());
        double endPointTime = PointCalculate::obtainTimeValueBasedOnTemperatureValue(startEndPointPair.second.x());

        str =    PointCalculate::textFormatPeakPointWithTime(
                    enthalpyValue,
                    peakPointTime,
                    startPointTime,
                    endPointTime);

        Global::ExperimentData peakPointEd =
                PointCalculate::getClosestDataByTemperature(peakPoint.x());

        peakPoint = QPointF(peakPointEd.runTime,peakPointEd.dsc);

        drawText(peakPoint,str,objectName);
    }
    else if(Global::_displayTimeValue){
        double peakPointTime = PointCalculate::obtainTimeValueBasedOnTemperatureValue(peakPoint.x());
        double startPointTime = PointCalculate::obtainTimeValueBasedOnTemperatureValue(startEndPointPair.first.x());
        double endPointTime = PointCalculate::obtainTimeValueBasedOnTemperatureValue(startEndPointPair.second.x());

        str = PointCalculate::textFormatPeakPointWithTime(
                    enthalpyValue,
                    peakPointTime,
                    startPointTime,
                    endPointTime);

        drawText(peakPoint,str,objectName);

    }else{
        str = PointCalculate::textFormatPeakPoint(
                    enthalpyValue,
                    peakPoint.x(),
                    startEndPointPair.first.x(),
                    startEndPointPair.second.x());
        drawText(peakPoint,str,objectName);
    }
}
#endif

void CentralWidget::clearAllData() {
    _customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables);

    Global::_mode = Global::Mode::Analysis;
    clearData(ClearDataMode::All);
}

QPixmap CentralWidget::getPixMap() {
    _customPlot->replot();
    QApplication::processEvents();
    return _customPlot->toPixmap();
}

void CentralWidget::slotAxisModify(const float temp) {
    logde << "slotAxisModify...";

    _customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables);

    float value = temp + 20;

    _customPlot->xAxis->setRange(0, value);
    _customPlot->yAxis->setRange(-5, 5);
}
