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 "proxy_object.h"
17
18#include <algorithm>
19
20#include <meta/api/make_callback.h>
21#include <meta/api/property/property_event_handler.h>
22#include <meta/api/util.h>
23
24META_BEGIN_NAMESPACE()
25
26namespace Internal {
27
28ProxyObject::~ProxyObject()
29{
30    if (GetMetadata()) {
31        ResetTargetListener();
32        for (auto&& p : Super::GetAllProperties()) {
33            p->OnChanged()->RemoveHandler(uintptr_t(this));
34        }
35    }
36}
37
38bool ProxyObject::Build(const IMetadata::Ptr& data)
39{
40    bool ret = Super::Build(data);
41    if (ret) {
42        Dynamic()->OnChanged()->AddHandler(META_NS::MakeCallback<META_NS::IOnChanged>([this]() {
43            if (Dynamic()->GetValue()) {
44                ListenTargetChanges();
45                RefreshProperties();
46            } else {
47                ResetTargetListener();
48            }
49        }));
50
51        for (auto&& p : Super::GetAllProperties()) {
52            p->OnChanged()->AddHandler(MakeCallback<IOnChanged>(
53                                           [this](auto p) {
54                                               if (p) {
55                                                   OnPropertyChanged(p);
56                                               }
57                                           },
58                                           p),
59                uintptr_t(this));
60        }
61
62        if (auto meta = GetSelf<IMetadata>()) {
63            metaAdded_.Subscribe<IOnChildChanged>(
64                meta->GetPropertyContainer()->OnAdded(), [this](const auto& i) { OnPropertyAdded(i); });
65            metaRemoved_.Subscribe<IOnChildChanged>(
66                meta->GetPropertyContainer()->OnRemoved(), [this](const auto& i) { OnPropertyRemoved(i); });
67        }
68        UpdateSerializeState();
69    }
70    return ret;
71}
72
73static bool SerializeEmbeddedProxy(IProperty::Ptr p)
74{
75    // Check for embedded proxy objects
76    if (auto proxy = GetPointer<IProxyObject>(p)) {
77        if (auto f = interface_cast<IObjectFlags>(proxy)) {
78            return static_cast<bool>(f->GetObjectFlags() & ObjectFlagBits::SERIALIZE);
79        }
80    }
81    return false;
82}
83
84void ProxyObject::OnPropertyAdded(const ChildChangedInfo& info)
85{
86    if (auto p = interface_pointer_cast<IProperty>(info.object)) {
87        auto f = GetSelf<IObjectFlags>();
88        if (!updating_ && f && !(f->GetObjectFlags() & ObjectFlagBits::SERIALIZE)) {
89            META_NS::SetObjectFlags(f, ObjectFlagBits::SERIALIZE, ShouldSerialise(p));
90        }
91        p->OnChanged()->AddHandler(MakeCallback<IOnChanged>(
92                                       [this](auto p) {
93                                           if (p) {
94                                               OnPropertyChanged(p);
95                                           }
96                                       },
97                                       p),
98            uintptr_t(this));
99    }
100}
101
102void ProxyObject::OnPropertyRemoved(const ChildChangedInfo& info)
103{
104    if (auto p = interface_cast<IProperty>(info.object)) {
105        p->OnChanged()->RemoveHandler(uintptr_t(this));
106    }
107    auto f = GetSelf<IObjectFlags>();
108    if (!updating_ && f && f->GetObjectFlags() & ObjectFlagBits::SERIALIZE) {
109        UpdateSerializeState();
110    }
111}
112
113void ProxyObject::OnPropertyChanged(const IProperty::Ptr& p)
114{
115    auto f = GetSelf<IObjectFlags>();
116    if (!updating_ && f) {
117        if (f->GetObjectFlags() & ObjectFlagBits::SERIALIZE) {
118            UpdateSerializeState();
119        } else {
120            META_NS::SetObjectFlags(f, ObjectFlagBits::SERIALIZE, ShouldSerialise(p));
121        }
122    }
123}
124
125void ProxyObject::ListenTargetChanges()
126{
127    if (auto target = interface_pointer_cast<IMetadata>(GetTarget())) {
128        targetAddedListener_.Subscribe<IOnChildChanged>(
129            target->GetPropertyContainer()->OnAdded(), [&](auto) { RefreshProperties(); });
130        targetRemovedListener_.Subscribe<IOnChildChanged>(
131            target->GetPropertyContainer()->OnRemoved(), [&](auto) { RefreshProperties(); });
132    }
133}
134
135void ProxyObject::ResetTargetListener()
136{
137    targetAddedListener_.Unsubscribe();
138    targetRemovedListener_.Unsubscribe();
139}
140
141void ProxyObject::RefreshProperties()
142{
143    updating_ = true;
144    if (auto target = interface_pointer_cast<IMetadata>(GetTarget())) {
145        auto props = BASE_NS::move(proxyProperties_);
146        for (auto&& p : props) {
147            if (auto tp = target->GetPropertyByName(p.first)) {
148                auto res = proxyProperties_.insert({ p.first, p.second });
149                res.first->second.Bind(tp);
150            } else {
151                Super::RemoveProperty(p.second.GetProperty());
152            }
153        }
154    } else {
155        // remove all we added
156        for (auto&& p : proxyProperties_) {
157            Super::RemoveProperty(p.second.GetProperty());
158        }
159        proxyProperties_.clear();
160    }
161    updating_ = false;
162    UpdateSerializeState();
163}
164
165const IObject::Ptr ProxyObject::GetTarget() const
166{
167    return target_.lock();
168}
169
170bool ProxyObject::SetTarget(const IObject::Ptr& target)
171{
172    ResetTargetListener();
173    target_ = target;
174    if (Dynamic()->GetValue()) {
175        ListenTargetChanges();
176    }
177    RefreshProperties();
178    if (META_ACCESS_PROPERTY_VALUE(Mode) & ProxyMode::REFLECT_PROXY_HIERARCHY) {
179        ReflectHierarchy(target);
180    }
181    return true;
182}
183
184void ProxyObject::ReflectHierarchy(const IObject::Ptr& target)
185{
186    auto m = interface_pointer_cast<IMetadata>(target);
187    if (!m) {
188        return;
189    }
190    for (auto&& p : Super::GetAllProperties()) {
191        // reflect only non-proxyable properties
192        if (!proxyProperties_.count(p->GetName())) {
193            if (auto proxy = GetPointer<IProxyObject>(p)) {
194                ReflectTargetForProperty(m, p->GetName(), proxy);
195            }
196        }
197    }
198}
199
200void ProxyObject::ReflectTargetForProperty(
201    const IMetadata::Ptr& m, BASE_NS::string_view name, const IProxyObject::Ptr& proxy)
202{
203    if (auto p = m->GetPropertyByName(name)) {
204        if (auto tp = GetPointer<IObject>(p)) {
205            proxy->SetTarget(tp);
206        }
207    }
208}
209
210BASE_NS::vector<IProperty::ConstPtr> ProxyObject::GetOverrides() const
211{
212    BASE_NS::vector<IProperty::ConstPtr> res;
213    for (auto&& p : proxyProperties_) {
214        if (!p.second.IsDefaultValue()) {
215            res.push_back(p.second.GetProperty());
216        }
217    }
218    return res;
219}
220
221IProperty::ConstPtr ProxyObject::GetOverride(BASE_NS::string_view name) const
222{
223    auto it = proxyProperties_.find(name);
224    return it != proxyProperties_.end() && !it->second.IsDefaultValue() ? it->second.GetProperty() : nullptr;
225}
226
227IProperty::ConstPtr ProxyObject::GetProxyProperty(BASE_NS::string_view name) const
228{
229    auto it = proxyProperties_.find(name);
230    return it != proxyProperties_.end() ? it->second.GetProperty() : nullptr;
231}
232
233IProperty::Ptr ProxyObject::SetPropertyTarget(const IProperty::Ptr& property)
234{
235    if (!property) {
236        return nullptr;
237    }
238    IProperty::Ptr res;
239    bool add = true;
240    auto it = proxyProperties_.find(property->GetName());
241    if (it != proxyProperties_.end()) {
242        auto bprop = it->second.GetProperty();
243        add = !bprop || bprop->GetTypeId() != property->GetTypeId();
244        if (add) {
245            proxyProperties_.erase(it);
246        } else {
247            it->second.Bind(property);
248            res = it->second.GetProperty();
249        }
250    }
251    if (add && property) {
252        if (auto p = Super::GetPropertyByName(property->GetName())) {
253            auto r = proxyProperties_.insert_or_assign(p->GetName(), DefaultValueBind(p));
254            r.first->second.Bind(property);
255            res = p;
256        } else {
257            res = AddProxyProperty(property);
258        }
259    }
260    return res;
261}
262
263IProperty::Ptr ProxyObject::AddProxyProperty(const IProperty::ConstPtr& tp)
264{
265    auto p = DuplicatePropertyType(META_NS::GetObjectRegistry(), tp);
266    if (p) {
267        auto res = proxyProperties_.insert_or_assign(p->GetName(), DefaultValueBind(p));
268        res.first->second.Bind(tp);
269        AddProperty(p);
270    }
271    return p;
272}
273
274IProperty::Ptr ProxyObject::AddProxyProperty(BASE_NS::string_view name)
275{
276    IProperty::Ptr p;
277    if (auto target = interface_pointer_cast<IMetadata>(GetTarget())) {
278        if (auto tp = target->GetPropertyByName(name)) {
279            p = AddProxyProperty(tp);
280        }
281    }
282    return p;
283}
284
285void ProxyObject::PopulateAllProperties()
286{
287    if (auto target = interface_pointer_cast<IMetadata>(GetTarget())) {
288        for (auto&& p : target->GetAllProperties()) {
289            auto it = proxyProperties_.find(p->GetName());
290            if (it == proxyProperties_.end()) {
291                AddProxyProperty(p);
292            }
293        }
294    }
295}
296
297IProperty::Ptr ProxyObject::GetPropertyByName(BASE_NS::string_view name)
298{
299    auto p = Super::GetPropertyByName(name);
300    if (!p) {
301        p = AddProxyProperty(name);
302    }
303    return p;
304}
305
306IProperty::ConstPtr ProxyObject::GetPropertyByName(BASE_NS::string_view name) const
307{
308    return const_cast<ProxyObject*>(this)->GetPropertyByName(name);
309}
310
311void ProxyObject::RemoveProperty(const IProperty::Ptr& p)
312{
313    Super::RemoveProperty(p);
314    proxyProperties_.erase(p->GetName());
315}
316
317BASE_NS::vector<IProperty::Ptr> ProxyObject::GetAllProperties()
318{
319    PopulateAllProperties();
320    return Super::GetAllProperties();
321}
322
323BASE_NS::vector<IProperty::ConstPtr> ProxyObject::GetAllProperties() const
324{
325    const_cast<ProxyObject*>(this)->PopulateAllProperties();
326    return Super::GetAllProperties();
327}
328
329bool ProxyObject::ShouldSerialise(const IProperty::Ptr& p) const
330{
331    auto s = interface_cast<IStackProperty>(p);
332    return (s && !s->GetValues({}, false).empty()) ||
333           (!IsFlagSet(p, ObjectFlagBits::NATIVE) && !proxyProperties_.count(p->GetName())) ||
334           SerializeEmbeddedProxy(p);
335}
336
337void ProxyObject::UpdateSerializeState()
338{
339    if (!updating_) {
340        bool serialise = !GetOverrides().empty();
341        if (!serialise) {
342            for (auto&& p : Super::GetAllProperties()) {
343                serialise = ShouldSerialise(p);
344                if (serialise) {
345                    break;
346                }
347            }
348        }
349        META_NS::SetObjectFlags(GetSelf(), ObjectFlagBits::SERIALIZE, serialise);
350    }
351}
352
353} // namespace Internal
354
355META_END_NAMESPACE()
356