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#ifndef SCENE_PLUGIN_ECSUTIL_H
178bf80f4bSopenharmony_ci#define SCENE_PLUGIN_ECSUTIL_H
188bf80f4bSopenharmony_ci
198bf80f4bSopenharmony_ci#include <PropertyTools/property_data.h>
208bf80f4bSopenharmony_ci
218bf80f4bSopenharmony_ci#include <base/containers/string.h>
228bf80f4bSopenharmony_ci#include <base/containers/unordered_map.h>
238bf80f4bSopenharmony_ci#include <base/math/vector.h>
248bf80f4bSopenharmony_ci#include <core/ecs/entity.h>
258bf80f4bSopenharmony_ci#include <core/ecs/intf_component_manager.h>
268bf80f4bSopenharmony_ci#include <core/ecs/intf_ecs.h>
278bf80f4bSopenharmony_ci#include <core/ecs/intf_entity_manager.h>
288bf80f4bSopenharmony_ci#include <core/property/intf_property_api.h>
298bf80f4bSopenharmony_ci#include <core/property/property_types.h>
308bf80f4bSopenharmony_ci#include <core/property/scoped_handle.h>
318bf80f4bSopenharmony_ci
328bf80f4bSopenharmony_ciCORE_BEGIN_NAMESPACE()
338bf80f4bSopenharmony_ci
348bf80f4bSopenharmony_ciinline void CloneComponent(CORE_NS::Entity srcEntity, const CORE_NS::IComponentManager& srcManager,
358bf80f4bSopenharmony_ci    CORE_NS::IEcs& dstEcs, CORE_NS::Entity dstEntity)
368bf80f4bSopenharmony_ci{
378bf80f4bSopenharmony_ci    auto* dstManager = dstEcs.GetComponentManager(srcManager.GetUid());
388bf80f4bSopenharmony_ci    if (dstManager) {
398bf80f4bSopenharmony_ci        // Get copy destiantion property handle.
408bf80f4bSopenharmony_ci        auto componentId = dstManager->GetComponentId(dstEntity);
418bf80f4bSopenharmony_ci        if (componentId == CORE_NS::IComponentManager::INVALID_COMPONENT_ID) {
428bf80f4bSopenharmony_ci            dstManager->Create(dstEntity);
438bf80f4bSopenharmony_ci            componentId = dstManager->GetComponentId(dstEntity);
448bf80f4bSopenharmony_ci        }
458bf80f4bSopenharmony_ci        BASE_ASSERT(componentId != CORE_NS::IComponentManager::INVALID_COMPONENT_ID);
468bf80f4bSopenharmony_ci        const auto* srcHandle = srcManager.GetData(srcEntity);
478bf80f4bSopenharmony_ci        if (srcHandle) {
488bf80f4bSopenharmony_ci            dstManager->SetData(dstEntity, *srcHandle);
498bf80f4bSopenharmony_ci        }
508bf80f4bSopenharmony_ci    }
518bf80f4bSopenharmony_ci}
528bf80f4bSopenharmony_ci
538bf80f4bSopenharmony_ciinline void CloneComponents(
548bf80f4bSopenharmony_ci    CORE_NS::IEcs& srcEcs, CORE_NS::Entity srcEntity, CORE_NS::IEcs& dstEcs, CORE_NS::Entity dstEntity)
558bf80f4bSopenharmony_ci{
568bf80f4bSopenharmony_ci    BASE_NS::vector<CORE_NS::IComponentManager*> managers;
578bf80f4bSopenharmony_ci    srcEcs.GetComponents(srcEntity, managers);
588bf80f4bSopenharmony_ci    for (auto* srcManager : managers) {
598bf80f4bSopenharmony_ci        CloneComponent(srcEntity, *srcManager, dstEcs, dstEntity);
608bf80f4bSopenharmony_ci    }
618bf80f4bSopenharmony_ci}
628bf80f4bSopenharmony_ci
638bf80f4bSopenharmony_ciinline CORE_NS::Entity CloneEntity(CORE_NS::IEcs& srcEcs, CORE_NS::Entity src, CORE_NS::IEcs& dstEcs)
648bf80f4bSopenharmony_ci{
658bf80f4bSopenharmony_ci    CORE_NS::Entity dst = dstEcs.GetEntityManager().Create();
668bf80f4bSopenharmony_ci    CloneComponents(srcEcs, src, dstEcs, dst);
678bf80f4bSopenharmony_ci    return dst;
688bf80f4bSopenharmony_ci}
698bf80f4bSopenharmony_ci
708bf80f4bSopenharmony_ciinline CORE_NS::EntityReference CloneEntityReference(CORE_NS::IEcs& srcEcs, CORE_NS::Entity src, CORE_NS::IEcs& dstEcs)
718bf80f4bSopenharmony_ci{
728bf80f4bSopenharmony_ci    CORE_NS::EntityReference dst = dstEcs.GetEntityManager().CreateReferenceCounted();
738bf80f4bSopenharmony_ci    CloneComponents(srcEcs, src, dstEcs, dst);
748bf80f4bSopenharmony_ci    return dst;
758bf80f4bSopenharmony_ci}
768bf80f4bSopenharmony_ci
778bf80f4bSopenharmony_ciinline void GatherEntityReferences(BASE_NS::vector<CORE_NS::Entity*>& entities,
788bf80f4bSopenharmony_ci    BASE_NS::vector<CORE_NS::EntityReference*>& entityReferences, const CORE_NS::Property& property,
798bf80f4bSopenharmony_ci    uintptr_t offset = 0)
808bf80f4bSopenharmony_ci{
818bf80f4bSopenharmony_ci    if (property.type == CORE_NS::PropertyType::ENTITY_T) {
828bf80f4bSopenharmony_ci        entities.emplace_back(reinterpret_cast<CORE_NS::Entity*>(offset));
838bf80f4bSopenharmony_ci    } else if (property.type == CORE_NS::PropertyType::ENTITY_REFERENCE_T) {
848bf80f4bSopenharmony_ci        entityReferences.emplace_back(reinterpret_cast<CORE_NS::EntityReference*>(offset));
858bf80f4bSopenharmony_ci    } else if (property.metaData.containerMethods) {
868bf80f4bSopenharmony_ci        auto& containerProperty = property.metaData.containerMethods->property;
878bf80f4bSopenharmony_ci        if (property.type.isArray) {
888bf80f4bSopenharmony_ci            // Array of properties.
898bf80f4bSopenharmony_ci            for (size_t i = 0; i < property.count; i++) {
908bf80f4bSopenharmony_ci                uintptr_t ptr = offset + i * containerProperty.size;
918bf80f4bSopenharmony_ci                GatherEntityReferences(entities, entityReferences, containerProperty, ptr);
928bf80f4bSopenharmony_ci            }
938bf80f4bSopenharmony_ci        } else {
948bf80f4bSopenharmony_ci            // This is a "non trivial container"
958bf80f4bSopenharmony_ci            // (So it needs to read the data and not just the metadata to figure out the data structure).
968bf80f4bSopenharmony_ci            const auto count = property.metaData.containerMethods->size(offset);
978bf80f4bSopenharmony_ci            for (size_t i = 0; i < count; i++) {
988bf80f4bSopenharmony_ci                uintptr_t ptr = property.metaData.containerMethods->get(offset, i);
998bf80f4bSopenharmony_ci                GatherEntityReferences(entities, entityReferences, containerProperty, ptr);
1008bf80f4bSopenharmony_ci            }
1018bf80f4bSopenharmony_ci        }
1028bf80f4bSopenharmony_ci
1038bf80f4bSopenharmony_ci    } else if (!property.metaData.memberProperties.empty()) {
1048bf80f4bSopenharmony_ci        // Custom type (struct). Process sub properties recursively.
1058bf80f4bSopenharmony_ci        for (size_t i = 0; i < property.count; i++) {
1068bf80f4bSopenharmony_ci            for (const auto& child : property.metaData.memberProperties) {
1078bf80f4bSopenharmony_ci                GatherEntityReferences(entities, entityReferences, child, offset + child.offset);
1088bf80f4bSopenharmony_ci            }
1098bf80f4bSopenharmony_ci            offset += property.size / property.count;
1108bf80f4bSopenharmony_ci        }
1118bf80f4bSopenharmony_ci    }
1128bf80f4bSopenharmony_ci}
1138bf80f4bSopenharmony_ci
1148bf80f4bSopenharmony_ciinline void RewriteEntityReferences(
1158bf80f4bSopenharmony_ci    CORE_NS::IEcs& ecs, CORE_NS::Entity entity, BASE_NS::unordered_map<CORE_NS::Entity, CORE_NS::Entity>& oldToNew)
1168bf80f4bSopenharmony_ci{
1178bf80f4bSopenharmony_ci    // Go through the entity properties and update any entity references to point to the ones pointed by the oldToNew
1188bf80f4bSopenharmony_ci    // map.
1198bf80f4bSopenharmony_ci    auto managers = ecs.GetComponentManagers();
1208bf80f4bSopenharmony_ci    for (auto cm : managers) {
1218bf80f4bSopenharmony_ci        if (auto id = cm->GetComponentId(entity); id != CORE_NS::IComponentManager::INVALID_COMPONENT_ID) {
1228bf80f4bSopenharmony_ci            auto* data = cm->GetData(id);
1238bf80f4bSopenharmony_ci            if (data) {
1248bf80f4bSopenharmony_ci                // Find all entity references from this component.
1258bf80f4bSopenharmony_ci                BASE_NS::vector<CORE_NS::Entity*> entities;
1268bf80f4bSopenharmony_ci                BASE_NS::vector<CORE_NS::EntityReference*> entityRefs;
1278bf80f4bSopenharmony_ci                uintptr_t offset = (uintptr_t)data->RLock();
1288bf80f4bSopenharmony_ci                if (offset) {
1298bf80f4bSopenharmony_ci                    for (const auto& property : data->Owner()->MetaData()) {
1308bf80f4bSopenharmony_ci                        GatherEntityReferences(entities, entityRefs, property, offset + property.offset);
1318bf80f4bSopenharmony_ci                    }
1328bf80f4bSopenharmony_ci
1338bf80f4bSopenharmony_ci                    // Rewrite old entity values with new ones. Assuming that the memory locations are the same as in
1348bf80f4bSopenharmony_ci                    // the RLock. NOTE: Keeping the read access open and we must not change any container sizes.
1358bf80f4bSopenharmony_ci                    if (!entities.empty() || !entityRefs.empty()) {
1368bf80f4bSopenharmony_ci                        data->WLock();
1378bf80f4bSopenharmony_ci                        for (CORE_NS::Entity* entity : entities) {
1388bf80f4bSopenharmony_ci                            if (const auto it = oldToNew.find(*entity); it != oldToNew.end()) {
1398bf80f4bSopenharmony_ci                                *entity = it->second;
1408bf80f4bSopenharmony_ci                            }
1418bf80f4bSopenharmony_ci                        }
1428bf80f4bSopenharmony_ci                        for (CORE_NS::EntityReference* entity : entityRefs) {
1438bf80f4bSopenharmony_ci                            if (const auto it = oldToNew.find(*entity); it != oldToNew.end()) {
1448bf80f4bSopenharmony_ci                                *entity = ecs.GetEntityManager().GetReferenceCounted(it->second);
1458bf80f4bSopenharmony_ci                            }
1468bf80f4bSopenharmony_ci                        }
1478bf80f4bSopenharmony_ci                        data->WUnlock();
1488bf80f4bSopenharmony_ci                    }
1498bf80f4bSopenharmony_ci                }
1508bf80f4bSopenharmony_ci
1518bf80f4bSopenharmony_ci                data->RUnlock();
1528bf80f4bSopenharmony_ci            }
1538bf80f4bSopenharmony_ci        }
1548bf80f4bSopenharmony_ci    }
1558bf80f4bSopenharmony_ci}
1568bf80f4bSopenharmony_ci
1578bf80f4bSopenharmony_ciinline BASE_NS::vector<CORE_NS::Entity> CloneEntities(
1588bf80f4bSopenharmony_ci    CORE_NS::IEcs& srcEcs, BASE_NS::array_view<const CORE_NS::Entity> src, CORE_NS::IEcs& dstEcs)
1598bf80f4bSopenharmony_ci{
1608bf80f4bSopenharmony_ci    BASE_NS::vector<CORE_NS::Entity> clonedEntities;
1618bf80f4bSopenharmony_ci    clonedEntities.reserve(src.size());
1628bf80f4bSopenharmony_ci    for (const auto& srcEntity : src) {
1638bf80f4bSopenharmony_ci        clonedEntities.emplace_back(CloneEntity(srcEcs, srcEntity, dstEcs));
1648bf80f4bSopenharmony_ci    }
1658bf80f4bSopenharmony_ci    return clonedEntities;
1668bf80f4bSopenharmony_ci}
1678bf80f4bSopenharmony_ci
1688bf80f4bSopenharmony_ciinline BASE_NS::vector<CORE_NS::EntityReference> CloneEntityReferences(
1698bf80f4bSopenharmony_ci    CORE_NS::IEcs& srcEcs, BASE_NS::array_view<const CORE_NS::EntityReference> src, CORE_NS::IEcs& dstEcs)
1708bf80f4bSopenharmony_ci{
1718bf80f4bSopenharmony_ci    BASE_NS::vector<CORE_NS::EntityReference> clonedEntities;
1728bf80f4bSopenharmony_ci    clonedEntities.reserve(src.size());
1738bf80f4bSopenharmony_ci    for (const auto& srcEntity : src) {
1748bf80f4bSopenharmony_ci        clonedEntities.emplace_back(CloneEntityReference(srcEcs, srcEntity, dstEcs));
1758bf80f4bSopenharmony_ci    }
1768bf80f4bSopenharmony_ci    return clonedEntities;
1778bf80f4bSopenharmony_ci}
1788bf80f4bSopenharmony_ci
1798bf80f4bSopenharmony_ciinline BASE_NS::vector<CORE_NS::EntityReference> CloneEntitiesUpdateRefs(
1808bf80f4bSopenharmony_ci    CORE_NS::IEcs& srcEcs, BASE_NS::array_view<const CORE_NS::EntityReference> src, CORE_NS::IEcs& dstEcs)
1818bf80f4bSopenharmony_ci{
1828bf80f4bSopenharmony_ci    BASE_NS::unordered_map<CORE_NS::Entity, CORE_NS::Entity> oldToNew;
1838bf80f4bSopenharmony_ci
1848bf80f4bSopenharmony_ci    BASE_NS::vector<CORE_NS::EntityReference> clonedEntities;
1858bf80f4bSopenharmony_ci    clonedEntities.reserve(src.size());
1868bf80f4bSopenharmony_ci    for (const auto& srcEntity : src) {
1878bf80f4bSopenharmony_ci        clonedEntities.emplace_back(CloneEntityReference(srcEcs, srcEntity, dstEcs));
1888bf80f4bSopenharmony_ci        oldToNew[srcEntity] = clonedEntities.back();
1898bf80f4bSopenharmony_ci    }
1908bf80f4bSopenharmony_ci
1918bf80f4bSopenharmony_ci    for (auto& entity : clonedEntities) {
1928bf80f4bSopenharmony_ci        RewriteEntityReferences(dstEcs, entity, oldToNew);
1938bf80f4bSopenharmony_ci    }
1948bf80f4bSopenharmony_ci    return clonedEntities;
1958bf80f4bSopenharmony_ci}
1968bf80f4bSopenharmony_ci
1978bf80f4bSopenharmony_ciinline bool isPropertyContainer(const CORE_NS::Property& property)
1988bf80f4bSopenharmony_ci{
1998bf80f4bSopenharmony_ci    return property.type == PROPERTYTYPE(CORE_NS::IPropertyHandle*);
2008bf80f4bSopenharmony_ci}
2018bf80f4bSopenharmony_ci
2028bf80f4bSopenharmony_ciinline CORE_NS::IPropertyHandle* ResolveContainerProperty(const CORE_NS::IPropertyHandle& handle,
2038bf80f4bSopenharmony_ci    const BASE_NS::string& propertyPath, BASE_NS::string& path, BASE_NS::string& name)
2048bf80f4bSopenharmony_ci{
2058bf80f4bSopenharmony_ci    // Extract property path.
2068bf80f4bSopenharmony_ci    auto separatorPosition = propertyPath.find_first_of('.');
2078bf80f4bSopenharmony_ci    if (separatorPosition == BASE_NS::string::npos) {
2088bf80f4bSopenharmony_ci        return nullptr;
2098bf80f4bSopenharmony_ci    }
2108bf80f4bSopenharmony_ci
2118bf80f4bSopenharmony_ci    path = propertyPath.substr(0, separatorPosition);
2128bf80f4bSopenharmony_ci    name = propertyPath.substr(separatorPosition + 1);
2138bf80f4bSopenharmony_ci
2148bf80f4bSopenharmony_ci    CORE_NS::IPropertyHandle* result = nullptr;
2158bf80f4bSopenharmony_ci
2168bf80f4bSopenharmony_ci    uintptr_t offset = uintptr_t(handle.RLock());
2178bf80f4bSopenharmony_ci
2188bf80f4bSopenharmony_ci    // Get potential container.
2198bf80f4bSopenharmony_ci    auto propertyData = CORE_NS::PropertyData::FindProperty(handle.Owner()->MetaData(), path, offset);
2208bf80f4bSopenharmony_ci    if (propertyData) {
2218bf80f4bSopenharmony_ci        // Ensure it is a container.
2228bf80f4bSopenharmony_ci        if (CORE_NS::isPropertyContainer(*propertyData.property)) {
2238bf80f4bSopenharmony_ci            // Try to flush value to container.
2248bf80f4bSopenharmony_ci            result = *(CORE_NS::IPropertyHandle**)(propertyData.offset);
2258bf80f4bSopenharmony_ci        }
2268bf80f4bSopenharmony_ci    }
2278bf80f4bSopenharmony_ci
2288bf80f4bSopenharmony_ci    handle.RUnlock();
2298bf80f4bSopenharmony_ci
2308bf80f4bSopenharmony_ci    return result;
2318bf80f4bSopenharmony_ci}
2328bf80f4bSopenharmony_ci
2338bf80f4bSopenharmony_ciCORE_END_NAMESPACE()
2348bf80f4bSopenharmony_ci
2358bf80f4bSopenharmony_ci#endif
236