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 <algorithm>
17#include <scene_plugin/api/material_uid.h>
18#include <scene_plugin/interface/compatibility.h>
19#include <scene_plugin/interface/intf_bitmap.h>
20
21#include <scene_plugin/interface/intf_scene.h>
22#include <meta/api/engine/util.h>
23#include <meta/api/property/property_event_handler.h>
24
25#include "intf_postprocess_private.h"
26#include "PropertyHandlerArrayHolder.h"
27#include "scene_holder.h"
28
29
30BASE_BEGIN_NAMESPACE()
31
32// our vector does not have op==
33template<typename T>
34bool operator==(const BASE_NS::vector<T>& l, const BASE_NS::vector<T>& r)
35{
36    if (l.size() != r.size()) {
37        return false;
38    }
39    auto it1 = l.begin();
40    auto it2 = r.begin();
41    for (; it1 != l.end(); ++it1, ++it2) {
42        if constexpr (META_NS::HasEqualOperator_v<T>) {
43            if (!(*it1 == *it2)) {
44                return false;
45            }
46        } else {
47            return false;
48        }
49    }
50    return true;
51}
52
53template<typename T>
54bool operator!=(const BASE_NS::vector<T>& l, const BASE_NS::vector<T>& r)
55{
56    return !(l == r);
57}
58
59BASE_END_NAMESPACE()
60
61bool HasChangedProperties(META_NS::IMetadata& meta);
62
63template<class EcsType, class UiType>
64struct StaticConverter {
65    static inline EcsType ToEcs(SceneHolder& sceneHolder, const UiType& v)
66    {
67        return static_cast<EcsType>(v);
68    }
69    static inline UiType ToUi(SceneHolder& sceneHolder, const EcsType& v)
70    {
71        return static_cast<UiType>(v);
72    }
73};
74
75struct ImageConverter {
76    static inline CORE_NS::EntityReference ToEcs(SceneHolder& sceneHolder, const SCENE_NS::IBitmap::Ptr& v)
77    {
78        // Get ecs image handle from bitmap.
79        if (v) {
80	    if (auto rh = v->GetRenderHandle()) {
81                auto uri = META_NS::GetValue(v->Uri());
82		return sceneHolder.LoadImage(uri, rh);
83	    } else {
84		auto uri = META_NS::GetValue(v->Uri());
85		if (!uri.empty()) {
86                    return sceneHolder.LoadImage(uri);
87		}
88            }
89        }
90        return {};
91    }
92
93    static inline SCENE_NS::IBitmap::Ptr ToUi(SceneHolder& sceneHolder, const CORE_NS::EntityReference& v)
94    {
95        CORE_NS::Entity image;
96        BASE_NS::string uri;
97        if (sceneHolder.GetEntityUri(v, uri)) {
98	    RENDER_NS::GpuImageDesc desc;
99	    RENDER_NS::RenderHandleReference rh;
100            auto uriBitmap = META_NS::GetObjectRegistry().Create<SCENE_NS::IBitmap>(SCENE_NS::ClassId::Bitmap);
101                if (sceneHolder.GetImageHandle(v, rh, desc)) {
102                uriBitmap->SetRenderHandle(rh, { desc.width, desc.height });
103	    }
104            META_NS::SetValue(uriBitmap->Uri(), uri);
105            return uriBitmap;
106        }
107
108        return {};
109    }
110};
111
112template<class ObjectType>
113struct EntityConverter {
114    static inline CORE_NS::Entity ToEcs(SceneHolder& sceneHolder, const typename ObjectType::Ptr& v)
115    {
116        // Get ecs object to entity.
117        if (auto ecsObject = interface_cast<SCENE_NS::IEcsObject>(v)) {
118            return ecsObject->GetEntity();
119        }
120        if (auto ippp = interface_cast<IPostProcessPrivate>(v)) {
121            return ippp->GetEntity();
122        }
123        return {};
124    }
125
126    static inline typename ObjectType::Ptr ToUi(SceneHolder& sceneHolder, const CORE_NS::Entity& v)
127    {
128        return {};
129    }
130};
131
132template<class originalType>
133inline auto& GetValueRef(originalType& from, size_t slot)
134{
135    return from[slot];
136}
137
138template<>
139inline auto& GetValueRef<SCENE_NS::Color>(SCENE_NS::Color& from, size_t slot)
140{
141    if (slot == 0) {
142        return from.x;
143    } else if (slot == 1) {
144        return from.y;
145    } else if (slot == 2) { // 2 slot index
146        return from.z;
147    }
148    return from.w;
149}
150
151template<class originalType, class valueType>
152inline bool SetValueFromComponent(META_NS::Property<valueType> to,
153    const META_NS::Property<originalType>& from, size_t slot, bool setAsDefault = false)
154{
155    originalType fromValue = from->GetValue();
156
157    if (to->GetValue() != fromValue[slot]) {
158        if (setAsDefault) {
159            to->SetDefaultValue(fromValue[slot], true);
160        } else {
161            to->SetValue(fromValue[slot]);
162        }
163        return true;
164    }
165    return false;
166}
167
168template<>
169inline bool SetValueFromComponent<SCENE_NS::Color, float>(META_NS::Property<float> to,
170    const META_NS::Property<SCENE_NS::Color>& from, size_t slot, bool setAsDefault)
171{
172    SCENE_NS::Color fromValue = from->GetValue();
173
174    if (slot == 0) {
175        if (to->GetValue() != fromValue.x) {
176            if (setAsDefault) {
177                to->SetDefaultValue(fromValue.x, true);
178            } else {
179                to->SetValue(fromValue.x);
180            }
181        }
182    } else if (slot == 1) {
183        if (to->GetValue() != fromValue.y) {
184            if (setAsDefault) {
185                to->SetDefaultValue(fromValue.y, true);
186            } else {
187                to->SetValue(fromValue.y);
188            }
189        }
190    } else if (slot == 2) {
191        if (to->GetValue() != fromValue.z) {
192            if (setAsDefault) {
193                to->SetDefaultValue(fromValue.z, true);
194            } else {
195                to->SetValue(fromValue.z);
196            }
197        }
198    } else if (slot == 3) {
199        if (to->GetValue() != fromValue.w) {
200            if (setAsDefault) {
201                to->SetDefaultValue(fromValue.w, true);
202            } else {
203                to->SetValue(fromValue.w);
204            }
205        }
206    } else {
207        return false;
208    }
209    return true;
210}
211
212// Bending the implementation a bit, implicit conversion from TimeSpan to Seconds in float
213template<>
214inline bool SetValueFromComponent<float, META_NS::TimeSpan>(META_NS::Property<META_NS::TimeSpan> to,
215    const META_NS::Property<float>& from, size_t /*slot*/, bool setAsDefault)
216{
217    META_NS::TimeSpan value;
218    value.SetSeconds(from->GetValue());
219    if (setAsDefault) {
220        to->SetDefaultValue(value, true);
221    } else {
222        to->SetValue(value);
223    }
224    return true;
225}
226
227template<class originalType, class valueType>
228inline bool SetValueToComponent(META_NS::Property<originalType> to,
229    const META_NS::Property<valueType>& from, size_t slot)
230{
231    valueType fromValue = from->GetValue();
232    originalType toValue = to->GetValue();
233
234    if (toValue[slot] != fromValue) {
235        toValue[slot] = fromValue;
236        to->SetValue(toValue);
237        return true;
238    }
239    return false;
240}
241
242// Bending the implementation a bit, implicit conversion from TimeSpan to Seconds in float
243template<>
244inline bool SetValueToComponent<float, META_NS::TimeSpan>(META_NS::Property<float> to,
245    const META_NS::Property<META_NS::TimeSpan>& from, size_t /*slot*/)
246{
247    to->SetValue(from->GetValue().ToSecondsFloat());
248    return true;
249}
250
251template<>
252inline bool SetValueToComponent<SCENE_NS::Color, float>(META_NS::Property<SCENE_NS::Color> to,
253    const META_NS::Property<float>& from, size_t slot)
254{
255    float fromValue = from->GetValue();
256    SCENE_NS::Color toValue = to->GetValue();
257
258    if (slot == 0) {
259        if (toValue.x != fromValue) {
260            toValue.x = fromValue;
261        }
262    } else if (slot == 1) {
263        if (toValue.y != fromValue) {
264            toValue.y = fromValue;
265        }
266    } else if (slot == 2) { // 2 slot index
267        if (toValue.z != fromValue) {
268            toValue.z = fromValue;
269        }
270    } else if (slot == 3) { // 3 slot index
271        if (toValue.w != fromValue) {
272            toValue.w = fromValue;
273        }
274    } else {
275        return false;
276    }
277
278    to->SetValue(toValue);
279    return true;
280}
281
282enum PropertyBindingType { ONE_WAY_TO_ECS, ONE_WAY_TO_UI, TWO_WAY };
283
284template<class valueType, class sourceType, class Conv = StaticConverter<sourceType, valueType>>
285inline bool BindPropChanges(PropertyHandlerArrayHolder& handler,
286    META_NS::Property<valueType> propertyInstance,
287    META_NS::Property<sourceType> prop, PropertyBindingType type = TWO_WAY)
288{
289    if (!prop || !propertyInstance) {
290        return false;
291    }
292
293    auto sceneHolder = handler.GetSceneHolder();
294    if (!sceneHolder) {
295        return false;
296    }
297
298    bool useEcsPropertyValue = handler.GetUseEcsDefaults();
299    if (propertyInstance->IsValueSet()) {
300        useEcsPropertyValue = false;
301    }
302
303    // If we don't have local value, initialize value from ECS>
304    if (useEcsPropertyValue) {
305        auto ecsValue = Conv::ToUi(*sceneHolder, prop->GetValue());
306        // try to keep our value intact if the engine default value is the same as ours, otherwise we should
307        // update our default
308        if (ecsValue != propertyInstance->GetValue()) {
309            // Set to default value.
310            propertyInstance->SetDefaultValue(ecsValue, true);
311        }
312    } else { // otherwise reflect our value to ecs directly
313        prop->SetValue(Conv::ToEcs(*sceneHolder, propertyInstance->GetValue()));
314    }
315    if (type == ONE_WAY_TO_ECS || type == TWO_WAY) {
316        // and reflect the changes from us to ecs from now on
317        handler.NewHandler(prop, propertyInstance)
318            .Subscribe(propertyInstance, META_NS::MakeCallback<META_NS::IOnChanged>(
319                                             [propertyInstance, sh = sceneHolder](const auto& prop) {
320                                                if (prop) {
321                                                    prop->SetValue(Conv::ToEcs(*sh, propertyInstance->GetValue()));
322                                                    //hack
323                                                    /*if (auto v = META_NS::GetEngineValueFromProperty(prop.GetProperty())) {
324                                                        v->Sync(META_NS::EngineSyncDirection::TO_ENGINE);
325                                                    }*/
326                                                }
327                                             },
328                                             prop));
329    }
330
331    if (type == ONE_WAY_TO_UI || type == TWO_WAY) {
332        //  EcsObject reports changes back only if they take place on ecs side, so this should not cause race
333        //  They anyway don't support bindings properly, so having the feedback channel this way
334        handler.NewHandler(nullptr, nullptr)
335            .Subscribe(prop, META_NS::MakeCallback<META_NS::IOnChanged>(
336                                 [propertyInstance, sh = sceneHolder](const auto& prop) {
337                                    if (prop) {
338                                        if (!propertyInstance->GetBind()) {
339                                            propertyInstance->SetValue(Conv::ToUi(*sh, prop->GetValue()));
340                                        }
341                                    }
342                                },
343                                prop));
344    }
345
346    return true;
347}
348
349template<class valueType, class sourceType, class Conv = StaticConverter<BASE_NS::vector<sourceType>, BASE_NS::vector<valueType>>>
350inline bool BindPropChanges(PropertyHandlerArrayHolder& handler, META_NS::ArrayProperty<valueType> propertyInstance,
351    META_NS::ArrayProperty<sourceType> prop, PropertyBindingType type = TWO_WAY)
352{
353    if (!prop || !propertyInstance) {
354        return false;
355    }
356
357    auto sceneHolder = handler.GetSceneHolder();
358    if (!sceneHolder) {
359        return false;
360    }
361
362    bool useEcsPropertyValue = handler.GetUseEcsDefaults();
363    if (propertyInstance->IsValueSet()) {
364        useEcsPropertyValue = false;
365    }
366
367    // If we don't have local value, initialize value from ECS>
368    if (useEcsPropertyValue) {
369        auto ecsValue = Conv::ToUi(*sceneHolder, prop->GetValue());
370        // try to keep our value intact if the engine default value is the same as ours, otherwise we should
371        // update our default
372        if (ecsValue != propertyInstance->GetValue()) {
373            // Set to default value.
374            propertyInstance->SetDefaultValue(ecsValue, true);
375        }
376    } else { // otherwise reflect our value to ecs directly
377        prop->SetValue(Conv::ToEcs(*sceneHolder, propertyInstance->GetValue()));
378    }
379    if (type == ONE_WAY_TO_ECS || type == TWO_WAY) {
380        // and reflect the changes from us to ecs from now on
381        handler.NewHandler(prop, propertyInstance)
382            .Subscribe(
383                propertyInstance, META_NS::MakeCallback<META_NS::IOnChanged>(
384                                      [propertyInstance, sh = sceneHolder](const auto& prop) {
385                                          if (prop) {
386                                              prop->SetValue(Conv::ToEcs(*sh, propertyInstance->GetValue()));
387                                              // hack
388                                          }
389                                      },
390                                      prop));
391    }
392
393    if (type == ONE_WAY_TO_UI || type == TWO_WAY) {
394        //  EcsObject reports changes back only if they take place on ecs side, so this should not cause race
395        //  They anyway don't support bindings properly, so having the feedback channel this way
396        handler.NewHandler(nullptr, nullptr)
397            .Subscribe(prop, META_NS::MakeCallback<META_NS::IOnChanged>(
398                                 [propertyInstance, sh = sceneHolder](const auto& prop) {
399                                     if (prop) {
400                                         if (!propertyInstance->GetBind()) {
401                                             propertyInstance->SetValue(Conv::ToUi(*sh, prop->GetValue()));
402                                         }
403                                     }
404                                 },
405                                 prop));
406    }
407
408    return true;
409}
410
411template<class valueType>
412inline bool BindChanges(PropertyHandlerArrayHolder& handler,
413    META_NS::Property<valueType> propertyInstance, META_NS::IMetadata::Ptr meta,
414    const BASE_NS::string_view name)
415{
416    if (!meta || !propertyInstance) {
417        return false;
418    }
419
420    if (auto prop = meta->GetPropertyByName<valueType>(name)) {
421        // check if the target is already a proxy
422        if (auto target = handler.GetTarget(prop)) {
423            // this could go wrong if the target and proxy are different, mapped types
424            if (META_NS::Property<valueType> typed { target }) {
425                prop = typed;
426            } else {
427                CORE_LOG_W("%s: could not match the types for %s", __func__, BASE_NS::string(name).c_str());
428            }
429        }
430        return BindPropChanges<valueType>(handler, propertyInstance, prop);
431    }
432
433    SCENE_PLUGIN_VERBOSE_LOG("%s: could not find '%s'", __func__, name.data());
434    return false;
435}
436
437template<class valueType>
438inline bool BindChanges(PropertyHandlerArrayHolder& handler, META_NS::ArrayProperty<valueType> propertyInstance,
439    META_NS::IMetadata::Ptr meta, const BASE_NS::string_view name)
440{
441    if (!meta || !propertyInstance) {
442        return false;
443    }
444
445    if (auto prop = meta->GetArrayPropertyByName<valueType>(name)) {
446        // check if the target is already a proxy
447        if (auto target = handler.GetTarget(prop)) {
448            // this could go wrong if the target and proxy are different, mapped types
449            if (META_NS::ArrayProperty<valueType> typed { target }) {
450                prop = typed;
451            } else {
452                CORE_LOG_W("%s: could not match the types for %s", __func__, BASE_NS::string(name).c_str());
453            }
454        }
455        return BindPropChanges<valueType>(handler, propertyInstance, prop);
456    }
457
458    SCENE_PLUGIN_VERBOSE_LOG("%s: could not find '%s'", __func__, name.data());
459    return false;
460}
461
462template<class originalType, class valueType>
463inline bool setComponentValues(META_NS::Property<originalType> to,
464    const META_NS::Property<valueType>& from, BASE_NS::vector<size_t> slots,
465    bool setAsDefault = false)
466{
467    bool changed { false };
468    valueType fromValue = from->GetValue();
469    originalType toValue = to->GetValue();
470
471    for (size_t ix = 1; ix < slots.size();) {
472        auto slot1 = slots[ix - 1];
473        auto slot2 = slots[ix];
474        auto& value = GetValueRef<originalType>(toValue, slot1);
475        auto& value2 = GetValueRef<valueType>(fromValue, slot2);
476        if (value != value2) {
477            if (value != value && value2 != value2) {
478                CORE_LOG_D("omitting changes on NaN");
479            } else {
480                changed = true;
481                value = value2;
482            }
483        }
484        ix += 2; // 2 index shift
485    }
486    if (changed) {
487        if (setAsDefault) {
488            to->SetDefaultValue(toValue, true);
489        } else {
490            to->SetValue(toValue);
491        }
492    }
493    return changed;
494}
495
496template<class originalType, class valueType>
497inline bool BindSlottedChanges(PropertyHandlerArrayHolder& handler,
498    META_NS::Property<valueType> propertyInstance, META_NS::IMetadata::Ptr meta,
499    const BASE_NS::string_view name, BASE_NS::vector<size_t> slots)
500{
501    if (!meta || !propertyInstance) {
502        return false;
503    }
504
505    if (auto prop = meta->GetPropertyByName<originalType>(name)) {
506        // If we don't have local value, initialize value from ECS>
507        bool useEcsPropertyValue = handler.GetUseEcsDefaults();
508        if (propertyInstance->IsValueSet()) {
509            useEcsPropertyValue = false;
510        }
511
512        // If we don't have local value, initialize value from ECS
513        if (useEcsPropertyValue) {
514            // Set default value.
515            setComponentValues(propertyInstance, prop, slots, true);
516        } else { // otherwise reflect our value to ecs directly
517            setComponentValues(prop, propertyInstance, slots);
518        }
519        // and reflect the changes from us to ecs from now on
520        handler.NewHandler(prop, propertyInstance)
521            .Subscribe(propertyInstance, META_NS::MakeCallback<META_NS::IOnChanged>(
522                                             [propertyInstance, slots](const auto& prop) {
523                                                 if (prop) {
524                                                     setComponentValues(prop, propertyInstance, slots);
525                                                 }
526                                             },
527                                             prop));
528        handler.NewHandler(nullptr, nullptr)
529            .Subscribe(prop, META_NS::MakeCallback<META_NS::IOnChanged>(
530                                 [propertyInstance, slots](const auto& prop) {
531                                     if (prop) {
532                                         setComponentValues(propertyInstance, prop, slots);
533                                     }
534                                 },
535                                 prop));
536
537        return true;
538    }
539    CORE_LOG_W("%s: could not find '%s'", __func__, name.data());
540    return false;
541}
542
543template<class valueType, class sourceType, class Conv = StaticConverter<sourceType, valueType>>
544inline bool ConvertBindChanges(PropertyHandlerArrayHolder& handler,
545    META_NS::Property<valueType> propertyInstance, META_NS::IMetadata::Ptr meta,
546    const BASE_NS::string_view name, PropertyBindingType type = TWO_WAY)
547{
548    if (!meta || !propertyInstance) {
549        return false;
550    }
551
552    if (auto prop = meta->GetPropertyByName<sourceType>(name)) {
553        return BindPropChanges<valueType, sourceType, Conv>(handler, propertyInstance, prop, type);
554    }
555
556    SCENE_PLUGIN_VERBOSE_LOG("%s: could not find '%s'", __func__, name.data());
557    return false;
558}
559
560
561template<class valueType>
562inline bool BindIfCorrectType(
563    PropertyHandlerArrayHolder& handler, META_NS::IProperty::Ptr& clone, META_NS::IProperty::Ptr& prop)
564{
565    if (clone->IsCompatible(META_NS::UidFromType<valueType>())) {
566        auto typed = META_NS::Property<valueType>(clone);
567        BindPropChanges<valueType>(handler, typed, META_NS::Property<valueType>(prop));
568        return true;
569    }
570
571    return false;
572}
573
574template<class originalType, class valueType>
575inline bool BindChanges(PropertyHandlerArrayHolder& handler,
576    META_NS::Property<valueType> propertyInstance, META_NS::IMetadata::Ptr meta,
577    const BASE_NS::string_view name, size_t slot)
578{
579    if (!meta || !propertyInstance) {
580        return false;
581    }
582
583    if (auto prop = meta->GetPropertyByName<originalType>(name)) {
584        // check if the target is already a proxy
585        if (auto target = handler.GetTarget(prop)) {
586            // this could go wrong if the target and proxy are different, mapped types
587            if (auto typed = META_NS::Property<originalType>(target)) {
588                prop = typed;
589            } else {
590                CORE_LOG_W("%s: could not match the types for %s", __func__, BASE_NS::string(name).c_str());
591            }
592        }
593
594        bool useEcsPropertyValue = handler.GetUseEcsDefaults();
595        if (propertyInstance->IsValueSet()) {
596            useEcsPropertyValue = false;
597        }
598
599        // If we don't have local value, initialize value from ECS
600        if (useEcsPropertyValue) {
601            // Set to default value
602            SetValueFromComponent(propertyInstance, prop, slot, true);
603        } else { // otherwise reflect our value to ecs directly
604            SetValueToComponent(prop, propertyInstance, slot);
605        }
606        // and reflect the changes from us to ecs from now on
607        handler.NewHandler(prop, propertyInstance)
608            .Subscribe(propertyInstance, META_NS::MakeCallback<META_NS::IOnChanged>(
609                                             [propertyInstance, slot](const auto& prop) {
610                                                 if (prop) {
611                                                     SetValueToComponent(prop, propertyInstance, slot);
612                                                 }
613                                             },
614                                             prop));
615        handler.NewHandler(nullptr, nullptr)
616            .Subscribe(prop, META_NS::MakeCallback<META_NS::IOnChanged>(
617                                 [propertyInstance, slot](const auto& prop) {
618                                     if (prop) {
619                                         SetValueFromComponent(propertyInstance, prop, slot);
620                                     }
621                                 },
622                                 prop));
623
624        return true;
625    }
626    CORE_LOG_W("%s: could not find '%s'", __func__, name.data());
627    return false;
628}
629