// ============================================================================
//  qt_calculator_designer —— Day2 案例参考实现（Qt Designer 表单版）
//  对应教案：OUTLINE_4DAY.md Day2 下午「项目案例：简易计算器」
//  配套文档：docs/teaching/examples/calculator_designer/README.md
//  手工维护，非生成产物。
//
//  与 ../calculator/qt_calculator.cpp（纯代码 QGridLayout 版）对照阅读：
//  界面结构完全一致，唯一区别是键盘布局用 Qt Designer 表单
//  （calculator_designer.ui）设计——编译期由 uic 生成 ui_calculator_designer.h，
//  setupUi(this) 一次性把控件树 + 布局全部建好；业务逻辑（onDigit/onOperator/
//  onEquals/...）与代码版完全相同、原样照搬，刻意保持一致以突出「界面搭建方式
//  变了，逻辑代码不用变」。
//
//  教学重点（对照代码版新增的一条）：
//    Designer 表单里控件的 objectName 决定「自动连接」——setupUi() 内部会调用
//    QMetaObject::connectSlotsByName(this)，自动把名为 on_<objectName>_<signal>()
//    的槽连到对应控件的信号。本例改用显式 connect()（更贴近代码版、也更利于
//    团队协作排查），但这个自动连接特性是 Designer 工作流特有的第三种信号槽写法，
//    坑点见 README：控件改名后自动连接悄悄失效，不报编译错误也不报运行时错误。
//
//  离屏验证（自测模式）：QT_QPA_PLATFORM=offscreen tools/run_qt.sh qt_calculator_designer
//  自测用例与 ../calculator/ 完全一致，用于验证「界面来源不同、行为一致」。
// ============================================================================

#include <QApplication>
#include <QDebug>
#include <QTimer>
#include <QWidget>

#include "ui_calculator_designer.h"

class CalculatorDesigner : public QWidget {
    Q_OBJECT
public:
    explicit CalculatorDesigner(QWidget* parent = nullptr)
        : QWidget(parent), ui(new Ui::CalculatorForm) {
        ui->setupUi(this);   // 表单里的控件树 + QGridLayout 全部由 Designer XML 建好

        connect(ui->btn0, &QPushButton::clicked, this, [this] { onDigit(0); });
        connect(ui->btn1, &QPushButton::clicked, this, [this] { onDigit(1); });
        connect(ui->btn2, &QPushButton::clicked, this, [this] { onDigit(2); });
        connect(ui->btn3, &QPushButton::clicked, this, [this] { onDigit(3); });
        connect(ui->btn4, &QPushButton::clicked, this, [this] { onDigit(4); });
        connect(ui->btn5, &QPushButton::clicked, this, [this] { onDigit(5); });
        connect(ui->btn6, &QPushButton::clicked, this, [this] { onDigit(6); });
        connect(ui->btn7, &QPushButton::clicked, this, [this] { onDigit(7); });
        connect(ui->btn8, &QPushButton::clicked, this, [this] { onDigit(8); });
        connect(ui->btn9, &QPushButton::clicked, this, [this] { onDigit(9); });
        connect(ui->btnDot, &QPushButton::clicked, this, [this] { onDot(); });

        connect(ui->btnPlus, &QPushButton::clicked, this, [this] { onOperator(QLatin1Char('+')); });
        connect(ui->btnMinus, &QPushButton::clicked, this, [this] { onOperator(QLatin1Char('-')); });
        connect(ui->btnMul, &QPushButton::clicked, this, [this] { onOperator(QLatin1Char('*')); });
        connect(ui->btnDiv, &QPushButton::clicked, this, [this] { onOperator(QLatin1Char('/')); });

        connect(ui->btnEquals, &QPushButton::clicked, this, &CalculatorDesigner::onEquals);
        connect(ui->btnClear, &QPushButton::clicked, this, &CalculatorDesigner::onClear);
        connect(ui->btnBackspace, &QPushButton::clicked, this, &CalculatorDesigner::onBackspace);
    }

    ~CalculatorDesigner() override { delete ui; }

    // 供自测直接调用，效果等价于点击对应按钮；逻辑与 ../calculator/ 版一字不差。
    void onDigit(int digit) {
        if (m_error) onClear();
        if (m_startNew || ui->display->text() == QStringLiteral("0")) {
            ui->display->setText(QString::number(digit));
            m_startNew = false;
        } else {
            ui->display->setText(ui->display->text() + QString::number(digit));
        }
    }

    void onDot() {
        if (m_error) onClear();
        if (m_startNew) {
            ui->display->setText(QStringLiteral("0."));
            m_startNew = false;
            return;
        }
        if (!ui->display->text().contains(QLatin1Char('.')))
            ui->display->setText(ui->display->text() + QStringLiteral("."));
    }

    void onOperator(QChar op) {
        if (m_error) return;
        if (!m_startNew) compute();
        m_pendingValue = ui->display->text().toDouble();
        m_pendingOp = op;
        m_startNew = true;
    }

    void onEquals() {
        if (m_error) return;
        compute();
        m_pendingOp = QChar();
    }

    void onClear() {
        ui->display->setText(QStringLiteral("0"));
        m_pendingValue = 0.0;
        m_pendingOp = QChar();
        m_startNew = false;
        m_error = false;
    }

    void onBackspace() {
        if (m_error) { onClear(); return; }
        QString text = ui->display->text();
        text.chop(1);
        ui->display->setText(text.isEmpty() ? QStringLiteral("0") : text);
    }

    QString displayText() const { return ui->display->text(); }

private:
    void compute() {
        if (m_pendingOp.isNull()) return;
        const double rhs = ui->display->text().toDouble();
        double result = 0.0;
        if (m_pendingOp == QLatin1Char('+')) result = m_pendingValue + rhs;
        else if (m_pendingOp == QLatin1Char('-')) result = m_pendingValue - rhs;
        else if (m_pendingOp == QLatin1Char('*')) result = m_pendingValue * rhs;
        else if (m_pendingOp == QLatin1Char('/')) {
            if (rhs == 0.0) { showError(); return; }
            result = m_pendingValue / rhs;
        }
        ui->display->setText(QString::number(result, 'g', 12));
        m_pendingValue = result;
        m_startNew = true;
    }

    void showError() {
        ui->display->setText(QStringLiteral("Error"));
        m_error = true;
        m_startNew = true;
    }

    Ui::CalculatorForm* ui;
    double m_pendingValue = 0.0;
    QChar m_pendingOp;
    bool m_startNew = false;
    bool m_error = false;
};

// ------------------------------------------------------------ 自测入口 ----
// 与 ../calculator/qt_calculator.cpp 完全相同的 5 组用例：验证「界面搭建方式
// 换了，行为不变」。

static bool expectEqual(const char* label, const QString& actual, const QString& expected) {
    if (actual != expected) {
        qCritical() << "自测失败：" << label << "实际=" << actual << "预期=" << expected;
        return false;
    }
    qInfo() << "自测通过：" << label << "=>" << actual;
    return true;
}

static bool runSelfTest(CalculatorDesigner* calc) {
    calc->onClear();
    calc->onDigit(7); calc->onOperator(QLatin1Char('+')); calc->onDigit(3); calc->onEquals();
    if (!expectEqual("7 + 3 =", calc->displayText(), QStringLiteral("10"))) return false;

    calc->onClear();
    calc->onDigit(6); calc->onOperator(QLatin1Char('/')); calc->onDigit(0); calc->onEquals();
    if (!expectEqual("6 ÷ 0 =", calc->displayText(), QStringLiteral("Error"))) return false;

    calc->onClear();
    calc->onDigit(4); calc->onDot(); calc->onDigit(5);
    calc->onOperator(QLatin1Char('+'));
    calc->onDigit(0); calc->onDot(); calc->onDigit(5);
    calc->onEquals();
    if (!expectEqual("4.5 + 0.5 =", calc->displayText(), QStringLiteral("5"))) return false;

    calc->onClear();
    calc->onDigit(1); calc->onDigit(2); calc->onBackspace();
    if (!expectEqual("12 退格", calc->displayText(), QStringLiteral("1"))) return false;

    calc->onClear();
    calc->onDigit(2); calc->onOperator(QLatin1Char('+'));
    calc->onDigit(3); calc->onOperator(QLatin1Char('+'));
    calc->onDigit(4); calc->onEquals();
    if (!expectEqual("2 + 3 + 4 =（链式运算）", calc->displayText(), QStringLiteral("9"))) return false;

    return true;
}

int main(int argc, char* argv[]) {
    QApplication app(argc, argv);
    CalculatorDesigner calc;
    calc.show();

    if (qgetenv("QT_QPA_PLATFORM") == "offscreen") {
        QTimer::singleShot(0, &calc, [&calc] {
            const bool ok = runSelfTest(&calc);
            QCoreApplication::exit(ok ? 0 : 1);
        });
    }
    return app.exec();
}

#include "qt_calculator_designer.moc"
