// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0

#ifndef QANYSTRINGVIEWUTILS_P_H
#define QANYSTRINGVIEWUTILS_P_H

//
//  W A R N I N G
//  -------------
//
// This file is not part of the Qt API.  It exists purely as an
// implementation detail.  This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.

#include <QtCore/private/qjson_p.h>

#include <QtCore/qanystringview.h>
#include <QtCore/qcbormap.h>
#include <QtCore/qcborvalue.h>

QT_BEGIN_NAMESPACE

namespace QAnyStringViewUtils {

inline QAnyStringView toStringView(const QCborValue &value)
{
    const QCborContainerPrivate *container = QJsonPrivate::Value::container(value);
    if (!container)
        return QAnyStringView();

    const qint64 n = QJsonPrivate::Value::valueHelper(value);
    const auto &e = container->elements.at(n);
    const auto data = container->byteData(e);
    if (!data)
        return QAnyStringView();
    if (e.flags & QtCbor::Element::StringIsUtf16)
        return data->asStringView();
    if (e.flags & QtCbor::Element::StringIsAscii)
        return data->asLatin1();
    return data->asUtf8StringView();
}

inline QAnyStringView toStringView(const QCborMap &map, QLatin1StringView key)
{
    return toStringView(map[key]);
}

// Note: This only works if part is US-ASCII, but there is no type to encode this information!
inline bool endsWith(QAnyStringView whole, QLatin1StringView part)
{
    Q_ASSERT(QtPrivate::isAscii(part));
    return whole.length() >= part.length() && whole.last(part.length()) == part;
}

// Note: This only works if part is US-ASCII, but there is no type to encode this information!
inline bool startsWith(QAnyStringView whole, QLatin1StringView part)
{
    Q_ASSERT(QtPrivate::isAscii(part));
    return whole.length() >= part.length() && whole.first(part.length()) == part;
}

inline bool doesContain(QStringView whole, QLatin1Char part) { return whole.contains(part); }
inline bool doesContain(QLatin1StringView whole, QLatin1Char part) { return whole.contains(part); }
inline bool doesContain(QUtf8StringView whole, QLatin1Char part)
{
    return QByteArrayView(whole.data(), whole.size()).contains(part.toLatin1());
}
inline bool contains(QAnyStringView whole, QLatin1Char part)
{
    return whole.visit([&](auto view) { return doesContain(view, part); });
}

inline qsizetype getLastIndexOf(QStringView whole, QLatin1StringView part)
{
    return whole.lastIndexOf(part);
}
inline qsizetype getLastIndexOf(QLatin1StringView whole, QLatin1StringView part)
{
    return whole.lastIndexOf(part);
}
inline qsizetype getLastIndexOf(QUtf8StringView whole, QLatin1StringView part)
{
    return QByteArrayView(whole.data(), whole.size()).lastIndexOf(part);
}
inline qsizetype lastIndexOf(QAnyStringView whole, QLatin1StringView part)
{
    Q_ASSERT(QtPrivate::isAscii(part));
    return whole.visit([&](auto view) { return getLastIndexOf(view, part); });
}

inline int toInt(QUtf8StringView view)
{
    return QByteArrayView(view.data(), view.length()).toInt();
}
inline int toInt(QLatin1StringView view) { return view.toInt(); }
inline int toInt(QStringView view) { return view.toInt(); }

inline int toInt(QAnyStringView string)
{
    return string.visit([](auto view) { return toInt(view); });
}

template<typename StringView>
QAnyStringView doTrimmed(StringView string)
{
    if constexpr (std::is_same_v<StringView, QStringView>)
        return string.trimmed();
    if constexpr (std::is_same_v<StringView, QLatin1StringView>)
        return string.trimmed();
    if constexpr (std::is_same_v<StringView, QUtf8StringView>)
        return QByteArrayView(string.data(), string.length()).trimmed();
}


inline QAnyStringView trimmed(QAnyStringView string)
{
    return string.visit([](auto data) {
        return doTrimmed(data);
    });
}

template<typename StringView, typename Handler>
auto processAsUtf8(StringView string, Handler &&handler)
{
    if constexpr (std::is_same_v<StringView, QStringView>)
        return handler(QByteArrayView(string.toUtf8()));
    if constexpr (std::is_same_v<StringView, QLatin1StringView>)
        return handler(QByteArrayView(string.data(), string.length()));
    if constexpr (std::is_same_v<StringView, QUtf8StringView>)
        return handler(QByteArrayView(string.data(), string.length()));
    if constexpr (std::is_same_v<StringView, QByteArrayView>)
        return handler(string);
    if constexpr (std::is_same_v<StringView, QByteArray>)
        return handler(QByteArrayView(string));
    if constexpr (std::is_same_v<StringView, QAnyStringView>) {

        // Handler is:
        // * a reference if an lvalue ref is passed
        // * a value otherwise
        // We conserve its nature for passing to the lambda below.
        // This is necessary because we need to decide on the nature of
        // the lambda capture as part of the syntax (prefix '&' or not).
        // So we always pass a reference-conserving wrapper as value.
        struct Wrapper { Handler handler; };

        return string.visit([w = Wrapper { std::forward<Handler>(handler) }](auto view) mutable {
            static_assert(!(std::is_same_v<decltype(view), QAnyStringView>));
            return processAsUtf8(std::move(view), std::forward<Handler>(w.handler));
        });
    }
    Q_UNREACHABLE();
}

// Note: This only works if sep is US-ASCII, but there is no type to encode this information!
inline QList<QAnyStringView> split(QAnyStringView source, QLatin1StringView sep)
{
    Q_ASSERT(QtPrivate::isAscii(sep));

    QList<QAnyStringView> list;
    if (source.isEmpty()) {
        list.append(source);
        return list;
    }

    qsizetype start = 0;
    qsizetype end = source.length();

    for (qsizetype current = 0; current < end; ++current) {
        if (source.mid(current, sep.length()) == sep) {
            list.append(source.mid(start, current - start));
            start = current + sep.length();
        }
    }

    if (start < end)
        list.append(source.mid(start, end - start));

    return list;
}

}

QT_END_NAMESPACE

#endif // QANYSTRINGVIEWUTILS_P_H
