#include "expressionevaluator.h"

namespace {

// 递归下降解析器：expression := term (('+'|'-') term)*
//                term       := factor (('*'|'/') factor)*
//                factor     := ('+'|'-') factor | '(' expression ')' | number
// 括号会先被完整求值，天然实现"优先级提升"；乘除法在 term 层处理，
// 天然高于 expression 层的加减法。
// 【Day3 calculator_mainwindow】与 ../calculator_designer_ext/ 版一字不差。
class Parser {
public:
    explicit Parser(const QString& text) : m_text(text) {}

    ExpressionEvaluator::Result parse() {
        ExpressionEvaluator::Result result;
        skipSpaces();
        if (atEnd()) {
            result.errorMessage = QStringLiteral("表达式为空");
            return result;
        }

        bool ok = true;
        const double value = parseExpression(ok);
        skipSpaces();
        if (!ok) {
            result.errorMessage = m_error;
            return result;
        }
        if (!atEnd()) {
            result.errorMessage = QStringLiteral("表达式包含无法识别的字符");
            return result;
        }

        result.ok = true;
        result.value = value;
        return result;
    }

private:
    double parseExpression(bool& ok) {
        double value = parseTerm(ok);
        while (ok) {
            skipSpaces();
            if (peek() == QLatin1Char('+')) {
                advance();
                value += parseTerm(ok);
            } else if (peek() == QLatin1Char('-')) {
                advance();
                value -= parseTerm(ok);
            } else {
                break;
            }
        }
        return value;
    }

    double parseTerm(bool& ok) {
        double value = parseFactor(ok);
        while (ok) {
            skipSpaces();
            if (peek() == QLatin1Char('*')) {
                advance();
                value *= parseFactor(ok);
            } else if (peek() == QLatin1Char('/')) {
                advance();
                const double rhs = parseFactor(ok);
                if (!ok) return 0.0;
                if (rhs == 0.0) {
                    fail(QStringLiteral("除数不能为零"), ok);
                    return 0.0;
                }
                value /= rhs;
            } else {
                break;
            }
        }
        return value;
    }

    double parseFactor(bool& ok) {
        skipSpaces();
        if (peek() == QLatin1Char('+')) {
            advance();
            return parseFactor(ok);
        }
        if (peek() == QLatin1Char('-')) {
            advance();
            return -parseFactor(ok);
        }
        if (peek() == QLatin1Char('(')) {
            advance();
            const double value = parseExpression(ok);
            if (!ok) return 0.0;
            skipSpaces();
            if (peek() != QLatin1Char(')')) {
                fail(QStringLiteral("括号不匹配"), ok);
                return 0.0;
            }
            advance();
            return value;
        }
        return parseNumber(ok);
    }

    double parseNumber(bool& ok) {
        skipSpaces();
        const int start = m_pos;
        bool hasDigits = false;
        while (!atEnd() && peek().isDigit()) { advance(); hasDigits = true; }
        if (!atEnd() && peek() == QLatin1Char('.')) {
            advance();
            while (!atEnd() && peek().isDigit()) { advance(); hasDigits = true; }
        }
        if (!hasDigits) {
            fail(QStringLiteral("表达式格式错误，缺少数字"), ok);
            return 0.0;
        }
        const QString token = m_text.mid(start, m_pos - start);
        bool numOk = false;
        const double value = token.toDouble(&numOk);
        if (!numOk) {
            fail(QStringLiteral("数字格式错误"), ok);
            return 0.0;
        }
        return value;
    }

    void skipSpaces() { while (!atEnd() && peek().isSpace()) advance(); }
    bool atEnd() const { return m_pos >= m_text.size(); }
    QChar peek() const { return atEnd() ? QChar() : m_text.at(m_pos); }
    void advance() { ++m_pos; }
    void fail(const QString& message, bool& ok) { ok = false; m_error = message; }

    const QString& m_text;
    int m_pos = 0;
    QString m_error;
};

}  // namespace

ExpressionEvaluator::Result ExpressionEvaluator::evaluate(const QString& expression) {
    Parser parser(expression);
    return parser.parse();
}
