// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

#include <QFile>
#include <QCborStreamWriter>
#include <QCborStreamReader>
#include <QCborValue>

using namespace Qt::StringLiterals;

void examples(QCborStreamWriter &writer)
{
    {
        //! [0]
        QCborValue(QCborTag(2), QByteArray("\x01\0\0\0\0\0\0\0\0", 9));
        //! [0]
    }

    {
        //! [1]
        using namespace Qt::StringLiterals;
        // ...
        writer.startMap(4);    // 4 elements in the map

        writer.append("label"_L1);
        writer.append("journald"_L1);

        writer.append("autoDetect"_L1);
        writer.append(false);

        writer.append("condition"_L1);
        writer.append("libs.journald"_L1);

        writer.append("output"_L1);
        writer.startArray(1);
        writer.append("privateFeature"_L1);
        writer.endArray();

        writer.endMap();
        //! [1]
    }

    {
        //! [2]
        QFile f("output");
        QCborStreamWriter writer(&f);
        writer.startMap(0);
        writer.endMap();
        //! [2]
    }
}

//! [3]
QByteArray encodedNumber(qint64 value)
{
    QByteArray ba;
    QCborStreamWriter writer(&ba);
    writer.append(value);
    return ba;
}
//! [3]

void writer_examples(QCborStreamWriter &writer)
{
    //! [4]
    writer.append(0U);
    writer.append(Q_UINT64_C(4294967296));
    writer.append(std::numeric_limits<quint64>::max());
    //! [4]

    //! [5]
    writer.append(0);
    writer.append(-1);
    writer.append(Q_INT64_C(4294967296));
    writer.append(std::numeric_limits<qint64>::max());
    //! [5]

    //! [6]
    writer.append(QCborNegativeInteger(1));
    writer.append(QCborNegativeInteger(Q_INT64_C(4294967296)));
    writer.append(QCborNegativeInteger(-quint64(std::numeric_limits<qint64>::min())));
    //! [6]
}
//! [7]
void writeFile(QCborStreamWriter &writer, const QString &fileName)
{
    QFile f(fileName);
    if (f.open(QIODevice::ReadOnly))
        writer.append(f.readAll());
}
//! [7]

void helloWorld(QCborStreamWriter &writer)
{
    //! [8]
    using namespace Qt::StringLiterals;
    // ...
    writer.append("Hello, World"_L1);
    //! [8]
}

//! [9]
void writeString(QCborStreamWriter &writer, const QString &str)
{
    writer.append(str);
}
//! [9]

//! [10]
void writeRxPattern(QCborStreamWriter &writer, const QRegularExpression &rx)
{
    writer.append(QCborTag(36));
    writer.append(rx.pattern());
}
//! [10]

//! [11]
void writeCurrentTime(QCborStreamWriter &writer)
{
    writer.append(QCborKnownTags::UnixTime_t);
    writer.append(qint64(time(nullptr)));
}
//! [11]

void ammend_example(QCborStreamWriter &writer)
{
    //! [12]
    writer.append(QCborSimpleType::Null);
    writer.append(QCborSimpleType(32));
    //! [12]
}

//! [13]
void writeFloat(QCborStreamWriter &writer, float f)
{
    qfloat16 f16 = qfloat16(f);
    if (qIsNaN(f) || f16 == f)
        writer.append(f16);
    else
        writer.append(f);
}
//! [13]

//! [14]
void writeFloat(QCborStreamWriter &writer, double d)
{
    float f = d;
    if (qIsNaN(d) || d == f)
        writer.append(f);
    else
        writer.append(d);
}
//! [14]

//! [15]
void writeDouble(QCborStreamWriter &writer, double d)
{
    float f;
    if (qIsNaN(d)) {
        writer.append(qfloat16(qQNaN()));
    } else if (qIsInf(d)) {
        writer.append(d < 0 ? -qInf() : qInf());
    } else if ((f = d) == d) {
        qfloat16 f16 = qfloat16(f);
        if (f16 == f)
            writer.append(f16);
        else
            writer.append(f);
    } else {
        writer.append(d);
    }
}
//! [15]

void snippets_16_19(QCborStreamWriter &writer, bool b)
{
    //! [16]
    writer.append(b ? QCborSimpleType::True : QCborSimpleType::False);
    //! [16]

    //! [17]
    writer.append(QCborSimpleType::Null);
    //! [17]

    //! [18]
    writer.append(QCborSimpleType::Null);
    //! [18]

    //! [19]
    writer.append(QCborSimpleType::Undefined);
    //! [19]
}

//! [20]
void appendList(QCborStreamWriter &writer, const QList<QString> &values)
{
    writer.startArray();
    for (const QString &s : values)
        writer.append(s);
    writer.endArray();
}
//! [20]

namespace repetitions
{
    //! [21]
    void appendList(QCborStreamWriter &writer, const QStringList &list)
    {
        writer.startArray(list.size());
        for (const QString &s : list)
            writer.append(s);
        writer.endArray();
    }
    //! [21]

    //! [22]
    void appendMap(QCborStreamWriter &writer, const QList<std::pair<int, QString>> &values)
    {
        writer.startMap();
        for (const auto pair : values) {
            writer.append(pair.first);
            writer.append(pair.second);
        }
        writer.endMap();
    }
    //! [22]
}

//! [23]
void appendMap(QCborStreamWriter &writer, const QMap<int, QString> &map)
{
    writer.startMap(map.size());
    for (auto it = map.cbegin(), end = map.cend(); it != end; ++it) {
        writer.append(it.key());
        writer.append(it.value());
    }
    writer.endMap();
}
//! [23]

void handleFixedWidth(QCborStreamReader &reader);

void handleString(QCborStreamReader &reader);

//! [24]
void handleStream(QCborStreamReader &reader)
{
    switch (reader.type())
    {
    case QCborStreamReader::UnsignedInteger:
    case QCborStreamReader::NegativeInteger:
    case QCborStreamReader::SimpleType:
    case QCborStreamReader::Float16:
    case QCborStreamReader::Float:
    case QCborStreamReader::Double:
        handleFixedWidth(reader);
        reader.next();
        break;
    case QCborStreamReader::ByteArray:
    case QCborStreamReader::String:
        handleString(reader);
        break;
    case QCborStreamReader::Array:
    case QCborStreamReader::Map:
        reader.enterContainer();
        while (reader.lastError() == QCborError::NoError)
            handleStream(reader);
        if (reader.lastError() == QCborError::NoError)
            reader.leaveContainer();
    }

}
//! [24]

QString readOneElement(QCborStreamReader &reader);

//! [25]
QVariantList populateFromCbor(QCborStreamReader &reader)
{
    QVariantList list;
    if (reader.isLengthKnown())
        list.reserve(reader.length());

    reader.enterContainer();
    while (reader.lastError() == QCborError::NoError && reader.hasNext())
        list.append(readOneElement(reader));
    if (reader.lastError() == QCborError::NoError)
        reader.leaveContainer();

    return list;
}
//! [25]

namespace secondDefinition
{
    QString readElementAsString(QCborStreamReader &reader);
    QVariantMap setMapLength(QVariantMap &map, qsizetype length);

    //! [26]
    QVariantMap populateFromCbor(QCborStreamReader &reader)
    {
        QVariantMap map;
        if (reader.isLengthKnown())
            map = setMapLength(map, reader.length());

        reader.enterContainer();
        while (reader.lastError() == QCborError::NoError && reader.hasNext()) {
            QString key = readElementAsString(reader);
            map.insert(key, readOneElement(reader));
        }
        if (reader.lastError() == QCborError::NoError)
            reader.leaveContainer();

        return map;
    }
    //! [26]
}

//! [27]
QString decodeString(QCborStreamReader &reader)
{
    QString result;
    auto r = reader.readString();
    while (r.status == QCborStreamReader::Ok) {
        result += r.data;
        r = reader.readString();
    }

    if (r.status == QCborStreamReader::Error) {
        // handle error condition
        result.clear();
    }
    return result;
}
//! [27]

//! [28]
QByteArray decodeBytearray(QCborStreamReader &reader)
{
    QByteArray result;
    auto r = reader.readByteArray();
    while (r.status == QCborStreamReader::Ok) {
        result += r.data;
        r = reader.readByteArray();
    }

    if (r.status == QCborStreamReader::Error) {
        // handle error condition
        result.clear();
    }
    return result;
}
//! [28]

void do_while_example(QCborStreamReader &reader, QByteArray &buffer)
{
    //! [29]
    QCborStreamReader::StringResult<qsizetype> result;
    do {
        qsizetype size = reader.currentStringChunkSize();
        qsizetype oldsize = buffer.size();
        buffer.resize(oldsize + size);
        result = reader.readStringChunk(buffer.data() + oldsize, size);
    } while (result.status == QCborStreamReader::Ok);
    //! [29]
}
