// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only


#include "qqmldommock_p.h"
#include "qqmldomitem_p.h"
#include "qqmldomcomments_p.h"

#include <QtCore/QBasicMutex>
#include <QtCore/QMutexLocker>

QT_BEGIN_NAMESPACE

namespace QQmlJS {
namespace Dom {

MockObject MockObject::copy() const
{
    QMap<QString, MockObject> newObjs;
    auto objs = subObjects;
    auto itO = objs.cbegin();
    auto endO = objs.cend();
    while (itO != endO) {
        newObjs.insert(itO.key(), itO->copy());
        ++itO;
    }
    return MockObject(pathFromOwner(), newObjs, subValues);
}

std::pair<QString, MockObject> MockObject::asStringPair() const
{
    return std::make_pair(pathFromOwner().last().headName(), *this);
}

bool MockObject::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
    static QHash<QString, QString> knownFields;
    static QBasicMutex m;
    auto toField = [](const QString &f) -> QStringView {
        QMutexLocker l(&m);
        if (!knownFields.contains(f))
            knownFields[f] = f;
        return knownFields[f];
    };
    bool cont = CommentableDomElement::iterateDirectSubpaths(self, visitor);
    auto itV = subValues.begin();
    auto endV = subValues.end();
    while (itV != endV) {
        cont = cont && self.invokeVisitorOnValue(visitor, PathEls::Field(toField(itV.key())), *itV);
        ++itV;
    }
    auto itO = subObjects.begin();
    auto endO = subObjects.end();
    while (itO != endO) {
        cont = cont && visitor(PathEls::Field(toField(itO.key())), [&self, &itO]() {
                   return self.copy(&(*itO));
               });
        ++itO;
    }
    return cont;
}

std::shared_ptr<OwningItem> MockOwner::doCopy(const DomItem &) const
{
    return std::make_shared<MockOwner>(*this);
}

MockOwner::MockOwner(const MockOwner &o)
    : OwningItem(o), pathFromTop(o.pathFromTop), subValues(o.subValues)
{
    auto objs = o.subObjects;
    auto itO = objs.cbegin();
    auto endO = objs.cend();
    while (itO != endO) {
        subObjects.insert(itO.key(), itO->copy());
        ++itO;
    }
}

std::shared_ptr<MockOwner> MockOwner::makeCopy(const DomItem &self) const
{
    return std::static_pointer_cast<MockOwner>(doCopy(self));
}

Path MockOwner::canonicalPath(const DomItem &) const
{
    return pathFromTop;
}

bool MockOwner::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
    static QHash<QString, QString> knownFields;
    static QBasicMutex m;
    auto toField = [](const QString &f) -> QStringView {
        QMutexLocker l(&m);
        if (!knownFields.contains(f))
            knownFields[f] = f;
        return knownFields[f];
    };
    {
        auto itV = subValues.begin();
        auto endV = subValues.end();
        while (itV != endV) {
            if (!self.invokeVisitorOnValue(visitor, PathEls::Field(toField(itV.key())), *itV))
                return false;
            ++itV;
        }
    }
    {
        auto itO = subObjects.begin();
        auto endO = subObjects.end();
        while (itO != endO) {
            if (!visitor(PathEls::Field(toField(itO.key())),
                         [&self, &itO]() { return self.copy(&(*itO)); })) {
                return false;
            }
            ++itO;
        }
    }
    {
        auto it = subMaps.begin();
        auto end = subMaps.end();
        while (it != end) {
            if (!self.invokeVisitorOnField(visitor, toField(it.key()), it.value()))
                return false;
            ++it;
        }
    }
    {
        auto it = subMultiMaps.begin();
        auto end = subMultiMaps.end();
        while (it != end) {
            if (!self.invokeVisitorOnField(visitor, toField(it.key()), it.value()))
                return false;
            ++it;
        }
    }
    {
        auto it = subLists.begin();
        auto end = subLists.end();
        while (it != end) {
            if (!self.invokeVisitorOnField(visitor, toField(it.key()), it.value()))
                return false;
            ++it;
        }
    }
    return true;
}

} // end namespace Dom
} // end namespace QQmlJS
QT_END_NAMESPACE
