/***************************************************************************
 *   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 "skgservices.h"
#include "skgtraces.h"

#include <kglobal.h>
#include <klocale.h>
#include <kcolorscheme.h>

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

SKGCalculatorEdit::SKGCalculatorEdit(QWidget* parent)
    : KLineEdit(parent), m_lastValue(0), m_lastOperator(0), m_valid(true)
{
    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* validator = new QDoubleValidator(this);
        setValidator(validator);
        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);
    m_valid = true;

    //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 (m_valid) KLineEdit::setText(iText);
}

bool SKGCalculatorEdit::valid() const
{
    return m_valid;
}

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* event)
{
    if (event) {
        int key = event->key();
        if (mode() == CALCULATOR) {
            bool hasText = !text().isEmpty() && selectedText() != text();

            if (event->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);
                event->accept();
            } else KLineEdit::keyPressEvent(event);
        } else {
            //Set default color
            QPalette field_palette = palette();
            field_palette.setColor(QPalette::Text, m_fontColor);
            setPalette(field_palette);

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

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

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));
                m_valid = true;
            } else {
                QPalette field_palette = palette();
                field_palette.setColor(QPalette::Text, KColorScheme(QPalette::Normal).foreground(KColorScheme::NegativeText).color());
                setPalette(field_palette);
                m_valid = false;
            }
            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* locale = KGlobal::locale();
        if (locale) {
            if (!locale->thousandsSeparator().isEmpty()) t = t.replace(locale->thousandsSeparator(), ".");
            if (!locale->monetaryThousandsSeparator().isEmpty()) t = t.replace(locale->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"

