/*
    SPDX-FileCopyrightText: 2013 Alex Richardson <alex.richardson@gmx.de>

    SPDX-License-Identifier: LGPL-2.1-or-later
*/

// test
#include "testutils.hpp"
// sut
#include <scriptengineinitializer.hpp>
#include <scriptvalueconverter.hpp>
#include <topleveldatainformation.hpp>
// Okteta core
#include <Okteta/ByteArrayModel>
// Qt
#include <QTest>
#include <QScriptEngine>
// Std
#include <array>
#include <utility>

class CustomToStringTest : public QObject
{
    Q_OBJECT

private Q_SLOTS:
    void initTestCase();
    void testUuid_data();
    void testUuid();
};

static constexpr std::array<uchar, 16> uuid1 =
{
    0x55, 0x0e, 0x84, 0x00, 0xe2, 0x9b, 0x41, 0xd4, 0xa7, 0x16, 0x44, 0x66, 0x55, 0x44, 0x00, 0x00
};

static constexpr std::array<uchar, 16> uuid2 =
{
    0x3f, 0x25, 0x04, 0xe0, 0x4f, 0x89, 0x11, 0xd3, 0x9a, 0x0c, 0x03, 0x05, 0xe8, 0x2c, 0x33, 0x01
};

static constexpr std::array<uchar, 16>  nullUuid =
{
    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
};

void CustomToStringTest::initTestCase()
{
    // needed so that imports can be resolved
    QString examples = QFINDTESTDATA("../examples");
    QVERIFY2(!examples.isEmpty(), "Test data must exist!");
    qputenv("XDG_DATA_DIRS", QFile::encodeName(QFileInfo(examples).absoluteFilePath()));
}

void CustomToStringTest::testUuid_data()
{
    QTest::addColumn<bool>("isGUID");
    QTest::addColumn<QString>("uuidString");
    QTest::addColumn<QByteArray>("data");
    QTest::newRow("uuid1") << false << "{550e8400-e29b-41d4-a716-446655440000}"
                           << QByteArray::fromRawData(reinterpret_cast<const char*>(uuid1.data()), uuid1.size());
    QTest::newRow("uuid2") << false << "{3f2504e0-4f89-11d3-9a0c-0305e82c3301}"
                           << QByteArray::fromRawData(reinterpret_cast<const char*>(uuid2.data()), uuid2.size());
    QTest::newRow("null uuid") << false << "{00000000-0000-0000-0000-000000000000}"
                               << QByteArray::fromRawData(reinterpret_cast<const char*>(nullUuid.data()), nullUuid.size());

    // now the same just as a Microsoft GUID
    QTest::newRow("guid1") << true << "{00840e55-9be2-d441-a716-446655440000}"
                           << QByteArray::fromRawData(reinterpret_cast<const char*>(uuid1.data()), sizeof(uuid1));
    QTest::newRow("guid2") << true << "{e004253f-894f-d311-9a0c-0305e82c3301}"
                           << QByteArray::fromRawData(reinterpret_cast<const char*>(uuid2.data()), uuid2.size());
    QTest::newRow("null guid") << true << "{00000000-0000-0000-0000-000000000000}"
                               << QByteArray::fromRawData(reinterpret_cast<const char*>(nullUuid.data()), nullUuid.size());
}

void CustomToStringTest::testUuid()
{
    QFETCH(QByteArray, data);
    QFETCH(QString, uuidString);
    QFETCH(bool, isGUID);
    std::unique_ptr<QScriptEngine> eng = ScriptEngineInitializer::newEngine();
    auto logger = std::make_unique<ScriptLogger>();
    logger->setLogToStdOut(true);
    std::unique_ptr<DataInformation> managedStructure;
    if (isGUID) {
        managedStructure = Utils::evalAndParse(eng.get(), "var u = importScript('uuid.js'); u.GUID();", logger.get());
    } else {
        managedStructure = Utils::evalAndParse(eng.get(), "var u = importScript('uuid.js'); u.UUID();", logger.get());
    }
#if defined(Q_OS_MACOS) || defined(Q_OS_WINDOWS)
  QEXPECT_FAIL("", "QStandardPaths::GenericDataLocation can't be modified on macOS/Windows", Abort);
#endif
    QVERIFY(managedStructure);
    DataInformation* const structure = managedStructure.get();
    TopLevelDataInformation top(std::move(managedStructure), std::move(logger), std::move(eng));
    QCOMPARE(structure->childCount(), 4U);

    QVERIFY(structure->toStringFunction().isFunction());
    Okteta::ByteArrayModel model(data);
    top.read(&model, 0, Okteta::ArrayChangeMetricsList(), true);

    QCOMPARE(structure->childAt(0)->effectiveByteOrder(),
             isGUID ? QSysInfo::LittleEndian : QSysInfo::BigEndian);
    QCOMPARE(structure->childAt(1)->effectiveByteOrder(),
             isGUID ? QSysInfo::LittleEndian : QSysInfo::BigEndian);
    QCOMPARE(structure->childAt(2)->effectiveByteOrder(),
             isGUID ? QSysInfo::LittleEndian : QSysInfo::BigEndian);
    bool ok;
    quint32 val1 = QStringView(uuidString).mid(1, 8).toUInt(&ok, 16);
    QVERIFY(ok);
    quint16 val2 = QStringView(uuidString).mid(10, 4).toUShort(&ok, 16);
    QVERIFY(ok);
    quint16 val3 = QStringView(uuidString).mid(15, 4).toUShort(&ok, 16);
    QVERIFY(ok);
    qDebug() << Qt::hex << val1 << val2 << val3;
    QCOMPARE(structure->childAt(0)->asPrimitive()->value().value<quint32>(), val1);
    QCOMPARE(structure->childAt(1)->asPrimitive()->value().value<quint16>(), val2);
    QCOMPARE(structure->childAt(2)->asPrimitive()->value().value<quint16>(), val3);

    QString typeStr = isGUID ? QStringLiteral("GUID") : QStringLiteral("UUID");
    QCOMPARE(structure->typeName(), typeStr);
    QCOMPARE(structure->valueString(), uuidString);
}

QTEST_GUILESS_MAIN(CustomToStringTest)

#include "customtostringtest.moc"
