/**
 * @file gui.cpp
 * @brief Implementation of the calculator GUI.
 */

#include "gui.h"
#include "ui_gui.h"

#include <QMessageBox>
#include <QKeyEvent>
#include <QString>
#include <cmath>

/**
 * @brief Constructs the GUI and initializes components.
 * @param parent Pointer to parent widget.
 */
Gui::Gui(QWidget *parent)
    : QMainWindow(parent),
      ui(new Ui::Gui),
      justEvaluated(false)
{
    ui->setupUi(this);
    currentExpression.clear();
}

/**
 * @brief Destructor. Frees allocated resources.
 */
Gui::~Gui()
{
    delete ui;
}

/**
 * @brief Displays an error message based on mathlib error code.
 * @param err Error type returned from mathlib.
 */
void Gui::showError(mathlib::MathError err)
{
    switch (err) {
        case mathlib::DIVISION_BY_ZERO:
            QMessageBox::warning(this, "Error", "Division by zero!");
            break;
        case mathlib::NEGATIVE_FACTORIAL:
            QMessageBox::warning(this, "Error", "Factorial of negative number!");
            break;
        case mathlib::INVALID_ROOT:
            QMessageBox::warning(this, "Error", "Invalid root operation!");
            break;
        case mathlib::INVALID_ARGUMENT:
            QMessageBox::warning(this, "Error", "Invalid argument!");
            break;
        default:
            break;
    }
}

/**
 * @brief Updates the calculator display.
 *
 * If the expression is empty, displays 0.
 */
void Gui::updateDisplay()
{
    ui->display->setText(currentExpression.isEmpty() ? "0" : currentExpression);
}

/**
 * @brief Checks if the last character in the expression is an operator.
 * @return true if expression ends with an operator.
 */
bool Gui::endsWithOperator() const
{
    if (currentExpression.isEmpty()) return false;
    QChar last = currentExpression.back();
    return last == '+' || last == '-' || last == '*' ||
           last == '/' || last == '%' || last == '^';
}

/**
 * @brief Checks if the expression already contains a binary operator.
 *
 * Skips a leading minus sign (negative number) and looks for any
 * binary operator in the remaining characters.
 * @return true if a binary operator is present.
 */
bool Gui::hasOperator() const
{
    // Start scanning from index 1 to allow a leading negative sign
    for (int i = 1; i < currentExpression.size(); ++i) {
        QChar c = currentExpression[i];
        if (c == '+' || c == '*' || c == '/' || c == '%' || c == '^')
            return true;
        // A '-' that isn't at position 0 counts as an operator
        if (c == '-')
            return true;
    }
    return false;
}

/**
 * @brief Appends a digit to the expression.
 *
 * If the calculator just finished an evaluation, the digit starts a
 * new expression instead of appending to the old result.
 */
void Gui::appendDigit(const QString &digit)
{
    if (justEvaluated) {
        currentExpression.clear();
        justEvaluated = false;
    }
    currentExpression += digit;
    updateDisplay();
}

/**
 * @brief Appends a binary operator to the expression.
 *
 * Prevents duplicate operators and enforces single-operation limit.
 * If the user just evaluated, the result is kept and the operator
 * is chained onto it.
 */
void Gui::appendOperator(const QString &op)
{
    // After evaluation, chain onto the result
    if (justEvaluated) {
        justEvaluated = false;
    }

    // Don't allow an operator on an empty expression
    // (except minus, which creates a negative number)
    if (currentExpression.isEmpty()) {
        if (op == "-") {
            currentExpression += op;
            updateDisplay();
        }
        return;
    }

    // If expression already ends with an operator, replace it
    if (endsWithOperator()) {
        currentExpression.chop(1);
        currentExpression += op;
        updateDisplay();
        return;
    }

    // Only allow one binary operator per expression
    if (hasOperator()) {
        return;
    }

    currentExpression += op;
    updateDisplay();
}

// ---- Digit button handlers ----

void Gui::on_btn_0_clicked() { appendDigit("0"); }
void Gui::on_btn_1_clicked() { appendDigit("1"); }
void Gui::on_btn_2_clicked() { appendDigit("2"); }
void Gui::on_btn_3_clicked() { appendDigit("3"); }
void Gui::on_btn_4_clicked() { appendDigit("4"); }
void Gui::on_btn_5_clicked() { appendDigit("5"); }
void Gui::on_btn_6_clicked() { appendDigit("6"); }
void Gui::on_btn_7_clicked() { appendDigit("7"); }
void Gui::on_btn_8_clicked() { appendDigit("8"); }
void Gui::on_btn_9_clicked() { appendDigit("9"); }

// ---- Operator button handlers ----

void Gui::on_btn_plus_clicked()  { appendOperator("+"); }
void Gui::on_btn_minus_clicked() { appendOperator("-"); }
void Gui::on_btn_mul_clicked()   { appendOperator("*"); }
void Gui::on_btn_div_clicked()   { appendOperator("/"); }
void Gui::on_btn_mod_clicked()   { appendOperator("%"); }
void Gui::on_btn_pow_clicked()   { appendOperator("^"); }

/**
 * @brief Appends a decimal point to the current expression.
 *
 * Only allows one decimal point per number segment. If the calculator
 * just evaluated, clears the expression first.
 */
void Gui::on_btn_dot_clicked()
{
    if (justEvaluated) {
        currentExpression.clear();
        justEvaluated = false;
    }

    // Find the current number segment (after the last operator)
    int lastOp = -1;
    for (int i = currentExpression.size() - 1; i >= 0; --i) {
        QChar c = currentExpression[i];
        if (c == '+' || c == '-' || c == '*' ||
            c == '/' || c == '%' || c == '^') {
            lastOp = i;
            break;
        }
    }
    QString segment = currentExpression.mid(lastOp + 1);

    // Only add a dot if this number segment doesn't already have one
    if (!segment.contains('.')) {
        currentExpression += ".";
        updateDisplay();
    }
}

/**
 * @brief Appends square root operator.
 *
 * Only allowed at the start of an expression (unary prefix operator).
 */
void Gui::on_btn_root_clicked()
{
    if (justEvaluated) {
        currentExpression.clear();
        justEvaluated = false;
    }

    // √ is a prefix unary op — only valid at the beginning
    if (currentExpression.isEmpty()) {
        currentExpression = "√";
        updateDisplay();
    }
}

/**
 * @brief Appends factorial operator.
 *
 * Only allowed at the end of a number (unary postfix operator).
 * Prevents factorial on an empty expression or after an operator.
 */
void Gui::on_btn_fac_clicked()
{
    if (justEvaluated) {
        justEvaluated = false;
    }

    if (currentExpression.isEmpty() || endsWithOperator())
        return;

    // Don't allow factorial if there's already an operator (keeps single-op rule)
    // but do allow it as the sole operation on a plain number
    if (hasOperator())
        return;

    // Don't allow double factorial
    if (currentExpression.endsWith("!"))
        return;

    currentExpression += "!";
    updateDisplay();
}

/**
 * @brief Clears the current expression and resets display.
 */
void Gui::on_btn_clear_clicked()
{
    currentExpression.clear();
    justEvaluated = false;
    updateDisplay();
}

/**
 * @brief Finds the position of the binary operator in the expression.
 *
 * Skips a leading minus sign so that negative first operands are
 * handled correctly (e.g. "-5+3" splits into -5 and 3).
 * @param op The operator character to find.
 * @return The index of the operator, or -1 if not found.
 */
int Gui::findOperatorPos(QChar op) const
{
    // Start at 1 to skip a possible leading negative sign
    for (int i = 1; i < currentExpression.size(); ++i) {
        if (currentExpression[i] == op)
            return i;
    }
    return -1;
}

/**
 * @brief Evaluates the current expression.
 *
 * Supports basic binary operations and simple unary operations.
 * Displays error messages if invalid operations occur.
 */
void Gui::on_btn_equal_clicked()
{
    if (currentExpression.isEmpty())
        return;


    if (justEvaluated)
        return;


    if (endsWithOperator())
        return;

    mathlib::MathError err = mathlib::OK;
    double result = 0.0;
    bool evaluated = false;




    if (currentExpression.endsWith("!")) {
        int val = currentExpression.chopped(1).toInt();
        result = mathlib::factorial(val, err);
        evaluated = true;
    }

    else if (currentExpression.startsWith("√")) {
        double val = currentExpression.mid(1).toDouble();
        result = mathlib::root(val, 2, err);
        evaluated = true;
    }



    if (!evaluated) {
        int pos = -1;
        QChar op = '\0';


        const QString ops = "+*/%^";
        for (QChar c : ops) {
            pos = findOperatorPos(c);
            if (pos != -1) { op = c; break; }
        }

        if (pos == -1) {
            pos = findOperatorPos('-');
            if (pos != -1) op = '-';
        }

        if (pos != -1) {
            double left  = currentExpression.left(pos).toDouble();
            double right = currentExpression.mid(pos + 1).toDouble();

            if (op == '+')      result = mathlib::add(left, right);
            else if (op == '-') result = mathlib::subtract(left, right);
            else if (op == '*') result = mathlib::multiply(left, right);
            else if (op == '/') result = mathlib::divide(left, right, err);
            else if (op == '%') result = mathlib::modulo(left, right, err);
            else if (op == '^') result = mathlib::power(left, static_cast<int>(right));

            evaluated = true;
        }
    }

    // Plain number — just keep it
    if (!evaluated) {
        result = currentExpression.toDouble();
    }

    if (err != mathlib::OK) {
        showError(err);
        currentExpression.clear();
        justEvaluated = false;
        updateDisplay();
        return;
    }

    currentExpression = QString::number(result);
    justEvaluated = true;
    updateDisplay();
}

/**
 * @brief Handles keyboard input for the calculator.
 *
 * Maps keyboard keys to their corresponding button actions.
 *
 * @param event The key press event.
 */
void Gui::keyPressEvent(QKeyEvent *event)
{
    int key = event->key();

    // Digit keys 0-9
    if (key >= Qt::Key_0 && key <= Qt::Key_9) {
        appendDigit(QString::number(key - Qt::Key_0));
        return;
    }

    switch (key) {
        // Basic operators
        case Qt::Key_Plus:
            appendOperator("+");
            break;
        case Qt::Key_Minus:
            appendOperator("-");
            break;
        case Qt::Key_Asterisk:
            appendOperator("*");
            break;
        case Qt::Key_Slash:
            appendOperator("/");
            break;
        case Qt::Key_Percent:
            appendOperator("%");
            break;
        case Qt::Key_AsciiCircum:
            appendOperator("^");
            break;
        case Qt::Key_Exclam:
            on_btn_fac_clicked();
            break;

        // Decimal point
        case Qt::Key_Period:
        case Qt::Key_Comma:
            on_btn_dot_clicked();
            break;

        // Evaluate
        case Qt::Key_Return:
        case Qt::Key_Enter:
        case Qt::Key_Equal:
            on_btn_equal_clicked();
            break;

        // Backspace — delete last character
        case Qt::Key_Backspace:
            if (justEvaluated) {
                currentExpression.clear();
                justEvaluated = false;
            } else if (!currentExpression.isEmpty()) {
                currentExpression.chop(1);
            }
            updateDisplay();
            break;

        // Clear
        case Qt::Key_Escape:
        case Qt::Key_Delete:
        case Qt::Key_C:
            on_btn_clear_clicked();
            break;

        default:
            QMainWindow::keyPressEvent(event);
            break;
    }
}