18bf80f4bSopenharmony_ci/*
28bf80f4bSopenharmony_ci * Copyright (c) 2024 Huawei Device Co., Ltd.
38bf80f4bSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
48bf80f4bSopenharmony_ci * you may not use this file except in compliance with the License.
58bf80f4bSopenharmony_ci * You may obtain a copy of the License at
68bf80f4bSopenharmony_ci *
78bf80f4bSopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
88bf80f4bSopenharmony_ci *
98bf80f4bSopenharmony_ci * Unless required by applicable law or agreed to in writing, software
108bf80f4bSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
118bf80f4bSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
128bf80f4bSopenharmony_ci * See the License for the specific language governing permissions and
138bf80f4bSopenharmony_ci * limitations under the License.
148bf80f4bSopenharmony_ci */
158bf80f4bSopenharmony_ci
168bf80f4bSopenharmony_ci#include "entity_collection.h"
178bf80f4bSopenharmony_ci
188bf80f4bSopenharmony_ci#include <cinttypes>
198bf80f4bSopenharmony_ci#include <PropertyTools/property_data.h>
208bf80f4bSopenharmony_ci
218bf80f4bSopenharmony_ci#include <base/containers/fixed_string.h>
228bf80f4bSopenharmony_ci#include <core/ecs/intf_entity_manager.h>
238bf80f4bSopenharmony_ci#include <core/log.h>
248bf80f4bSopenharmony_ci#include <core/property/intf_property_api.h>
258bf80f4bSopenharmony_ci
268bf80f4bSopenharmony_ci#include "ecs_util.h"
278bf80f4bSopenharmony_ci
288bf80f4bSopenharmony_ciusing namespace BASE_NS;
298bf80f4bSopenharmony_ciusing namespace CORE_NS;
308bf80f4bSopenharmony_ci
318bf80f4bSopenharmony_ciSCENE_BEGIN_NAMESPACE()
328bf80f4bSopenharmony_ci
338bf80f4bSopenharmony_ciEntityCollection::EntityCollection(IEcs& ecs, string_view uri, string_view contextUri)
348bf80f4bSopenharmony_ci    : ecs_(ecs), uri_(uri), contextUri_(contextUri)
358bf80f4bSopenharmony_ci{}
368bf80f4bSopenharmony_ci
378bf80f4bSopenharmony_civoid EntityCollection::AddListener(IEntityCollection::IListener& listener)
388bf80f4bSopenharmony_ci{
398bf80f4bSopenharmony_ci    BASE_ASSERT(&listener);
408bf80f4bSopenharmony_ci    listeners_.emplace_back(&listener);
418bf80f4bSopenharmony_ci}
428bf80f4bSopenharmony_ci
438bf80f4bSopenharmony_civoid EntityCollection::RemoveListener(IEntityCollection::IListener& listener)
448bf80f4bSopenharmony_ci{
458bf80f4bSopenharmony_ci    BASE_ASSERT(&listener);
468bf80f4bSopenharmony_ci    for (size_t i = 0; i < listeners_.size(); ++i) {
478bf80f4bSopenharmony_ci        if (&listener == listeners_[i]) {
488bf80f4bSopenharmony_ci            listeners_.erase(listeners_.begin() + i);
498bf80f4bSopenharmony_ci            return;
508bf80f4bSopenharmony_ci        }
518bf80f4bSopenharmony_ci    }
528bf80f4bSopenharmony_ci
538bf80f4bSopenharmony_ci    // trying to remove a non-existent listener.
548bf80f4bSopenharmony_ci    BASE_ASSERT(true);
558bf80f4bSopenharmony_ci}
568bf80f4bSopenharmony_ci
578bf80f4bSopenharmony_ciIEcs& EntityCollection::GetEcs() const
588bf80f4bSopenharmony_ci{
598bf80f4bSopenharmony_ci    return ecs_;
608bf80f4bSopenharmony_ci}
618bf80f4bSopenharmony_ci
628bf80f4bSopenharmony_cistring EntityCollection::GetUri() const
638bf80f4bSopenharmony_ci{
648bf80f4bSopenharmony_ci    return uri_;
658bf80f4bSopenharmony_ci}
668bf80f4bSopenharmony_ci
678bf80f4bSopenharmony_civoid EntityCollection::SetUri(const BASE_NS::string& uri)
688bf80f4bSopenharmony_ci{
698bf80f4bSopenharmony_ci    uri_ = uri;
708bf80f4bSopenharmony_ci}
718bf80f4bSopenharmony_ci
728bf80f4bSopenharmony_cistring EntityCollection::GetContextUri() const
738bf80f4bSopenharmony_ci{
748bf80f4bSopenharmony_ci    return contextUri_;
758bf80f4bSopenharmony_ci}
768bf80f4bSopenharmony_ci
778bf80f4bSopenharmony_cistring EntityCollection::GetSrc() const
788bf80f4bSopenharmony_ci{
798bf80f4bSopenharmony_ci    return src_;
808bf80f4bSopenharmony_ci}
818bf80f4bSopenharmony_ci
828bf80f4bSopenharmony_civoid EntityCollection::SetSrc(string_view src)
838bf80f4bSopenharmony_ci{
848bf80f4bSopenharmony_ci    src_ = src;
858bf80f4bSopenharmony_ci    MarkModified(true);
868bf80f4bSopenharmony_ci}
878bf80f4bSopenharmony_ci
888bf80f4bSopenharmony_cistring EntityCollection::GetType() const
898bf80f4bSopenharmony_ci{
908bf80f4bSopenharmony_ci    return type_;
918bf80f4bSopenharmony_ci}
928bf80f4bSopenharmony_ci
938bf80f4bSopenharmony_civoid EntityCollection::SetType(string_view type)
948bf80f4bSopenharmony_ci{
958bf80f4bSopenharmony_ci    type_ = type;
968bf80f4bSopenharmony_ci    MarkModified(true);
978bf80f4bSopenharmony_ci}
988bf80f4bSopenharmony_ci
998bf80f4bSopenharmony_cisize_t EntityCollection::GetEntityCount() const
1008bf80f4bSopenharmony_ci{
1018bf80f4bSopenharmony_ci    return entities_.size();
1028bf80f4bSopenharmony_ci}
1038bf80f4bSopenharmony_ci
1048bf80f4bSopenharmony_ciEntityReference EntityCollection::GetEntity(size_t collectionIndex) const
1058bf80f4bSopenharmony_ci{
1068bf80f4bSopenharmony_ci    BASE_ASSERT(collectionIndex < entities_.size());
1078bf80f4bSopenharmony_ci    if (collectionIndex >= entities_.size()) {
1088bf80f4bSopenharmony_ci        return EntityReference {};
1098bf80f4bSopenharmony_ci    }
1108bf80f4bSopenharmony_ci    return entities_[collectionIndex];
1118bf80f4bSopenharmony_ci}
1128bf80f4bSopenharmony_ci
1138bf80f4bSopenharmony_ciEntityReference EntityCollection::GetEntity(string_view localContextId) const
1148bf80f4bSopenharmony_ci{
1158bf80f4bSopenharmony_ci    const auto it = entityIdentifiers_.find(localContextId);
1168bf80f4bSopenharmony_ci    if (it != entityIdentifiers_.end()) {
1178bf80f4bSopenharmony_ci        return it->second;
1188bf80f4bSopenharmony_ci    }
1198bf80f4bSopenharmony_ci    const auto itt = namedEntities_.find(localContextId);
1208bf80f4bSopenharmony_ci    if (itt != namedEntities_.end()) {
1218bf80f4bSopenharmony_ci        return itt->second;
1228bf80f4bSopenharmony_ci    }
1238bf80f4bSopenharmony_ci    return EntityReference {};
1248bf80f4bSopenharmony_ci}
1258bf80f4bSopenharmony_ci
1268bf80f4bSopenharmony_ciEntityReference EntityCollection::GetEntityRecursive(string_view localContextId) const
1278bf80f4bSopenharmony_ci{
1288bf80f4bSopenharmony_ci    auto ret = GetEntity(localContextId);
1298bf80f4bSopenharmony_ci
1308bf80f4bSopenharmony_ci    if (!CORE_NS::EntityUtil::IsValid(ret)) {
1318bf80f4bSopenharmony_ci        for (auto& it : collections_) {
1328bf80f4bSopenharmony_ci            ret = it->GetEntityRecursive(localContextId);
1338bf80f4bSopenharmony_ci            if (CORE_NS::EntityUtil::IsValid(ret)) {
1348bf80f4bSopenharmony_ci                break;
1358bf80f4bSopenharmony_ci            }
1368bf80f4bSopenharmony_ci        }
1378bf80f4bSopenharmony_ci    }
1388bf80f4bSopenharmony_ci
1398bf80f4bSopenharmony_ci    return ret;
1408bf80f4bSopenharmony_ci}
1418bf80f4bSopenharmony_ci
1428bf80f4bSopenharmony_civoid EntityCollection::RemoveEntityRecursive(CORE_NS::Entity entity)
1438bf80f4bSopenharmony_ci{
1448bf80f4bSopenharmony_ci    for (auto entityRef = entities_.cbegin(); entityRef != entities_.cend();) {
1458bf80f4bSopenharmony_ci        if (entity == *entityRef) {
1468bf80f4bSopenharmony_ci            entityRef = entities_.erase(entityRef);
1478bf80f4bSopenharmony_ci        } else {
1488bf80f4bSopenharmony_ci            entityRef++;
1498bf80f4bSopenharmony_ci        }
1508bf80f4bSopenharmony_ci    }
1518bf80f4bSopenharmony_ci
1528bf80f4bSopenharmony_ci    for (auto entityRef = namedEntities_.cbegin(); entityRef != namedEntities_.cend();) {
1538bf80f4bSopenharmony_ci        if (entityRef->second == entity) {
1548bf80f4bSopenharmony_ci            entityRef = namedEntities_.erase(entityRef);
1558bf80f4bSopenharmony_ci        } else {
1568bf80f4bSopenharmony_ci            ++entityRef;
1578bf80f4bSopenharmony_ci        }
1588bf80f4bSopenharmony_ci    }
1598bf80f4bSopenharmony_ci
1608bf80f4bSopenharmony_ci    auto uniqueIdentifier = GetUniqueIdentifier(entity);
1618bf80f4bSopenharmony_ci    if (!uniqueIdentifier.empty()) {
1628bf80f4bSopenharmony_ci        entityIdentifiers_.erase(uniqueIdentifier);
1638bf80f4bSopenharmony_ci    }
1648bf80f4bSopenharmony_ci
1658bf80f4bSopenharmony_ci    for (auto& it : collections_) {
1668bf80f4bSopenharmony_ci        it->RemoveEntityRecursive(entity);
1678bf80f4bSopenharmony_ci    }
1688bf80f4bSopenharmony_ci}
1698bf80f4bSopenharmony_ci
1708bf80f4bSopenharmony_ciarray_view<const EntityReference> EntityCollection::GetEntities() const
1718bf80f4bSopenharmony_ci{
1728bf80f4bSopenharmony_ci    return entities_;
1738bf80f4bSopenharmony_ci}
1748bf80f4bSopenharmony_ci
1758bf80f4bSopenharmony_civoid EntityCollection::AddEntity(EntityReference entity)
1768bf80f4bSopenharmony_ci{
1778bf80f4bSopenharmony_ci    AddEntities({ &entity, 1 });
1788bf80f4bSopenharmony_ci}
1798bf80f4bSopenharmony_ci
1808bf80f4bSopenharmony_civoid EntityCollection::AddEntities(array_view<const EntityReference> entities)
1818bf80f4bSopenharmony_ci{
1828bf80f4bSopenharmony_ci    entities_.reserve(entities_.size() + entities.size());
1838bf80f4bSopenharmony_ci    for (const auto& entity : entities) {
1848bf80f4bSopenharmony_ci        if (entity != Entity {}) {
1858bf80f4bSopenharmony_ci            // TODO: make sure that the same entity is not added twice.
1868bf80f4bSopenharmony_ci            entities_.emplace_back(entity);
1878bf80f4bSopenharmony_ci        } else {
1888bf80f4bSopenharmony_ci            CORE_LOG_D("Trying to add null entity reference to a collection");
1898bf80f4bSopenharmony_ci        }
1908bf80f4bSopenharmony_ci    }
1918bf80f4bSopenharmony_ci    MarkModified(true);
1928bf80f4bSopenharmony_ci}
1938bf80f4bSopenharmony_ci
1948bf80f4bSopenharmony_cibool EntityCollection::RemoveEntity(EntityReference entity)
1958bf80f4bSopenharmony_ci{
1968bf80f4bSopenharmony_ci    for (size_t i = 0; i < entities_.size(); ++i) {
1978bf80f4bSopenharmony_ci        if (entities_[i] == entity) {
1988bf80f4bSopenharmony_ci            entities_.erase(entities_.begin() + i);
1998bf80f4bSopenharmony_ci
2008bf80f4bSopenharmony_ci            // Also remove any related id mappings.
2018bf80f4bSopenharmony_ci            for (auto it = namedEntities_.begin(); it != namedEntities_.end(); ++it) {
2028bf80f4bSopenharmony_ci                if (it->second == entity) {
2038bf80f4bSopenharmony_ci                    namedEntities_.erase(it);
2048bf80f4bSopenharmony_ci                    break;
2058bf80f4bSopenharmony_ci                }
2068bf80f4bSopenharmony_ci            }
2078bf80f4bSopenharmony_ci
2088bf80f4bSopenharmony_ci            auto uniqueIdentifier = GetUniqueIdentifier(entity);
2098bf80f4bSopenharmony_ci            if (!uniqueIdentifier.empty()) {
2108bf80f4bSopenharmony_ci                entityIdentifiers_.erase(uniqueIdentifier);
2118bf80f4bSopenharmony_ci            }
2128bf80f4bSopenharmony_ci
2138bf80f4bSopenharmony_ci            // TODO:  If this collection is overriding another "template" collection, when removing entities, we
2148bf80f4bSopenharmony_ci            // need to remember that and the information about deletion needs to be serialized. Maybe check if
2158bf80f4bSopenharmony_ci            // (src_.empty()). However this also needs to work with undo
2168bf80f4bSopenharmony_ci
2178bf80f4bSopenharmony_ci            MarkModified(true);
2188bf80f4bSopenharmony_ci            return true;
2198bf80f4bSopenharmony_ci        }
2208bf80f4bSopenharmony_ci    }
2218bf80f4bSopenharmony_ci
2228bf80f4bSopenharmony_ci    // Not found. Check the sub-collections.
2238bf80f4bSopenharmony_ci    for (auto& collection : collections_) {
2248bf80f4bSopenharmony_ci        BASE_ASSERT(collection);
2258bf80f4bSopenharmony_ci        if (collection->RemoveEntity(entity)) {
2268bf80f4bSopenharmony_ci            MarkModified(true);
2278bf80f4bSopenharmony_ci            return true;
2288bf80f4bSopenharmony_ci        }
2298bf80f4bSopenharmony_ci    }
2308bf80f4bSopenharmony_ci
2318bf80f4bSopenharmony_ci    return false;
2328bf80f4bSopenharmony_ci}
2338bf80f4bSopenharmony_ci
2348bf80f4bSopenharmony_civoid EntityCollection::RemoveEntities(array_view<const EntityReference> entities)
2358bf80f4bSopenharmony_ci{
2368bf80f4bSopenharmony_ci    for (auto& entity : entities) {
2378bf80f4bSopenharmony_ci        RemoveEntity(entity);
2388bf80f4bSopenharmony_ci    }
2398bf80f4bSopenharmony_ci}
2408bf80f4bSopenharmony_ci
2418bf80f4bSopenharmony_civoid EntityCollection::SetId(string_view id, EntityReference entity)
2428bf80f4bSopenharmony_ci{
2438bf80f4bSopenharmony_ci    namedEntities_[id] = entity;
2448bf80f4bSopenharmony_ci}
2458bf80f4bSopenharmony_ci
2468bf80f4bSopenharmony_cistring_view EntityCollection::GetId(Entity entity) const
2478bf80f4bSopenharmony_ci{
2488bf80f4bSopenharmony_ci    for (auto& it : namedEntities_) {
2498bf80f4bSopenharmony_ci        if (it.second == entity) {
2508bf80f4bSopenharmony_ci            return it.first;
2518bf80f4bSopenharmony_ci        }
2528bf80f4bSopenharmony_ci    }
2538bf80f4bSopenharmony_ci    return {};
2548bf80f4bSopenharmony_ci}
2558bf80f4bSopenharmony_ci
2568bf80f4bSopenharmony_cistring_view EntityCollection::GetIdRecursive(Entity entity) const
2578bf80f4bSopenharmony_ci{
2588bf80f4bSopenharmony_ci    auto ret = GetId(entity);
2598bf80f4bSopenharmony_ci    if (ret.empty()) {
2608bf80f4bSopenharmony_ci        for (auto& it : collections_) {
2618bf80f4bSopenharmony_ci            ret = it->GetIdRecursive(entity);
2628bf80f4bSopenharmony_ci            if (!ret.empty()) {
2638bf80f4bSopenharmony_ci                break;
2648bf80f4bSopenharmony_ci            }
2658bf80f4bSopenharmony_ci        }
2668bf80f4bSopenharmony_ci    }
2678bf80f4bSopenharmony_ci    return ret;
2688bf80f4bSopenharmony_ci}
2698bf80f4bSopenharmony_ci
2708bf80f4bSopenharmony_civoid EntityCollection::SetUniqueIdentifier(string_view id, EntityReference entity)
2718bf80f4bSopenharmony_ci{
2728bf80f4bSopenharmony_ci    entityIdentifiers_[id] = entity;
2738bf80f4bSopenharmony_ci}
2748bf80f4bSopenharmony_ci
2758bf80f4bSopenharmony_cistring_view EntityCollection::GetUniqueIdentifier(Entity entity) const
2768bf80f4bSopenharmony_ci{
2778bf80f4bSopenharmony_ci    for (auto& it : entityIdentifiers_) {
2788bf80f4bSopenharmony_ci        if (it.second == entity) {
2798bf80f4bSopenharmony_ci            return it.first;
2808bf80f4bSopenharmony_ci        }
2818bf80f4bSopenharmony_ci    }
2828bf80f4bSopenharmony_ci    return {};
2838bf80f4bSopenharmony_ci}
2848bf80f4bSopenharmony_ci
2858bf80f4bSopenharmony_cistring_view EntityCollection::GetUniqueIdentifierRecursive(Entity entity) const
2868bf80f4bSopenharmony_ci{
2878bf80f4bSopenharmony_ci    auto ret = GetUniqueIdentifier(entity);
2888bf80f4bSopenharmony_ci    if (ret.empty()) {
2898bf80f4bSopenharmony_ci        for (auto& it : collections_) {
2908bf80f4bSopenharmony_ci            ret = it->GetUniqueIdentifierRecursive(entity);
2918bf80f4bSopenharmony_ci            if (!ret.empty()) {
2928bf80f4bSopenharmony_ci                break;
2938bf80f4bSopenharmony_ci            }
2948bf80f4bSopenharmony_ci        }
2958bf80f4bSopenharmony_ci    }
2968bf80f4bSopenharmony_ci    return ret;
2978bf80f4bSopenharmony_ci}
2988bf80f4bSopenharmony_ci
2998bf80f4bSopenharmony_cisize_t EntityCollection::GetSubCollectionCount() const
3008bf80f4bSopenharmony_ci{
3018bf80f4bSopenharmony_ci    return collections_.size();
3028bf80f4bSopenharmony_ci}
3038bf80f4bSopenharmony_ci
3048bf80f4bSopenharmony_ciIEntityCollection* EntityCollection::GetSubCollection(size_t index)
3058bf80f4bSopenharmony_ci{
3068bf80f4bSopenharmony_ci    if (index < 0 || index >= collections_.size()) {
3078bf80f4bSopenharmony_ci        return nullptr;
3088bf80f4bSopenharmony_ci    }
3098bf80f4bSopenharmony_ci    return collections_.at(index).get();
3108bf80f4bSopenharmony_ci}
3118bf80f4bSopenharmony_ci
3128bf80f4bSopenharmony_ciconst IEntityCollection* EntityCollection::GetSubCollection(size_t index) const
3138bf80f4bSopenharmony_ci{
3148bf80f4bSopenharmony_ci    if (index < 0 || index >= collections_.size()) {
3158bf80f4bSopenharmony_ci        return nullptr;
3168bf80f4bSopenharmony_ci    }
3178bf80f4bSopenharmony_ci    return collections_.at(index).get();
3188bf80f4bSopenharmony_ci}
3198bf80f4bSopenharmony_ci
3208bf80f4bSopenharmony_ciint32_t EntityCollection::GetSubCollectionIndex(string_view uri) const
3218bf80f4bSopenharmony_ci{
3228bf80f4bSopenharmony_ci    for (size_t i = 0; i < collections_.size(); ++i) {
3238bf80f4bSopenharmony_ci        BASE_ASSERT(collections_[i]);
3248bf80f4bSopenharmony_ci        if (collections_[i]->GetUri() == uri) {
3258bf80f4bSopenharmony_ci            return static_cast<int32_t>(i);
3268bf80f4bSopenharmony_ci        }
3278bf80f4bSopenharmony_ci    }
3288bf80f4bSopenharmony_ci    return -1;
3298bf80f4bSopenharmony_ci}
3308bf80f4bSopenharmony_ci
3318bf80f4bSopenharmony_ciint32_t EntityCollection::GetSubCollectionIndexByRoot(Entity entity) const
3328bf80f4bSopenharmony_ci{
3338bf80f4bSopenharmony_ci    if (entity != Entity {}) {
3348bf80f4bSopenharmony_ci        for (size_t i = 0; i < collections_.size(); ++i) {
3358bf80f4bSopenharmony_ci            BASE_ASSERT(collections_[i]);
3368bf80f4bSopenharmony_ci            if (collections_[i]->GetEntity("/") == entity) {
3378bf80f4bSopenharmony_ci                return static_cast<int32_t>(i);
3388bf80f4bSopenharmony_ci            }
3398bf80f4bSopenharmony_ci        }
3408bf80f4bSopenharmony_ci    }
3418bf80f4bSopenharmony_ci    return -1;
3428bf80f4bSopenharmony_ci}
3438bf80f4bSopenharmony_ci
3448bf80f4bSopenharmony_ciIEntityCollection& EntityCollection::AddSubCollection(string_view uri, string_view contextUri, bool serializable)
3458bf80f4bSopenharmony_ci{
3468bf80f4bSopenharmony_ci    auto collection = EntityCollection::Ptr { new EntityCollection(ecs_, uri, contextUri) };
3478bf80f4bSopenharmony_ci    collection->SetSerialized(serializable);
3488bf80f4bSopenharmony_ci    collections_.emplace_back(move(collection));
3498bf80f4bSopenharmony_ci
3508bf80f4bSopenharmony_ci    // listen to changes in subcollection
3518bf80f4bSopenharmony_ci    collections_.back()->AddListener(*this);
3528bf80f4bSopenharmony_ci
3538bf80f4bSopenharmony_ci    if (serializable) {
3548bf80f4bSopenharmony_ci        MarkModified(true);
3558bf80f4bSopenharmony_ci    }
3568bf80f4bSopenharmony_ci    return *collections_.back();
3578bf80f4bSopenharmony_ci}
3588bf80f4bSopenharmony_ci
3598bf80f4bSopenharmony_ciIEntityCollection& EntityCollection::AddSubCollectionClone(IEntityCollection& collection, string_view uri)
3608bf80f4bSopenharmony_ci{
3618bf80f4bSopenharmony_ci    // TODO: use just the public api
3628bf80f4bSopenharmony_ci    collections_.emplace_back(EntityCollection::Ptr { new EntityCollection(ecs_, uri, collection.GetContextUri()) });
3638bf80f4bSopenharmony_ci    auto& ec = *collections_.back();
3648bf80f4bSopenharmony_ci    static_cast<EntityCollection&>(collection).ClonePrivate(ec);
3658bf80f4bSopenharmony_ci
3668bf80f4bSopenharmony_ci    // listen to changes in subcollection
3678bf80f4bSopenharmony_ci    ec.AddListener(*this);
3688bf80f4bSopenharmony_ci
3698bf80f4bSopenharmony_ci    MarkModified(true);
3708bf80f4bSopenharmony_ci    return ec;
3718bf80f4bSopenharmony_ci}
3728bf80f4bSopenharmony_ci
3738bf80f4bSopenharmony_civoid EntityCollection::RemoveSubCollection(size_t index)
3748bf80f4bSopenharmony_ci{
3758bf80f4bSopenharmony_ci    BASE_ASSERT(index < collections_.size());
3768bf80f4bSopenharmony_ci    if (index < collections_.size()) {
3778bf80f4bSopenharmony_ci        // stop listening to changes in subcollection
3788bf80f4bSopenharmony_ci        auto& ec = collections_.at(index);
3798bf80f4bSopenharmony_ci        ec->RemoveListener(*this);
3808bf80f4bSopenharmony_ci
3818bf80f4bSopenharmony_ci        collections_.erase(collections_.begin() + index);
3828bf80f4bSopenharmony_ci        MarkModified(true);
3838bf80f4bSopenharmony_ci    }
3848bf80f4bSopenharmony_ci}
3858bf80f4bSopenharmony_ci
3868bf80f4bSopenharmony_cisize_t EntityCollection::GetEntityCountRecursive(bool includeDestroyed, bool includeNonSerialized) const
3878bf80f4bSopenharmony_ci{
3888bf80f4bSopenharmony_ci    if (!includeDestroyed && IsMarkedDestroyed()) {
3898bf80f4bSopenharmony_ci        return 0;
3908bf80f4bSopenharmony_ci    }
3918bf80f4bSopenharmony_ci
3928bf80f4bSopenharmony_ci    if (!includeNonSerialized && !IsSerialized()) {
3938bf80f4bSopenharmony_ci        return 0;
3948bf80f4bSopenharmony_ci    }
3958bf80f4bSopenharmony_ci
3968bf80f4bSopenharmony_ci    auto size = entities_.size();
3978bf80f4bSopenharmony_ci    for (const auto& collection : collections_) {
3988bf80f4bSopenharmony_ci        BASE_ASSERT(collection);
3998bf80f4bSopenharmony_ci        size += collection->GetEntityCountRecursive(includeDestroyed, includeNonSerialized);
4008bf80f4bSopenharmony_ci    }
4018bf80f4bSopenharmony_ci    return size;
4028bf80f4bSopenharmony_ci}
4038bf80f4bSopenharmony_ci
4048bf80f4bSopenharmony_civoid EntityCollection::GetEntitiesRecursive(
4058bf80f4bSopenharmony_ci    bool includeDestroyed, vector<EntityReference>& entitiesOut, bool includeNonSerialized) const
4068bf80f4bSopenharmony_ci{
4078bf80f4bSopenharmony_ci    // NOTE: Cloning depends on ordering of entitiesOut.
4088bf80f4bSopenharmony_ci    entitiesOut.reserve(entitiesOut.size() + GetEntityCountRecursive(includeDestroyed, includeNonSerialized));
4098bf80f4bSopenharmony_ci    DoGetEntitiesRecursive(includeDestroyed, includeNonSerialized, entitiesOut);
4108bf80f4bSopenharmony_ci}
4118bf80f4bSopenharmony_ci
4128bf80f4bSopenharmony_civoid EntityCollection::DoGetEntitiesRecursive(
4138bf80f4bSopenharmony_ci    bool includeDestroyed, bool includeNonSerialized, vector<EntityReference>& entitiesOut) const
4148bf80f4bSopenharmony_ci{
4158bf80f4bSopenharmony_ci    if (!includeDestroyed && IsMarkedDestroyed()) {
4168bf80f4bSopenharmony_ci        return;
4178bf80f4bSopenharmony_ci    }
4188bf80f4bSopenharmony_ci
4198bf80f4bSopenharmony_ci    if (!includeNonSerialized && !IsSerialized()) {
4208bf80f4bSopenharmony_ci        return;
4218bf80f4bSopenharmony_ci    }
4228bf80f4bSopenharmony_ci
4238bf80f4bSopenharmony_ci    entitiesOut.insert(entitiesOut.end(), entities_.begin(), entities_.end());
4248bf80f4bSopenharmony_ci    for (const auto& collection : collections_) {
4258bf80f4bSopenharmony_ci        BASE_ASSERT(collection);
4268bf80f4bSopenharmony_ci        collection->DoGetEntitiesRecursive(includeDestroyed, includeNonSerialized, entitiesOut);
4278bf80f4bSopenharmony_ci    }
4288bf80f4bSopenharmony_ci}
4298bf80f4bSopenharmony_ci
4308bf80f4bSopenharmony_cibool EntityCollection::Contains(Entity entity) const
4318bf80f4bSopenharmony_ci{
4328bf80f4bSopenharmony_ci    for (auto& it : entities_) {
4338bf80f4bSopenharmony_ci        if (it == entity) {
4348bf80f4bSopenharmony_ci            return true;
4358bf80f4bSopenharmony_ci        }
4368bf80f4bSopenharmony_ci    }
4378bf80f4bSopenharmony_ci    for (const auto& collection : collections_) {
4388bf80f4bSopenharmony_ci        if (!collection->IsMarkedDestroyed()) {
4398bf80f4bSopenharmony_ci            if (collection->Contains(entity)) {
4408bf80f4bSopenharmony_ci                return true;
4418bf80f4bSopenharmony_ci            }
4428bf80f4bSopenharmony_ci        }
4438bf80f4bSopenharmony_ci    }
4448bf80f4bSopenharmony_ci    return false;
4458bf80f4bSopenharmony_ci}
4468bf80f4bSopenharmony_ci
4478bf80f4bSopenharmony_cibool EntityCollection::IsExternal(Entity entity) const
4488bf80f4bSopenharmony_ci{
4498bf80f4bSopenharmony_ci    for (auto& it : entities_) {
4508bf80f4bSopenharmony_ci        if (it == entity) {
4518bf80f4bSopenharmony_ci            return false;
4528bf80f4bSopenharmony_ci        }
4538bf80f4bSopenharmony_ci    }
4548bf80f4bSopenharmony_ci    return true;
4558bf80f4bSopenharmony_ci}
4568bf80f4bSopenharmony_ci
4578bf80f4bSopenharmony_cibool EntityCollection::isSubCollectionRoot(Entity entity) const
4588bf80f4bSopenharmony_ci{
4598bf80f4bSopenharmony_ci    if (entity == Entity {}) {
4608bf80f4bSopenharmony_ci        return false;
4618bf80f4bSopenharmony_ci    }
4628bf80f4bSopenharmony_ci
4638bf80f4bSopenharmony_ci    for (const auto& collection : collections_) {
4648bf80f4bSopenharmony_ci        if (collection->GetEntity("/") == entity) {
4658bf80f4bSopenharmony_ci            return true;
4668bf80f4bSopenharmony_ci        }
4678bf80f4bSopenharmony_ci    }
4688bf80f4bSopenharmony_ci
4698bf80f4bSopenharmony_ci    return false;
4708bf80f4bSopenharmony_ci}
4718bf80f4bSopenharmony_ci
4728bf80f4bSopenharmony_ciCORE_NS::EntityReference EntityCollection::GetReference(CORE_NS::Entity entity) const
4738bf80f4bSopenharmony_ci{
4748bf80f4bSopenharmony_ci    if (Contains(entity)) {
4758bf80f4bSopenharmony_ci        auto ref = GetEcs().GetEntityManager().GetReferenceCounted(entity);
4768bf80f4bSopenharmony_ci
4778bf80f4bSopenharmony_ci        // Check that this entity was reference counted already (it should be part of the collection).
4788bf80f4bSopenharmony_ci        CORE_ASSERT(ref.GetRefCount() > 1);
4798bf80f4bSopenharmony_ci
4808bf80f4bSopenharmony_ci        return ref;
4818bf80f4bSopenharmony_ci    }
4828bf80f4bSopenharmony_ci    return {};
4838bf80f4bSopenharmony_ci}
4848bf80f4bSopenharmony_ci
4858bf80f4bSopenharmony_civoid EntityCollection::SetActive(bool active)
4868bf80f4bSopenharmony_ci{
4878bf80f4bSopenharmony_ci    isActive_ = active;
4888bf80f4bSopenharmony_ci    const bool effectivelyActive = isActive_ && !isMarkedDestroyed_;
4898bf80f4bSopenharmony_ci
4908bf80f4bSopenharmony_ci    auto& em = ecs_.GetEntityManager();
4918bf80f4bSopenharmony_ci    for (auto& entity : entities_) {
4928bf80f4bSopenharmony_ci        em.SetActive(entity, effectivelyActive);
4938bf80f4bSopenharmony_ci    }
4948bf80f4bSopenharmony_ci
4958bf80f4bSopenharmony_ci    for (auto& collection : collections_) {
4968bf80f4bSopenharmony_ci        BASE_ASSERT(collection);
4978bf80f4bSopenharmony_ci        collection->SetActive(active);
4988bf80f4bSopenharmony_ci    }
4998bf80f4bSopenharmony_ci}
5008bf80f4bSopenharmony_ci
5018bf80f4bSopenharmony_cibool EntityCollection::IsActive() const
5028bf80f4bSopenharmony_ci{
5038bf80f4bSopenharmony_ci    return isActive_;
5048bf80f4bSopenharmony_ci}
5058bf80f4bSopenharmony_ci
5068bf80f4bSopenharmony_civoid EntityCollection::MarkDestroyed(bool destroyed)
5078bf80f4bSopenharmony_ci{
5088bf80f4bSopenharmony_ci    MarkModified(true);
5098bf80f4bSopenharmony_ci    isMarkedDestroyed_ = destroyed;
5108bf80f4bSopenharmony_ci    const bool effectivelyActive = isActive_ && !isMarkedDestroyed_;
5118bf80f4bSopenharmony_ci
5128bf80f4bSopenharmony_ci    // Change the active state of entities without changing the active state of the collection itself.
5138bf80f4bSopenharmony_ci    auto& em = ecs_.GetEntityManager();
5148bf80f4bSopenharmony_ci    for (auto& entity : entities_) {
5158bf80f4bSopenharmony_ci        em.SetActive(entity, effectivelyActive);
5168bf80f4bSopenharmony_ci    }
5178bf80f4bSopenharmony_ci
5188bf80f4bSopenharmony_ci    for (auto& collection : collections_) {
5198bf80f4bSopenharmony_ci        BASE_ASSERT(collection);
5208bf80f4bSopenharmony_ci        collection->MarkDestroyed(destroyed);
5218bf80f4bSopenharmony_ci    }
5228bf80f4bSopenharmony_ci}
5238bf80f4bSopenharmony_ci
5248bf80f4bSopenharmony_cibool EntityCollection::IsMarkedDestroyed() const
5258bf80f4bSopenharmony_ci{
5268bf80f4bSopenharmony_ci    return isMarkedDestroyed_;
5278bf80f4bSopenharmony_ci}
5288bf80f4bSopenharmony_ci
5298bf80f4bSopenharmony_civoid EntityCollection::MarkModified(bool modified)
5308bf80f4bSopenharmony_ci{
5318bf80f4bSopenharmony_ci    if (!IsSerialized()) {
5328bf80f4bSopenharmony_ci        return;
5338bf80f4bSopenharmony_ci    }
5348bf80f4bSopenharmony_ci
5358bf80f4bSopenharmony_ci    if (isMarkedModified_ != modified) {
5368bf80f4bSopenharmony_ci        isMarkedModified_ = modified;
5378bf80f4bSopenharmony_ci        for (auto* l : listeners_) {
5388bf80f4bSopenharmony_ci            l->ModifiedChanged(*this, modified);
5398bf80f4bSopenharmony_ci        }
5408bf80f4bSopenharmony_ci    }
5418bf80f4bSopenharmony_ci}
5428bf80f4bSopenharmony_ci
5438bf80f4bSopenharmony_civoid EntityCollection::MarkModified(bool modified, bool recursive)
5448bf80f4bSopenharmony_ci{
5458bf80f4bSopenharmony_ci    if (!IsSerialized()) {
5468bf80f4bSopenharmony_ci        return;
5478bf80f4bSopenharmony_ci    }
5488bf80f4bSopenharmony_ci
5498bf80f4bSopenharmony_ci    if (recursive && !collections_.empty()) {
5508bf80f4bSopenharmony_ci        for (auto& c : collections_) {
5518bf80f4bSopenharmony_ci            c->MarkModified(modified, true);
5528bf80f4bSopenharmony_ci        }
5538bf80f4bSopenharmony_ci    }
5548bf80f4bSopenharmony_ci    MarkModified(modified);
5558bf80f4bSopenharmony_ci}
5568bf80f4bSopenharmony_ci
5578bf80f4bSopenharmony_cibool EntityCollection::IsMarkedModified() const
5588bf80f4bSopenharmony_ci{
5598bf80f4bSopenharmony_ci    return isMarkedModified_;
5608bf80f4bSopenharmony_ci}
5618bf80f4bSopenharmony_ci
5628bf80f4bSopenharmony_civoid EntityCollection::Clear()
5638bf80f4bSopenharmony_ci{
5648bf80f4bSopenharmony_ci    serializationInfo_.clear();
5658bf80f4bSopenharmony_ci    namedEntities_.clear();
5668bf80f4bSopenharmony_ci    entityIdentifiers_.clear();
5678bf80f4bSopenharmony_ci    entities_.clear();
5688bf80f4bSopenharmony_ci    collections_.clear();
5698bf80f4bSopenharmony_ci    listeners_.clear();
5708bf80f4bSopenharmony_ci
5718bf80f4bSopenharmony_ci    MarkModified(true);
5728bf80f4bSopenharmony_ci}
5738bf80f4bSopenharmony_ci
5748bf80f4bSopenharmony_civoid EntityCollection::CopyContents(IEntityCollection& srcCollection)
5758bf80f4bSopenharmony_ci{
5768bf80f4bSopenharmony_ci    // TODO: use just the public api
5778bf80f4bSopenharmony_ci    static_cast<EntityCollection&>(srcCollection).ClonePrivate(*this);
5788bf80f4bSopenharmony_ci    MarkModified(true);
5798bf80f4bSopenharmony_ci}
5808bf80f4bSopenharmony_ci
5818bf80f4bSopenharmony_civoid EntityCollection::AddEntityToSubcollection(
5828bf80f4bSopenharmony_ci    BASE_NS::string_view collection, BASE_NS::string_view name, CORE_NS::Entity entity, bool makeUnique)
5838bf80f4bSopenharmony_ci{
5848bf80f4bSopenharmony_ci    auto collectionIx = GetSubCollectionIndex(collection);
5858bf80f4bSopenharmony_ci    if (collectionIx == -1) {
5868bf80f4bSopenharmony_ci        AddSubCollection(collection, {}, !makeUnique);
5878bf80f4bSopenharmony_ci        collectionIx = GetSubCollectionCount() - 1;
5888bf80f4bSopenharmony_ci    }
5898bf80f4bSopenharmony_ci
5908bf80f4bSopenharmony_ci    if (auto targetCollection = GetSubCollection(collectionIx)) {
5918bf80f4bSopenharmony_ci        BASE_NS::string postFixed(name.data(), name.size());
5928bf80f4bSopenharmony_ci        if (makeUnique) {
5938bf80f4bSopenharmony_ci            postFixed.append(":");
5948bf80f4bSopenharmony_ci            postFixed.append(BASE_NS::to_hex(entity.id));
5958bf80f4bSopenharmony_ci        }
5968bf80f4bSopenharmony_ci        if (!CORE_NS::EntityUtil::IsValid(targetCollection->GetEntity(postFixed))) {
5978bf80f4bSopenharmony_ci            auto ref = ecs_.GetEntityManager().GetReferenceCounted(entity);
5988bf80f4bSopenharmony_ci            targetCollection->AddEntity(ref);
5998bf80f4bSopenharmony_ci            targetCollection->SetUniqueIdentifier(postFixed, ref);
6008bf80f4bSopenharmony_ci        }
6018bf80f4bSopenharmony_ci    }
6028bf80f4bSopenharmony_ci}
6038bf80f4bSopenharmony_ci
6048bf80f4bSopenharmony_cibool EntityCollection::IsSerialized() const
6058bf80f4bSopenharmony_ci{
6068bf80f4bSopenharmony_ci    return isSerialized_;
6078bf80f4bSopenharmony_ci}
6088bf80f4bSopenharmony_ci
6098bf80f4bSopenharmony_civoid EntityCollection::SetSerialized(bool serialize)
6108bf80f4bSopenharmony_ci{
6118bf80f4bSopenharmony_ci    isSerialized_ = serialize;
6128bf80f4bSopenharmony_ci}
6138bf80f4bSopenharmony_ci
6148bf80f4bSopenharmony_ci// TODO: clean up copying.
6158bf80f4bSopenharmony_cinamespace {
6168bf80f4bSopenharmony_civoid CloneEntitiesFromCollection(IEntityCollection& srcCollection, IEntityCollection& dstCollection,
6178bf80f4bSopenharmony_ci    IEntityCollection& srcSerializationCollection, IEntityCollection& dstSerializationCollection,
6188bf80f4bSopenharmony_ci    array_view<const EntityReference> entities, unordered_map<Entity, Entity>& oldToNew,
6198bf80f4bSopenharmony_ci    vector<EntityReference>& clonedOut)
6208bf80f4bSopenharmony_ci{
6218bf80f4bSopenharmony_ci    const size_t entityCount = entities.size();
6228bf80f4bSopenharmony_ci    for (size_t i = 0; i < entityCount; ++i) {
6238bf80f4bSopenharmony_ci        auto srcEntity = entities[i];
6248bf80f4bSopenharmony_ci
6258bf80f4bSopenharmony_ci        // Copy the serialization and subcollection info.
6268bf80f4bSopenharmony_ci        auto subCollectionIndex = srcCollection.GetSubCollectionIndexByRoot(srcEntity);
6278bf80f4bSopenharmony_ci        if (subCollectionIndex >= 0) {
6288bf80f4bSopenharmony_ci            auto* srcSubCollection = srcCollection.GetSubCollection(subCollectionIndex);
6298bf80f4bSopenharmony_ci            auto& dstSubCollection =
6308bf80f4bSopenharmony_ci                dstCollection.AddSubCollection(srcSubCollection->GetUri(), srcSubCollection->GetContextUri());
6318bf80f4bSopenharmony_ci            dstSubCollection.SetSrc(srcSubCollection->GetSrc());
6328bf80f4bSopenharmony_ci
6338bf80f4bSopenharmony_ci            // Recursively copy sub collection.
6348bf80f4bSopenharmony_ci            CloneEntitiesFromCollection(*srcSubCollection, dstSubCollection, srcSerializationCollection,
6358bf80f4bSopenharmony_ci                dstSerializationCollection, srcSubCollection->GetEntities(), oldToNew, clonedOut);
6368bf80f4bSopenharmony_ci
6378bf80f4bSopenharmony_ci            // Need to find the collection roots manually.
6388bf80f4bSopenharmony_ci            // TODO: a bit messy that the colelctions are iterated separately.
6398bf80f4bSopenharmony_ci            // TODO: no easy way to get the root entity.
6408bf80f4bSopenharmony_ci            const size_t collectionCount = srcSubCollection->GetSubCollectionCount();
6418bf80f4bSopenharmony_ci            for (size_t j = 0; j < collectionCount; ++j) {
6428bf80f4bSopenharmony_ci                auto* sc = srcSubCollection->GetSubCollection(j);
6438bf80f4bSopenharmony_ci                auto root = sc->GetEntity("/");
6448bf80f4bSopenharmony_ci                if (root != Entity {}) {
6458bf80f4bSopenharmony_ci                    CloneEntitiesFromCollection(*srcSubCollection, dstSubCollection, srcSerializationCollection,
6468bf80f4bSopenharmony_ci                        dstSerializationCollection, { &root, 1 }, oldToNew, clonedOut);
6478bf80f4bSopenharmony_ci                }
6488bf80f4bSopenharmony_ci            }
6498bf80f4bSopenharmony_ci
6508bf80f4bSopenharmony_ci        } else if (!srcCollection.IsExternal(srcEntity)) {
6518bf80f4bSopenharmony_ci            auto dstEntity = CloneEntityReference(srcCollection.GetEcs(), srcEntity, dstCollection.GetEcs());
6528bf80f4bSopenharmony_ci
6538bf80f4bSopenharmony_ci            clonedOut.emplace_back(dstEntity);
6548bf80f4bSopenharmony_ci            oldToNew[srcEntity] = dstEntity;
6558bf80f4bSopenharmony_ci
6568bf80f4bSopenharmony_ci            dstCollection.AddEntity(dstEntity);
6578bf80f4bSopenharmony_ci            auto id = srcCollection.GetId(srcEntity);
6588bf80f4bSopenharmony_ci            if (!id.empty()) {
6598bf80f4bSopenharmony_ci                dstCollection.SetId(id, dstEntity);
6608bf80f4bSopenharmony_ci            }
6618bf80f4bSopenharmony_ci
6628bf80f4bSopenharmony_ci            // Note that the root collections contain all the serialization info.
6638bf80f4bSopenharmony_ci            for (auto& cm : srcCollection.GetEcs().GetComponentManagers()) {
6648bf80f4bSopenharmony_ci                auto* properties = srcSerializationCollection.GetSerializedProperties(srcEntity, cm->GetUid());
6658bf80f4bSopenharmony_ci                if (properties) {
6668bf80f4bSopenharmony_ci                    dstSerializationCollection.MarkComponentSerialized(dstEntity, cm->GetUid(), true);
6678bf80f4bSopenharmony_ci                    for (auto& property : *properties) {
6688bf80f4bSopenharmony_ci                        dstSerializationCollection.MarkPropertySerialized(dstEntity, cm->GetUid(), property, true);
6698bf80f4bSopenharmony_ci                    }
6708bf80f4bSopenharmony_ci                }
6718bf80f4bSopenharmony_ci            }
6728bf80f4bSopenharmony_ci        }
6738bf80f4bSopenharmony_ci    }
6748bf80f4bSopenharmony_ci}
6758bf80f4bSopenharmony_ci} // namespace
6768bf80f4bSopenharmony_ci
6778bf80f4bSopenharmony_civector<EntityReference> EntityCollection::CopyContentsWithSerialization(
6788bf80f4bSopenharmony_ci    IEntityCollection& srcCollection, array_view<const EntityReference> entities)
6798bf80f4bSopenharmony_ci{
6808bf80f4bSopenharmony_ci    unordered_map<Entity, Entity> oldToNew;
6818bf80f4bSopenharmony_ci
6828bf80f4bSopenharmony_ci    vector<EntityReference> entitiesOut;
6838bf80f4bSopenharmony_ci    entitiesOut.reserve(entities.size());
6848bf80f4bSopenharmony_ci    CloneEntitiesFromCollection(srcCollection, *this, srcCollection, *this, entities, oldToNew, entitiesOut);
6858bf80f4bSopenharmony_ci
6868bf80f4bSopenharmony_ci    for (auto& entity : entitiesOut) {
6878bf80f4bSopenharmony_ci        RewriteEntityReferences(GetEcs(), entity, oldToNew);
6888bf80f4bSopenharmony_ci    }
6898bf80f4bSopenharmony_ci    return entitiesOut;
6908bf80f4bSopenharmony_ci}
6918bf80f4bSopenharmony_ci
6928bf80f4bSopenharmony_civector<EntityReference> EntityCollection::CopyContentsWithSerialization(IEntityCollection& srcCollection)
6938bf80f4bSopenharmony_ci{
6948bf80f4bSopenharmony_ci    vector<EntityReference> entitiesIn;
6958bf80f4bSopenharmony_ci    srcCollection.GetEntitiesRecursive(false, entitiesIn);
6968bf80f4bSopenharmony_ci    return CopyContentsWithSerialization(srcCollection, entitiesIn);
6978bf80f4bSopenharmony_ci}
6988bf80f4bSopenharmony_ci
6998bf80f4bSopenharmony_civoid EntityCollection::ClonePrivate(EntityCollection& dst) const
7008bf80f4bSopenharmony_ci{
7018bf80f4bSopenharmony_ci    // Clone all collections recursively.
7028bf80f4bSopenharmony_ci    DoCloneRecursive(dst);
7038bf80f4bSopenharmony_ci
7048bf80f4bSopenharmony_ci    //
7058bf80f4bSopenharmony_ci    // Remap entity properties that are pointing to the src entities to point to cloned ones.
7068bf80f4bSopenharmony_ci    //
7078bf80f4bSopenharmony_ci    unordered_map<Entity, Entity> oldToNew;
7088bf80f4bSopenharmony_ci
7098bf80f4bSopenharmony_ci    vector<EntityReference> sourceEntities;
7108bf80f4bSopenharmony_ci    GetEntitiesRecursive(false, sourceEntities);
7118bf80f4bSopenharmony_ci    vector<EntityReference> clonedEntities;
7128bf80f4bSopenharmony_ci    dst.GetEntitiesRecursive(false, clonedEntities);
7138bf80f4bSopenharmony_ci
7148bf80f4bSopenharmony_ci    // NOTE: Assuming the order in GetEntitiesRecursive is consistent.
7158bf80f4bSopenharmony_ci    BASE_ASSERT(sourceEntities.size() == clonedEntities.size());
7168bf80f4bSopenharmony_ci    const auto entityCount = sourceEntities.size();
7178bf80f4bSopenharmony_ci    for (size_t i = 0; i < entityCount; ++i) {
7188bf80f4bSopenharmony_ci        oldToNew[sourceEntities[i]] = clonedEntities[i];
7198bf80f4bSopenharmony_ci    }
7208bf80f4bSopenharmony_ci    for (auto& entity : clonedEntities) {
7218bf80f4bSopenharmony_ci        RewriteEntityReferences(dst.GetEcs(), entity, oldToNew);
7228bf80f4bSopenharmony_ci    }
7238bf80f4bSopenharmony_ci}
7248bf80f4bSopenharmony_ci
7258bf80f4bSopenharmony_civoid EntityCollection::DoCloneRecursive(EntityCollection& dst) const
7268bf80f4bSopenharmony_ci{
7278bf80f4bSopenharmony_ci    // Clone entities.
7288bf80f4bSopenharmony_ci    dst.entities_ = CloneEntityReferences(ecs_, { entities_.data(), entities_.size() }, dst.GetEcs());
7298bf80f4bSopenharmony_ci
7308bf80f4bSopenharmony_ci    // Create id mapping but reference cloned entities instead of the original
7318bf80f4bSopenharmony_ci    BASE_ASSERT(entities_.size() == dst.entities_.size());
7328bf80f4bSopenharmony_ci    dst.namedEntities_.clear();
7338bf80f4bSopenharmony_ci    dst.namedEntities_.reserve(namedEntities_.size());
7348bf80f4bSopenharmony_ci    const auto entityCount = entities_.size();
7358bf80f4bSopenharmony_ci    for (const auto& it : namedEntities_) {
7368bf80f4bSopenharmony_ci        for (size_t i = 0; i < entityCount; ++i) {
7378bf80f4bSopenharmony_ci            if (it.second == entities_[i]) {
7388bf80f4bSopenharmony_ci                dst.SetId(it.first, dst.entities_[i]);
7398bf80f4bSopenharmony_ci                break;
7408bf80f4bSopenharmony_ci            }
7418bf80f4bSopenharmony_ci        }
7428bf80f4bSopenharmony_ci    }
7438bf80f4bSopenharmony_ci
7448bf80f4bSopenharmony_ci    // Recurse.
7458bf80f4bSopenharmony_ci    dst.collections_.reserve(collections_.size());
7468bf80f4bSopenharmony_ci    for (auto& collection : collections_) {
7478bf80f4bSopenharmony_ci        BASE_ASSERT(collection);
7488bf80f4bSopenharmony_ci
7498bf80f4bSopenharmony_ci        if (!collection->IsMarkedDestroyed()) {
7508bf80f4bSopenharmony_ci            dst.collections_.emplace_back(EntityCollection::Ptr {
7518bf80f4bSopenharmony_ci                new EntityCollection(dst.GetEcs(), collection->GetUri(), collection->GetContextUri()) });
7528bf80f4bSopenharmony_ci            auto& clonedChild = *dst.collections_.back();
7538bf80f4bSopenharmony_ci            collection->DoCloneRecursive(clonedChild);
7548bf80f4bSopenharmony_ci        }
7558bf80f4bSopenharmony_ci    }
7568bf80f4bSopenharmony_ci}
7578bf80f4bSopenharmony_ci
7588bf80f4bSopenharmony_cinamespace {
7598bf80f4bSopenharmony_ci
7608bf80f4bSopenharmony_cibool SetPropertyDefined(EntityCollection::PropertyList& pl, string_view propertyPath)
7618bf80f4bSopenharmony_ci{
7628bf80f4bSopenharmony_ci    for (const auto& prop : pl) {
7638bf80f4bSopenharmony_ci        if (prop == propertyPath) {
7648bf80f4bSopenharmony_ci            // Already marked as a property defined by this node.
7658bf80f4bSopenharmony_ci            return false;
7668bf80f4bSopenharmony_ci        }
7678bf80f4bSopenharmony_ci
7688bf80f4bSopenharmony_ci        // check if the property we are trying to set is a sub-property of an already defined property
7698bf80f4bSopenharmony_ci        auto len1 = prop.length();
7708bf80f4bSopenharmony_ci        auto len2 = propertyPath.length();
7718bf80f4bSopenharmony_ci        if (len2 > len1) {
7728bf80f4bSopenharmony_ci            auto view1 = prop.substr(0, len1);
7738bf80f4bSopenharmony_ci            auto view2 = propertyPath.substr(0, len1);
7748bf80f4bSopenharmony_ci            if (view1 == view2) {
7758bf80f4bSopenharmony_ci                // already defined in a higher level, so no need to define this sub-property
7768bf80f4bSopenharmony_ci                return false;
7778bf80f4bSopenharmony_ci            }
7788bf80f4bSopenharmony_ci        }
7798bf80f4bSopenharmony_ci    }
7808bf80f4bSopenharmony_ci    pl.push_back(string(propertyPath));
7818bf80f4bSopenharmony_ci    return true;
7828bf80f4bSopenharmony_ci}
7838bf80f4bSopenharmony_cibool SetPropertyUndefined(EntityCollection::PropertyList& pl, string_view propertyPath)
7848bf80f4bSopenharmony_ci{
7858bf80f4bSopenharmony_ci    for (size_t i = 0; i < pl.size(); ++i) {
7868bf80f4bSopenharmony_ci        if (pl[i] == propertyPath) {
7878bf80f4bSopenharmony_ci            pl.erase(pl.begin() + i);
7888bf80f4bSopenharmony_ci            return true;
7898bf80f4bSopenharmony_ci        }
7908bf80f4bSopenharmony_ci    }
7918bf80f4bSopenharmony_ci    return false;
7928bf80f4bSopenharmony_ci}
7938bf80f4bSopenharmony_ci
7948bf80f4bSopenharmony_ci} // namespace
7958bf80f4bSopenharmony_ci
7968bf80f4bSopenharmony_cibool EntityCollection::MarkComponentSerialized(Entity entity, Uid component, bool serialize)
7978bf80f4bSopenharmony_ci{
7988bf80f4bSopenharmony_ci    bool changed = false;
7998bf80f4bSopenharmony_ci    const auto entityInfo = serializationInfo_.find(entity);
8008bf80f4bSopenharmony_ci    const bool entityFound = (entityInfo != serializationInfo_.end());
8018bf80f4bSopenharmony_ci    if (serialize) {
8028bf80f4bSopenharmony_ci        if (!entityFound) {
8038bf80f4bSopenharmony_ci            serializationInfo_[entity][component] = {};
8048bf80f4bSopenharmony_ci            changed = true;
8058bf80f4bSopenharmony_ci        } else {
8068bf80f4bSopenharmony_ci            const auto componentInfo = entityInfo->second.find(component);
8078bf80f4bSopenharmony_ci            const bool componentFound = (componentInfo != entityInfo->second.end());
8088bf80f4bSopenharmony_ci            if (!componentFound) {
8098bf80f4bSopenharmony_ci                entityInfo->second[component] = {};
8108bf80f4bSopenharmony_ci                changed = true;
8118bf80f4bSopenharmony_ci            }
8128bf80f4bSopenharmony_ci        }
8138bf80f4bSopenharmony_ci    } else {
8148bf80f4bSopenharmony_ci        if (entityFound) {
8158bf80f4bSopenharmony_ci            entityInfo->second.erase(component);
8168bf80f4bSopenharmony_ci            changed = true;
8178bf80f4bSopenharmony_ci        }
8188bf80f4bSopenharmony_ci    }
8198bf80f4bSopenharmony_ci
8208bf80f4bSopenharmony_ci    if (changed) {
8218bf80f4bSopenharmony_ci        MarkModified(true);
8228bf80f4bSopenharmony_ci    }
8238bf80f4bSopenharmony_ci
8248bf80f4bSopenharmony_ci    return changed;
8258bf80f4bSopenharmony_ci}
8268bf80f4bSopenharmony_ci
8278bf80f4bSopenharmony_cibool EntityCollection::MarkAllPropertiesSerialized(Entity entity, Uid component)
8288bf80f4bSopenharmony_ci{
8298bf80f4bSopenharmony_ci    bool changed = false;
8308bf80f4bSopenharmony_ci
8318bf80f4bSopenharmony_ci    auto cm = GetEcs().GetComponentManager(component);
8328bf80f4bSopenharmony_ci    if (!cm) {
8338bf80f4bSopenharmony_ci        CORE_LOG_W("Set modified: Unrecognized component");
8348bf80f4bSopenharmony_ci        return false;
8358bf80f4bSopenharmony_ci    }
8368bf80f4bSopenharmony_ci
8378bf80f4bSopenharmony_ci    auto info = serializationInfo_.find(entity);
8388bf80f4bSopenharmony_ci    if (info == serializationInfo_.end()) {
8398bf80f4bSopenharmony_ci        serializationInfo_[entity] = {};
8408bf80f4bSopenharmony_ci        info = serializationInfo_.find(entity);
8418bf80f4bSopenharmony_ci        changed = true;
8428bf80f4bSopenharmony_ci    }
8438bf80f4bSopenharmony_ci
8448bf80f4bSopenharmony_ci    const auto& propertyApi = cm->GetPropertyApi();
8458bf80f4bSopenharmony_ci    const auto propertyCount = propertyApi.PropertyCount();
8468bf80f4bSopenharmony_ci    for (size_t i = 0; i < propertyCount; ++i) {
8478bf80f4bSopenharmony_ci        auto* property = propertyApi.MetaData(i);
8488bf80f4bSopenharmony_ci        changed = changed | SetPropertyDefined(info->second[component], property->name);
8498bf80f4bSopenharmony_ci    }
8508bf80f4bSopenharmony_ci
8518bf80f4bSopenharmony_ci    if (changed) {
8528bf80f4bSopenharmony_ci        MarkModified(true);
8538bf80f4bSopenharmony_ci    }
8548bf80f4bSopenharmony_ci
8558bf80f4bSopenharmony_ci    return changed;
8568bf80f4bSopenharmony_ci}
8578bf80f4bSopenharmony_ci
8588bf80f4bSopenharmony_cibool EntityCollection::MarkPropertySerialized(Entity entity, Uid component, string_view propertyPath, bool serialize)
8598bf80f4bSopenharmony_ci{
8608bf80f4bSopenharmony_ci    bool changed = false;
8618bf80f4bSopenharmony_ci
8628bf80f4bSopenharmony_ci    auto* cm = GetEcs().GetComponentManager(component);
8638bf80f4bSopenharmony_ci    if (cm) {
8648bf80f4bSopenharmony_ci        auto info = serializationInfo_.find(entity);
8658bf80f4bSopenharmony_ci        if (serialize) {
8668bf80f4bSopenharmony_ci            if (info == serializationInfo_.end()) {
8678bf80f4bSopenharmony_ci                serializationInfo_[entity][component] = {};
8688bf80f4bSopenharmony_ci                info = serializationInfo_.find(entity);
8698bf80f4bSopenharmony_ci                changed = true;
8708bf80f4bSopenharmony_ci            }
8718bf80f4bSopenharmony_ci            changed = changed | SetPropertyDefined(info->second[component], propertyPath);
8728bf80f4bSopenharmony_ci        } else {
8738bf80f4bSopenharmony_ci            if (info != serializationInfo_.end()) {
8748bf80f4bSopenharmony_ci                changed = changed | SetPropertyUndefined(info->second[component], propertyPath);
8758bf80f4bSopenharmony_ci            }
8768bf80f4bSopenharmony_ci        }
8778bf80f4bSopenharmony_ci    }
8788bf80f4bSopenharmony_ci
8798bf80f4bSopenharmony_ci    if (changed) {
8808bf80f4bSopenharmony_ci        MarkModified(true);
8818bf80f4bSopenharmony_ci    }
8828bf80f4bSopenharmony_ci
8838bf80f4bSopenharmony_ci    return changed;
8848bf80f4bSopenharmony_ci}
8858bf80f4bSopenharmony_ci
8868bf80f4bSopenharmony_ciconst IEntityCollection::PropertyList* EntityCollection::GetSerializedProperties(Entity entity, Uid component) const
8878bf80f4bSopenharmony_ci{
8888bf80f4bSopenharmony_ci    const auto info = serializationInfo_.find(entity);
8898bf80f4bSopenharmony_ci    if (info != serializationInfo_.end()) {
8908bf80f4bSopenharmony_ci        const auto props = info->second.find(component);
8918bf80f4bSopenharmony_ci        if (props != info->second.end()) {
8928bf80f4bSopenharmony_ci            return &props->second;
8938bf80f4bSopenharmony_ci        }
8948bf80f4bSopenharmony_ci    }
8958bf80f4bSopenharmony_ci    return nullptr;
8968bf80f4bSopenharmony_ci}
8978bf80f4bSopenharmony_ci
8988bf80f4bSopenharmony_cibool EntityCollection::IsPropertySerialized(Entity entity, Uid component, string_view propertyPath)
8998bf80f4bSopenharmony_ci{
9008bf80f4bSopenharmony_ci    auto info = serializationInfo_.find(entity);
9018bf80f4bSopenharmony_ci    if (info == serializationInfo_.end()) {
9028bf80f4bSopenharmony_ci        return false;
9038bf80f4bSopenharmony_ci    } else {
9048bf80f4bSopenharmony_ci        const auto props = info->second.find(component);
9058bf80f4bSopenharmony_ci        if (props == info->second.end()) {
9068bf80f4bSopenharmony_ci            return false;
9078bf80f4bSopenharmony_ci        } else {
9088bf80f4bSopenharmony_ci            for (auto& prop : props->second) {
9098bf80f4bSopenharmony_ci                if (prop == propertyPath) {
9108bf80f4bSopenharmony_ci                    return true;
9118bf80f4bSopenharmony_ci                }
9128bf80f4bSopenharmony_ci            }
9138bf80f4bSopenharmony_ci        }
9148bf80f4bSopenharmony_ci    }
9158bf80f4bSopenharmony_ci    return false;
9168bf80f4bSopenharmony_ci}
9178bf80f4bSopenharmony_ci
9188bf80f4bSopenharmony_civoid EntityCollection::Destroy()
9198bf80f4bSopenharmony_ci{
9208bf80f4bSopenharmony_ci    delete this;
9218bf80f4bSopenharmony_ci}
9228bf80f4bSopenharmony_ci
9238bf80f4bSopenharmony_ciEntityCollection::~EntityCollection()
9248bf80f4bSopenharmony_ci{
9258bf80f4bSopenharmony_ci    Clear();
9268bf80f4bSopenharmony_ci}
9278bf80f4bSopenharmony_ci
9288bf80f4bSopenharmony_civoid EntityCollection::ModifiedChanged(IEntityCollection& entityCollection, bool modified)
9298bf80f4bSopenharmony_ci{
9308bf80f4bSopenharmony_ci    // subcollection changed, propagate modified status
9318bf80f4bSopenharmony_ci    if (modified) {
9328bf80f4bSopenharmony_ci        MarkModified(modified);
9338bf80f4bSopenharmony_ci    }
9348bf80f4bSopenharmony_ci}
9358bf80f4bSopenharmony_ci
9368bf80f4bSopenharmony_ciIEntityCollection::Ptr EntityCollection::CreateNewEntityCollection(string_view uri, string_view contextUri)
9378bf80f4bSopenharmony_ci{
9388bf80f4bSopenharmony_ci    return EntityCollection::Ptr { new EntityCollection(ecs_, uri, contextUri) };
9398bf80f4bSopenharmony_ci}
9408bf80f4bSopenharmony_ci
9418bf80f4bSopenharmony_ciSCENE_END_NAMESPACE()
942