1/*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "component_query.h"
17
18#include <base/containers/array_view.h>
19#include <base/containers/iterator.h>
20#include <base/containers/type_traits.h>
21#include <base/containers/unordered_map.h>
22#include <base/containers/vector.h>
23#include <base/namespace.h>
24#include <core/ecs/entity.h>
25#include <core/ecs/intf_component_manager.h>
26#include <core/ecs/intf_ecs.h>
27#include <core/ecs/intf_entity_manager.h>
28#include <core/log.h>
29#include <core/namespace.h>
30
31CORE_BEGIN_NAMESPACE()
32using BASE_NS::array_view;
33using BASE_NS::move;
34
35ComponentQuery::~ComponentQuery()
36{
37    UnregisterEcsListeners();
38}
39
40bool ComponentQuery::ResultRow::IsValidComponentId(size_t index) const
41{
42    if (index < components.size()) {
43        return components[index] != IComponentManager::INVALID_COMPONENT_ID;
44    }
45    return false;
46}
47
48void ComponentQuery::SetupQuery(
49    const IComponentManager& baseComponentSet, const array_view<const Operation> operations, bool enableEntityLookup)
50{
51    const size_t componentCount = operations.size() + 1;
52    enableLookup_ = enableEntityLookup;
53
54    result_.clear();
55    valid_ = false;
56
57    // Unregistering any old listeners because the operations might not use the same managers.
58    UnregisterEcsListeners();
59
60    managers_.clear();
61    managers_.reserve(componentCount);
62    operationMethods_.clear();
63    operationMethods_.reserve(componentCount);
64
65    managers_.push_back(const_cast<IComponentManager*>(&baseComponentSet));
66    operationMethods_.push_back(Operation::REQUIRE);
67    for (auto& operation : operations) {
68        managers_.push_back(const_cast<IComponentManager*>(&operation.target));
69        CORE_ASSERT(managers_.back());
70        operationMethods_.push_back(operation.method);
71    }
72
73    if (enableListeners_) {
74        RegisterEcsListeners();
75    }
76}
77
78bool ComponentQuery::Execute()
79{
80    if (enableListeners_ && valid_) {
81        // No changes detected since previous execute.
82        return false;
83    }
84    if (managers_.empty()) {
85        // Query setup not done.
86        return false;
87    }
88    if (!managers_[0]) {
89        // Base manager is null.
90        return false;
91    }
92
93    const IComponentManager& baseComponentSet = *managers_[0];
94
95    const auto baseComponents = baseComponentSet.GetComponentCount();
96    result_.resize(baseComponents);
97
98    auto& em = baseComponentSet.GetEcs().GetEntityManager();
99    const size_t managerCount = managers_.size();
100    size_t index = 0U;
101    for (IComponentManager::ComponentId id = 0; id < baseComponents; ++id) {
102        if (const Entity entity = baseComponentSet.GetEntity(id); em.IsAlive(entity)) {
103            auto& row = result_[index];
104            row.entity = entity;
105            row.components.resize(managerCount, IComponentManager::INVALID_COMPONENT_ID);
106            row.components[0U] = id;
107
108            bool valid = true;
109
110            // NOTE: starting from index 1 that is the first manager after the base component set.
111            for (size_t i = 1; valid && (i < managerCount); ++i) {
112                const auto& manager = managers_[i];
113                const auto componentId =
114                    manager ? manager->GetComponentId(entity) : IComponentManager::INVALID_COMPONENT_ID;
115                row.components[i] = componentId;
116
117                switch (operationMethods_[i]) {
118                    case Operation::REQUIRE: {
119                        // for required components ID must be valid
120                        valid = (componentId != IComponentManager::INVALID_COMPONENT_ID);
121                        break;
122                    }
123
124                    case Operation::OPTIONAL: {
125                        // for optional ID doesn't matter
126                        break;
127                    }
128
129                    default: {
130                        valid = false;
131                    }
132                }
133            }
134
135            if (valid) {
136                if (enableLookup_) {
137                    mapping_[entity] = index;
138                }
139                ++index;
140            }
141        }
142    }
143    result_.resize(index);
144
145    valid_ = true;
146    return true;
147}
148
149void ComponentQuery::Execute(
150    const IComponentManager& baseComponentSet, const array_view<const Operation> operations, bool enableEntityLookup)
151{
152    SetupQuery(baseComponentSet, operations, enableEntityLookup);
153    Execute();
154}
155
156void ComponentQuery::SetEcsListenersEnabled(bool enableListeners)
157{
158    enableListeners_ = enableListeners;
159    if (enableListeners_) {
160        RegisterEcsListeners();
161    } else {
162        UnregisterEcsListeners();
163    }
164}
165
166bool ComponentQuery::IsValid() const
167{
168    return valid_;
169}
170
171array_view<const ComponentQuery::ResultRow> ComponentQuery::GetResults() const
172{
173    return { result_.data(), result_.size() };
174}
175
176const ComponentQuery::ResultRow* ComponentQuery::FindResultRow(Entity entity) const
177{
178    if (EntityUtil::IsValid(entity)) {
179        const auto it = mapping_.find(entity);
180        if (it != mapping_.end() && it->second < result_.size()) {
181            return &(result_[it->second]);
182        }
183    }
184
185    return nullptr;
186}
187
188void ComponentQuery::RegisterEcsListeners()
189{
190    if (!registered_ && !managers_.empty()) {
191        // Listen to changes in managers so the result can be automatically invalidated.
192        ecs_ = &managers_[0]->GetEcs();
193        for (auto& manager : managers_) {
194            if (manager) {
195                ecs_->AddListener(*manager, *this);
196            }
197        }
198        ecs_->AddListener(static_cast<IEcs::EntityListener&>(*this));
199        registered_ = true;
200    }
201}
202
203void ComponentQuery::UnregisterEcsListeners()
204{
205    if (registered_ && !managers_.empty() && ecs_) {
206        for (auto& manager : managers_) {
207            if (manager) {
208                ecs_->RemoveListener(*manager, *this);
209            }
210        }
211        ecs_->RemoveListener(static_cast<IEcs::EntityListener&>(*this));
212        registered_ = false;
213    }
214}
215
216void ComponentQuery::OnEntityEvent(const IEcs::EntityListener::EventType type, const array_view<const Entity> entities)
217{
218    if (!valid_) {
219        // Listener is only used to invalidate the quety. If the query is already invalid -> no need to check anything.
220        return;
221    }
222    if (type == IEcs::EntityListener::EventType::ACTIVATED || type == IEcs::EntityListener::EventType::DEACTIVATED) {
223        const auto managerCount = managers_.size();
224        for (const auto& entity : entities) {
225            // We are only interested in entities that have all the required managers.
226            bool isRelevantEntity = true;
227            for (size_t i = 0; i < managerCount; ++i) {
228                if (operationMethods_[i] == Operation::OPTIONAL) {
229                    continue;
230                }
231                if (managers_[i] && !managers_[i]->HasComponent(entity)) {
232                    // This entity is missing a required manager -> irrelevant.
233                    isRelevantEntity = false;
234                    break;
235                }
236            }
237
238            if (isRelevantEntity) {
239                // Marking this query as invalid. No need to iterate entities further.
240                valid_ = false;
241                return;
242            }
243        }
244    } else if (type == IEcs::EntityListener::EventType::DESTROYED) {
245        if (enableLookup_) {
246            for (const auto& entity : entities) {
247                mapping_.erase(entity);
248            }
249        }
250    }
251}
252
253void ComponentQuery::OnComponentEvent(const IEcs::ComponentListener::EventType type,
254    const IComponentManager& /* componentManager */, const array_view<const Entity> /* entities */)
255{
256    // We only get events from relevant managers. If they have new or deleted components, the query is no longer valid.
257    if (type == IEcs::ComponentListener::EventType::CREATED || type == IEcs::ComponentListener::EventType::DESTROYED) {
258        valid_ = false;
259    }
260}
261CORE_END_NAMESPACE()
262