#include <QApplication>
#include <QWidget>
#include <QFontComboBox>
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QCheckBox>
#include <QComboBox>
#include <QLabel>
#include <QLineEdit>
#include <QGroupBox>
#include <QGridLayout>
#include <QVBoxLayout>
#include <QFormLayout>
#include <QPainter>
#include <QPaintEvent>
#include <QFontMetrics>
#include <QFontDatabase>
#include <QFrame>
#include <QStyle>

class Preview : public QWidget {
    Q_OBJECT
public:
    explicit Preview(QWidget* parent=nullptr) : QWidget(parent) {
        setMinimumHeight(180);
        setAutoFillBackground(true);
        QPalette pal = palette();
        pal.setColor(QPalette::Window, QColor(30,30,30));
        pal.setColor(QPalette::WindowText, Qt::white);
        setPalette(pal);
    }

    void setSample(const QString& t) { m_text = t; update(); }
    void setFontForPreview(const QFont& f) { m_font = f; update(); }

protected:
    void paintEvent(QPaintEvent*) override {
        QPainter p(this);
        p.setRenderHint(QPainter::Antialiasing, true);
        p.fillRect(rect(), palette().window());

        if (m_text.isEmpty()) return;

        // Draw guide area
        const int margin = 20;
        QRect area = rect().adjusted(margin, margin, -margin, -margin);

        // Metrics
        QFont f = m_font;
        p.setFont(f);
        QFontMetrics fm(f);

        int baselineY = area.center().y();
        int ascent = fm.ascent();
        int descent = fm.descent();
        int leading = fm.leading();

        // Baseline
        p.setPen(QPen(Qt::red, 1, Qt::DashLine));
        p.drawLine(area.left(), baselineY, area.right(), baselineY);

        // Ascent line
        p.setPen(QPen(QColor(0,200,0), 1, Qt::DashLine));
        p.drawLine(area.left(), baselineY - ascent, area.right(), baselineY - ascent);

        // Descent line
        p.setPen(QPen(QColor(200,0,0), 1, Qt::DashLine));
        p.drawLine(area.left(), baselineY + descent, area.right(), baselineY + descent);

        // Leading area (below baseline + descent)
        p.setPen(QPen(QColor(120,120,120), 1, Qt::DotLine));
        p.drawLine(area.left(), baselineY + descent + leading, area.right(), baselineY + descent + leading);

        // Bounding rects
        QRect br = fm.boundingRect(m_text);
        QRect tbr = fm.tightBoundingRect(m_text);

        // Position text so its baseline at baselineY, start at left margin
        QPoint textPos(area.left(), baselineY);

        // Draw text bounding at its target position
        QRect brAtPos = QRect(textPos + br.topLeft(), br.size());
        QRect tbrAtPos = QRect(textPos + tbr.topLeft(), tbr.size());

        // Tight bounding rect
        p.setPen(QPen(QColor(255,0,255), 1));
        p.drawRect(tbrAtPos);

        // Normal bounding rect
        p.setPen(QPen(QColor(0,255,255), 1));
        p.drawRect(brAtPos);

        // Draw text
        p.setPen(palette().windowText().color());
        p.drawText(textPos, m_text);

        // Legend
        p.setPen(Qt::white);
        QString legend = QString("ascent=%1, descent=%2, leading=%3, height=%4, lineSpacing=%5")
            .arg(ascent).arg(descent).arg(leading).arg(fm.height()).arg(fm.lineSpacing());
        p.drawText(area.left(), area.bottom(), legend);
    }

private:
    QString m_text = QStringLiteral("Abcgjpq汉字测 12345");
    QFont m_font;
};

int main(int argc, char** argv) {
    QApplication app(argc, argv);
    QWidget win;
    win.setWindowTitle("Qt Font Metrics Viewer");

    // Controls
    auto family = new QFontComboBox;
    family->setEditable(false);

    auto usePixel = new QCheckBox("use pixel size");
    auto sizePx = new QSpinBox; sizePx->setRange(4, 200); sizePx->setValue(18);
    auto sizePt = new QDoubleSpinBox; sizePt->setDecimals(1); sizePt->setRange(1.0, 200.0); sizePt->setValue(12.0);

    auto weight = new QComboBox;
    weight->addItem("Thin (100)", QFont::Thin);
    weight->addItem("Light (300)", QFont::Light);
    weight->addItem("Normal (400)", QFont::Normal);
    weight->addItem("Medium (500)", QFont::Medium);
    weight->addItem("DemiBold (600)", QFont::DemiBold);
    weight->addItem("Bold (700)", QFont::Bold);
    weight->addItem("Black (900)", QFont::Black);
    weight->setCurrentIndex(3); // Medium

    auto italic = new QCheckBox("Italic");

    auto sample = new QLineEdit("Abcgjpq汉字测 12345");

    auto ascentL = new QLabel, descentL = new QLabel, leadingL = new QLabel, heightL = new QLabel, lineSpacingL = new QLabel;
    auto avgW = new QLabel, maxW = new QLabel, xHeight = new QLabel, capHeight = new QLabel;

    auto metricsBox = new QGroupBox("Metrics");
    auto form = new QFormLayout(metricsBox);
    form->addRow("ascent", ascentL);
    form->addRow("descent", descentL);
    form->addRow("leading", leadingL);
    form->addRow("height", heightL);
    form->addRow("lineSpacing", lineSpacingL);
    form->addRow("averageCharWidth", avgW);
    form->addRow("maxWidth", maxW);
    form->addRow("xHeight (approx)", xHeight);
    form->addRow("capHeight (approx)", capHeight);

    auto preview = new Preview;

    auto controls = new QGridLayout;
    controls->addWidget(new QLabel("Family"), 0,0); controls->addWidget(family, 0,1,1,3);
    controls->addWidget(new QLabel("Size"), 1,0);
    controls->addWidget(sizePx, 1,1);
    controls->addWidget(usePixel, 1,2);
    controls->addWidget(sizePt, 1,3);
    controls->addWidget(new QLabel("Weight"), 2,0); controls->addWidget(weight, 2,1);
    controls->addWidget(italic, 2,2);
    controls->addWidget(new QLabel("Sample"), 3,0); controls->addWidget(sample, 3,1,1,3);

    auto mainLayout = new QVBoxLayout(&win);
    mainLayout->addLayout(controls);
    mainLayout->addWidget(metricsBox);
    mainLayout->addWidget(preview, 1);

    auto updateFont = [&](){
        QFont f(family->currentFont());
        f.setItalic(italic->isChecked());
        f.setWeight(static_cast<QFont::Weight>(weight->currentData().toInt()));
        if (usePixel->isChecked()) {
            f.setPixelSize(sizePx->value());
        } else {
            f.setPointSizeF(sizePt->value());
        }
        preview->setFontForPreview(f);

        QFontMetrics fm(f);
        ascentL->setNum(fm.ascent());
        descentL->setNum(fm.descent());
        leadingL->setNum(fm.leading());
        heightL->setNum(fm.height());
        lineSpacingL->setNum(fm.lineSpacing());
        avgW->setNum(fm.averageCharWidth());
        maxW->setNum(fm.maxWidth());
        // Approximations:
        xHeight->setNum(QFontMetricsF(f).xHeight());
        capHeight->setNum(QFontMetricsF(f).capHeight());
    };

    QObject::connect(family, &QFontComboBox::currentFontChanged, &win, [&](const QFont&){ updateFont(); });
    QObject::connect(usePixel, &QCheckBox::toggled, &win, [&](bool){ updateFont(); });
    QObject::connect(sizePx, QOverload<int>::of(&QSpinBox::valueChanged), &win, [&](int){ if (usePixel->isChecked()) updateFont(); });
    QObject::connect(sizePt, QOverload<double>::of(&QDoubleSpinBox::valueChanged), &win, [&](double){ if (!usePixel->isChecked()) updateFont(); });
    QObject::connect(weight, QOverload<int>::of(&QComboBox::currentIndexChanged), &win, [&](int){ updateFont(); });
    QObject::connect(italic, &QCheckBox::toggled, &win, [&](bool){ updateFont(); });
    QObject::connect(sample, &QLineEdit::textChanged, preview, &Preview::setSample);

    updateFont();
    win.resize(780, 520);
    win.show();
    return app.exec();
}

#include "main.moc"
