/***************************************************************************
 *   Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr  *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>  *
 ***************************************************************************/
/** @file
 * A KLineEdit with calculator included.
 *
 * @author Stephane MANKOWSKI / Guillaume DE BURE
 */
#include "skgcalculatoredit.h"

#include <KGlobal>
#include <KLocale>
#include <KColorScheme>

#include <QDoubleValidator>
#include <QKeyEvent>
#include <QScriptEngine>

#include "skgservices.h"
#include "skgtraces.h"

SKGCalculatorEdit::SKGCalculatorEdit(QWidget* iParent)
    : KLineEdit(iParent), m_lastValue(0), m_lastOperator(0), m_currentMode(CALCULATOR)
{
    setMode(CALCULATOR);
    m_fontColor = palette().color(QPalette::Text);
}

SKGCalculatorEdit::~SKGCalculatorEdit()
{
}

double SKGCalculatorEdit::value()
{
    bool test;
    return getEvaluatedValue(test);
}

int SKGCalculatorEdit::sign() const
{
    QString t = text();
    if (t.count() && t[0] == '+') {
        return 1;
    } else if (t.count() && t[0] == '-') {
        return -1;
    }
    return 0;
}

SKGCalculatorEdit::Mode SKGCalculatorEdit::mode() const
{
    return m_currentMode;
}

void SKGCalculatorEdit::setMode(Mode iMode)
{
    m_currentMode = iMode;
    if (iMode == CALCULATOR) {
        QDoubleValidator* newValidator = new QDoubleValidator(this);
        setValidator(newValidator);
        setAlignment(Qt::AlignRight);
    } else {
        setValidator(NULL);
    }
}

void SKGCalculatorEdit::setValue(double iValue)
{
    setText(SKGServices::doubleToString(iValue));
}

void SKGCalculatorEdit::setText(const QString& iText)
{
    // Set default color
    QPalette field_palette = palette();
    field_palette.setColor(QPalette::Text, m_fontColor);
    setPalette(field_palette);

    // Set text (to be sure than keyPressEvent is able to get it)
    KLineEdit::setText(iText);

    // Simulate a validation
    if (mode() == EXPRESSION) {
        bool previous = this->blockSignals(true);
        keyPressEvent(Qt::Key_Return);
        this->blockSignals(previous);
    }

    // Set text (to display the input value)
    if (valid()) {
        KLineEdit::setText(iText);
    }
}

bool SKGCalculatorEdit::valid()
{
    bool test;
    getEvaluatedValue(test);
    return test;
}

QString SKGCalculatorEdit::formula()
{
    return m_formula;
}

void SKGCalculatorEdit::addParameterValue(const QString& iParameter, double iValue)
{
    m_parameters.insert(iParameter, iValue);

    // Refresh completion
    KCompletion* comp = this->completionObject();
    if (comp) {
        comp->addItem('=' % iParameter);
    }
}

void SKGCalculatorEdit::keyPressEvent(QKeyEvent* iEvent)
{
    if (iEvent) {
        int key = iEvent->key();
        if (mode() == CALCULATOR) {
            bool hasText = !text().isEmpty() && selectedText() != text();

            if (iEvent->count() == 1 && ((key == Qt::Key_Plus && hasText) || (key == Qt::Key_Minus && hasText) || key == Qt::Key_Asterisk || key == Qt::Key_Slash || key == Qt::Key_Return || key == Qt::Key_Enter)) {
                keyPressEvent(key);
                iEvent->accept();
            } else {
                KLineEdit::keyPressEvent(iEvent);
            }
        } else {
            // Set default color
            QPalette field_palette = palette();
            field_palette.setColor(QPalette::Text, m_fontColor);
            setPalette(field_palette);

            keyPressEvent(key);
            KLineEdit::keyPressEvent(iEvent);
        }
    }
}

void SKGCalculatorEdit::focusOutEvent(QFocusEvent* iEvent)
{
    keyPressEvent(Qt::Key_Return);
    KLineEdit::focusOutEvent(iEvent);
}

void SKGCalculatorEdit::keyPressEvent(int key)
{
    if (mode() == CALCULATOR) {
        if (m_lastOperator != 0) {
            if (m_lastOperator == Qt::Key_Plus) {
                m_lastValue += value();
                setValue(m_lastValue);
            } else if (m_lastOperator == Qt::Key_Minus) {
                m_lastValue -= value();
                setValue(m_lastValue);
            } else if (m_lastOperator == Qt::Key_Asterisk) {
                m_lastValue *= value();
                setValue(m_lastValue);
            } else if (m_lastOperator == Qt::Key_Slash && value() != 0) {
                m_lastValue /= value();
                setValue(m_lastValue);
            }

        } else {
            m_lastValue = value();
        }

        if (key == Qt::Key_Return || key == Qt::Key_Enter) {
            m_lastOperator = 0;
            m_lastValue = 0;
        } else {
            m_lastOperator = key;
            KLineEdit::setText("");
        }
    } else {
        if (key == Qt::Key_Return || key == Qt::Key_Enter) {
            bool test;
            double v = getEvaluatedValue(test);
            if (test) {
                QString t = text();
                KLineEdit::setText((!t.isEmpty() && t[0] == '+' && v > 0 ? "+" : "") % SKGServices::doubleToString(v));
            } else {
                QPalette field_palette = palette();
                field_palette.setColor(QPalette::Text, KColorScheme(QPalette::Normal).foreground(KColorScheme::NegativeText).color());
                setPalette(field_palette);
            }
            emit textChanged(text());
        }
    }
}

double SKGCalculatorEdit::getEvaluatedValue(bool& iOk)
{
    qsreal output = 0;
    iOk = false;

    QString t = text().trimmed();
    if (!t.isEmpty()) {
        m_formula = t;
        t = t.replace(',', '.');  // Replace comma by a point in case of typo
        KLocale* loc = KGlobal::locale();
        if (loc) {
            if (!loc->thousandsSeparator().isEmpty()) {
                t = t.replace(loc->thousandsSeparator(), ".");
            }
            if (!loc->monetaryThousandsSeparator().isEmpty()) {
                t = t.replace(loc->monetaryThousandsSeparator(), ".");
            }
        }

        // Remove double . in numbers
        int toRemoveIndex = -1;
        int nbc = t.count();
        for (int i = 0; i < nbc; ++i) {
            if (t[i] == '.') {
                if (toRemoveIndex != -1) {
                    t = t.remove(toRemoveIndex, 1);
                    --nbc;
                    --i;
                    toRemoveIndex = i;
                } else {
                    toRemoveIndex = i;
                }
            } else if (t[i] < '0' || t[i] > '9') {
                toRemoveIndex = -1;
            }
        }
        if (t.startsWith(QLatin1String("="))) {
            t = t.right(t.length() - 1);
            QMapIterator<QString, double> i(m_parameters);
            while (i.hasNext()) {
                i.next();
                t.replace(i.key(), SKGServices::doubleToString(i.value()));
            }

        } else {
            m_formula = "";
        }

        QScriptEngine myEngine;
        QScriptValue result = myEngine.evaluate(t);
        if (result.isNumber()) {
            output = result.toNumber();
            iOk = true;
        }
    }
    return output;
}

#include "skgcalculatoredit.moc"

