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

#include "metastrings.h"

#include <QFileInfo>

namespace {
using namespace Qt::Literals::StringLiterals;

static constexpr QLatin1StringView PlaceholderClass = "<class>"_L1;
static constexpr QLatin1StringView PlaceholderContext = "<context>"_L1;
static constexpr QLatin1StringView PlaceholderFile = "<file>"_L1;
static constexpr QLatin1StringView UnnamedPlaceholder = "<unnamed>"_L1;

static constexpr QLatin1String CppMagicComment = "TRANSLATOR"_L1;
} // namespace

QT_BEGIN_NAMESPACE

bool MetaStrings::parse(QString &string)
{
    const QChar *ptr = string.unicode();
    if (*ptr == extraCommentAnotation && ptr[1].isSpace()) {
        string.remove(0, 2);
        m_extracomment += string;
        if (!m_extracomment.endsWith(u'\n'))
            m_extracomment.push_back(u'\n');
        m_extracomment.detach();
    } else if (*ptr == idAnotation && ptr[1].isSpace()) {
        string.remove(0, 2);
        m_msgid = string.simplified();
        m_msgid.detach();
        m_error =
                "Setting translation IDs using //= or #= is deprecated and will be removed in the upcoming versions.\n"_L1;
    } else if (*ptr == extraAnotation && ptr[1].isSpace()) {
        string.remove(0, 2);
        const QString trimmed = string.trimmed();
        int k = trimmed.indexOf(u' ');
        if (k > -1) {
            QString commentvalue = trimmed.mid(k + 1).trimmed();
            if (commentvalue.startsWith(u'"') && commentvalue.endsWith(u'"')
                && commentvalue.size() != 1) {
                commentvalue = commentvalue.sliced(1, commentvalue.size() - 2);
            }
            m_extra.insert(trimmed.left(k), commentvalue);
        }
    } else if (*ptr == sourceTextAnotation && ptr[1].isSpace()) {
        m_sourcetext.reserve(m_sourcetext.size() + string.size() - 2);
        ushort *ptr = (ushort *)m_sourcetext.data() + m_sourcetext.size();
        int p = 2, c;
        forever {
            if (p >= string.size())
                break;
            c = string.unicode()[p++].unicode();
            if (isspace(c))
                continue;
            if (c != '"') {
                m_error = "Unexpected character in meta string\n"_L1;
                break;
            }
            forever {
                if (p >= string.size()) {
                whoops:
                    m_error = "Unterminated meta string\n"_L1;
                    break;
                }
                c = string.unicode()[p++].unicode();
                if (c == '"')
                    break;
                if (c == '\\') {
                    if (p >= string.size())
                        goto whoops;
                    c = string.unicode()[p++].unicode();
                    if (c == '\n')
                        goto whoops;
                    *ptr++ = '\\';
                }
                *ptr++ = c;
            }
        }
        m_sourcetext.resize(ptr - (ushort *)m_sourcetext.data());
    } else if (*ptr == labelAnotation && ptr[1].isSpace()) {
        string.remove(0, 2);
        m_label = string.trimmed().simplified();
        m_label.detach();
    } else if (const QString trimmed = string.trimmed(); trimmed.startsWith(CppMagicComment)) {
        qsizetype idx = CppMagicComment.size();
        QString comment =
                QString::fromRawData(trimmed.unicode() + idx, trimmed.size() - idx).simplified();
        if (int k = comment.indexOf(u' '); k != -1) {
            QString context = comment.left(k);
            comment.remove(0, k + 1);
            m_magicComment.emplace(MagicComment{ std::move(context), std::move(comment) });
        }
    }

    return m_error.isEmpty();
}

bool MetaStrings::resolveLabel(const QString &filename, const QString &context,
                               const QString &className)
{
    if (m_label.contains(PlaceholderClass)) {
        QString classStr = className;
        if (classStr.isEmpty()) {
            classStr = UnnamedPlaceholder;
            m_error =
                    "Label placeholder <class> used, but no class available. Using <unnamed>.\n"_L1;
        }
        m_label.replace(PlaceholderClass, classStr);
    }

    if (m_label.contains(PlaceholderContext)) {
        QString contextStr = context;
        if (contextStr.isEmpty()) {
            contextStr = UnnamedPlaceholder;
            m_error +=
                    "Label placeholder <context> used, but no context available. Using <unnamed>.\n"_L1;
        }
        m_label.replace(PlaceholderContext, contextStr);
    }

    if (m_label.contains(PlaceholderFile))
        m_label.replace(PlaceholderFile, QFileInfo(filename).fileName());

    return m_error.isEmpty();
}

void MetaStrings::clear()
{
    m_magicComment.reset();
    m_extracomment.clear();
    m_msgid.clear();
    m_label.clear();
    m_sourcetext.clear();
    m_extra.clear();
}

bool MetaStrings::hasData() const
{
    return !m_msgid.isEmpty() || m_magicComment || !m_sourcetext.isEmpty()
            || !m_extracomment.isEmpty() || !m_extra.isEmpty() || !m_label.isEmpty();
}

QT_END_NAMESPACE
