// This file is part of the KDE project
// SPDX-FileCopyrightText: 2007 Ariya Hidayat <ariya@kde.org>
// SPDX-FileCopyrightText: 2006 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
// SPDX-FileCopyrightText: 2005 Tomas Mecir <mecirt@gmail.com>
// SPDX-License-Identifier: LGPL-2.0-only

#include "TestOpenFormula.h"

#include <QTest>

#include <core/Cell.h>
#include <core/odf/SheetsOdf.h>
#include <engine/Formula.h>
#include <engine/FunctionModuleRegistry.h>
#include <engine/Value.h>

using namespace Calligra::Sheets;

// because we may need to promote expected value from integer to float
#define CHECK_EVAL(x, y)                                                                                                                                       \
    {                                                                                                                                                          \
        Value z(y);                                                                                                                                            \
        QCOMPARE(evaluate(x, z), (z));                                                                                                                         \
    }

Value TestOpenFormula::evaluate(const QString &formula, Value &ex)
{
    Formula f;
    QString expr = formula;
    if (expr[0] != '=')
        expr.prepend('=');
    f.setExpression(expr);
    Value result = f.eval();

    if (result.isFloat() && ex.isInteger())
        ex = Value(ex.asFloat());
    if (result.isInteger() && ex.isFloat())
        result = Value(result.asFloat());

    return result;
}

namespace QTest
{
template<>
char *toString(const Value &value)
{
    QString message;
    QTextStream ts(&message, QIODevice::WriteOnly);
    ts << value;
    return qstrdup(message.toLatin1());
}
}

/*
#define CHECK_CONVERT(x,y) \
    { QCOMPARE(convertToOpenFormula(x),QString(y)); \
        QCOMPARE(convertFromOpenFormula(y),QString(x)); }
*/

QString TestOpenFormula::convertToOpenFormula(const QString &expr)
{
    // TODO - formula encoding is done differently (needs a valid cell), use that!
    QString formula = QString(); /* Cell().encodeFormula(expr); */
    return formula;
}

QString TestOpenFormula::convertFromOpenFormula(const QString &expr)
{
    QString formula = Cell().decodeFormula(expr);
    return formula;
}

void TestOpenFormula::initTestCase()
{
    KLocalizedString::setApplicationDomain("calligrasheets");
    FunctionModuleRegistry::instance()->loadFunctionModules();
}

void TestOpenFormula::testEvaluation()
{
    // tests from the OpenFormula testing suite:
    // note that these get auto-generated using generate-openformula-tests
    CHECK_EVAL("=(1/3)*3=1", Value(true)); // row 51
    CHECK_EVAL("=(\"4\" & \"5\")+2", Value(47)); // row 57
    CHECK_EVAL("=2+(\"4\" & \"5\")", Value(47)); // row 58
    CHECK_EVAL("=1+2", Value(3)); // row 63
    CHECK_EVAL("=3-1", Value(2)); // row 65
    CHECK_EVAL("=5--2", Value(7)); // row 67
    CHECK_EVAL("=3*4", Value(12)); // row 68
    CHECK_EVAL("=2+3*4", Value(14)); // row 70
    CHECK_EVAL("=6/3", Value(2)); // row 71
    CHECK_EVAL("=5/2", Value(2.5)); // row 72
    CHECK_EVAL("=ISERROR(1/0)", Value(true)); // row 73
    CHECK_EVAL("=2^3", Value(8)); // row 74
    CHECK_EVAL("=9^0.5", Value(3)); // row 75
    CHECK_EVAL("=(-5)^3", Value(-125)); // row 76
    CHECK_EVAL("=4^-1", Value(0.25)); // row 77
    CHECK_EVAL("=5^0", Value(1)); // row 78
    CHECK_EVAL("=0^5", Value(0)); // row 79
    CHECK_EVAL("=2+3*4^2", Value(50)); // row 80
    CHECK_EVAL("=-2^2", Value(4)); // row 81
    CHECK_EVAL("=1=1", Value(true)); // row 82
    CHECK_EVAL("=1=0", Value(false)); // row 84
    CHECK_EVAL("=3=3.0001", Value(false)); // row 85
    // Not passed for line 86.
    CHECK_EVAL("=\"Hi\"=\"Bye\"", Value(false)); // row 87
    CHECK_EVAL("=FALSE()=FALSE()", Value(true)); // row 88
    CHECK_EVAL("=TRUE()=FALSE()", Value(false)); // row 89
    CHECK_EVAL("=\"5\"=5", Value(false)); // row 90
    CHECK_EVAL("=TRUE()=1", Value(false)); // row 91
    // Not passed for line 92.
    // Not passed for line 93.
    CHECK_EVAL("=1<>1", Value(false)); // row 94
    CHECK_EVAL("=1<>2", Value(true)); // row 95
    CHECK_EVAL("=1<>\"1\"", Value(true)); // row 96
    // Not passed for line 97.
    CHECK_EVAL("=5<6", Value(true)); // row 98
    CHECK_EVAL("=5<=6", Value(true)); // row 99
    CHECK_EVAL("=5>6", Value(false)); // row 100
    CHECK_EVAL("=5>=6", Value(false)); // row 101
    CHECK_EVAL("=\"A\"<\"B\"", Value(true)); // row 102
    // Not passed for line 103.
    CHECK_EVAL("=\"AA\">\"A\"", Value(true)); // row 104
    CHECK_EVAL("=\"Hi \" & \"there\"", Value("Hi there")); // row 107
    CHECK_EVAL("=\"H\" & \"\"", Value("H")); // row 108
    // Not passed for line 109.
    CHECK_EVAL("=50%", Value(0.5)); // row 111
    CHECK_EVAL("=20+50%", Value(20.5)); // row 112
    CHECK_EVAL("=+5", Value(5)); // row 113
    CHECK_EVAL("=+\"Hello\"", Value("Hello")); // row 114
    CHECK_EVAL("=-\"7\"", Value(-7)); // row 116
    /*
     These are currently disabled, due to being locale specific.
     CHECK_EVAL("=DATE(2005;1;3)=DATEVALUE(\"2005-01-03\")", Value(true));  // row 118
      CHECK_EVAL("=DATE(2017.5; 1; 2)=DATEVALUE(\"2017-01-02\")", Value(true));  // row 119
      CHECK_EVAL("=DATE(2006; 2.5; 3)=DATEVALUE(\"2006-02-03\")", Value(true));  // row 120
      CHECK_EVAL("=DATE(2006; 1; 3.5)=DATEVALUE(\"2006-01-03\")", Value(true));  // row 121
      CHECK_EVAL("=DATE(2006; 13; 3)=DATEVALUE(\"2007-01-03\")", Value(true));  // row 122
      CHECK_EVAL("=DATE(2006; 1; 32)=DATEVALUE(\"2006-02-01\")", Value(true));  // row 123
      CHECK_EVAL("=DATE(2006; 25; 34)=DATEVALUE(\"2008-02-03\")", Value(true));  // row 124
      CHECK_EVAL("=DATE(2006;-1; 1)=DATEVALUE(\"2005-11-01\")", Value(true));  // row 125
    // Not passed for line 126.
    // Not passed for line 127.
      CHECK_EVAL("=DATE(2004;2;29)=DATEVALUE(\"2004-02-29\")", Value(true));  // row 128
      CHECK_EVAL("=DATE(2003;2;29)=DATEVALUE(\"2003-03-01\")", Value(true));  // row 129
      CHECK_EVAL("=DATE(1904; 1; 1)=DATEVALUE(\"1904-01-01\")", Value(true));  // row 130
      CHECK_EVAL("=DATEVALUE(\"2004-12-25\")=DATE(2004;12;25)", Value(true));  // row 131
      CHECK_EVAL("=DAY(\"2006-05-21\")", Value(21));  // row 132
      CHECK_EVAL("=DAY(\"5/21/2006\")", Value(21));  // row 133
      CHECK_EVAL("=DAY(\"05-21-2006\")", Value(21));  // row 134
      CHECK_EVAL("=DAY(\"5/21/06\")", Value(21));  // row 135
      CHECK_EVAL("=DAY(\"5-21-06\")", Value(21));  // row 136
    */
}

/*
void TestOpenFormula::testFormulaConversion()
{
    // cell references
    CHECK_CONVERT("=A1", "=[.A1]");
    CHECK_CONVERT("=A1:A4", "=[.A1:.A4]");
    CHECK_CONVERT("=A$1:$A4", "=[.A$1:.$A4]");
    CHECK_CONVERT("=Sheet2!A1", "=[Sheet2.A1]");
    CHECK_CONVERT("='Sheet 2'!A1", "=['Sheet 2'.A1]");
    CHECK_CONVERT("=Sheet2!A1:B4", "=[Sheet2.A1:Sheet2.B4]");
    CHECK_CONVERT("='Sheet 2'!A1:B4", "=['Sheet 2'.A1:'Sheet 2'.B4]");

    // equality
    CHECK_CONVERT("=A1==A2", "=[.A1]=[.A2]");

    // strings
    CHECK_CONVERT("=\"2,2\"+2,1+\"2,0\"", "=\"2,2\"+2.1+\"2,0\"");

    // decimal separator ','
    CHECK_CONVERT("=,12", "=.12");
    CHECK_CONVERT("=12,12", "=12.12");
    CHECK_CONVERT("=368*7*(0,1738+0,1784)*(0,1738+0,1784)", "=368*7*(0.1738+0.1784)*(0.1738+0.1784)");

    // function names
    CHECK_CONVERT("=sum(A1;A2;A3;A4;A5)", "=sum([.A1];[.A2];[.A3];[.A4];[.A5])");
}
*/

void TestOpenFormula::testReferenceLoading()
{
    QCOMPARE(Odf::loadRegion(".A1"), QString("A1"));
    QCOMPARE(Odf::loadRegion(".A1:.A4"), QString("A1:A4"));
    QCOMPARE(Odf::loadRegion(".A$1:.$A4"), QString("A$1:$A4"));
    QCOMPARE(Odf::loadRegion("Sheet2.A1"), QString("Sheet2!A1"));
    QCOMPARE(Odf::loadRegion("'Sheet 2'.A1"), QString("'Sheet 2'!A1"));
    QCOMPARE(Odf::loadRegion("Sheet2.A1:Sheet2.B4"), QString("Sheet2!A1:B4"));
    QCOMPARE(Odf::loadRegion("'Sheet 2'.A1:'Sheet 2'.B4"), QString("'Sheet 2'!A1:B4"));
    QCOMPARE(Odf::loadRegion("$Sheet2.A1:$Sheet2.B4"), QString("Sheet2!A1:B4"));
    QCOMPARE(Odf::loadRegion("$'Sheet 2'.A1:$'Sheet 2'.B4"), QString("'Sheet 2'!A1:B4"));
}

void TestOpenFormula::testReferenceSaving()
{
    QCOMPARE(Odf::saveRegion("A1"), QString(".A1"));
    QCOMPARE(Odf::saveRegion("A1:A4"), QString(".A1:.A4"));
    QCOMPARE(Odf::saveRegion("A$1:$A4"), QString(".A$1:.$A4"));
    QCOMPARE(Odf::saveRegion("Sheet2!A1"), QString("Sheet2.A1"));
    QCOMPARE(Odf::saveRegion("'Sheet 2'!A1"), QString("'Sheet 2'.A1"));
    QCOMPARE(Odf::saveRegion("Sheet2!A1:B4"), QString("Sheet2.A1:Sheet2.B4"));
    QCOMPARE(Odf::saveRegion("'Sheet 2'!A1:B4"), QString("'Sheet 2'.A1:'Sheet 2'.B4"));
}

QTEST_MAIN(TestOpenFormula)
