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#include "ToneMapJS.h"
16
17#include <meta/api/make_callback.h>
18#include <meta/interface/intf_task_queue.h>
19#include <meta/interface/intf_task_queue_registry.h>
20#include <meta/interface/property/property_events.h>
21#include <scene_plugin/api/camera.h> //for the classid...
22#include <scene_plugin/api/node_uid.h>
23#include <scene_plugin/interface/intf_ecs_scene.h>
24#include <scene_plugin/interface/intf_node.h>
25#include <scene_plugin/interface/intf_postprocess.h>
26#include <scene_plugin/interface/intf_scene.h>
27
28#include <render/intf_render_context.h>
29
30#include "PostProcJS.h"
31using IntfPtr = BASE_NS::shared_ptr<CORE_NS::IInterface>;
32using IntfWeakPtr = BASE_NS::weak_ptr<CORE_NS::IInterface>;
33using namespace SCENE_NS;
34SCENE_NS::ITonemap::TonemapType ConvertTo(ToneMapJS::ToneMappingType typeI)
35{
36    SCENE_NS::ITonemap::TonemapType type;
37    switch (typeI) {
38        case ToneMapJS::ToneMappingType::ACES:
39            type = SCENE_NS::ITonemap::TonemapType::ACES;
40            break;
41        case ToneMapJS::ToneMappingType::ACES_2020:
42            type = SCENE_NS::ITonemap::TonemapType::ACES_2020;
43            break;
44        case ToneMapJS::ToneMappingType::FILMIC:
45            type = SCENE_NS::ITonemap::TonemapType::FILMIC;
46            break;
47        default:
48            // default from lowlev..
49            type = ITonemap::TonemapType::ACES;
50            break;
51    }
52    return type;
53}
54ToneMapJS::ToneMappingType ConvertFrom(SCENE_NS::ITonemap::TonemapType typeI)
55{
56    ToneMapJS::ToneMappingType type;
57    switch (typeI) {
58        case SCENE_NS::ITonemap::TonemapType::ACES:
59            type = ToneMapJS::ToneMappingType::ACES;
60            break;
61        case SCENE_NS::ITonemap::TonemapType::ACES_2020:
62            type = ToneMapJS::ToneMappingType::ACES_2020;
63            break;
64        case SCENE_NS::ITonemap::TonemapType::FILMIC:
65            type = ToneMapJS::ToneMappingType ::FILMIC;
66            break;
67        default:
68            // default from lowlev..
69            type = ToneMapJS::ToneMappingType ::ACES;
70            break;
71    }
72    return type;
73}
74
75SCENE_NS::ITonemap::TonemapType ConvertTo(uint32_t typeI)
76{
77    return ConvertTo(static_cast<ToneMapJS::ToneMappingType>(typeI));
78}
79void ToneMapJS::Init(napi_env env, napi_value exports)
80{
81    using namespace NapiApi;
82
83    BASE_NS::vector<napi_property_descriptor> node_props;
84    // clang-format off
85    node_props.emplace_back(GetSetProperty<uint32_t, ToneMapJS, &ToneMapJS::GetType, &ToneMapJS::SetType>("type"));
86    node_props.emplace_back(GetSetProperty<float, ToneMapJS, &ToneMapJS::GetExposure,
87        &ToneMapJS::SetExposure>("exposure"));
88    node_props.push_back(MakeTROMethod<NapiApi::FunctionContext<>, ToneMapJS, &ToneMapJS::Dispose>("destroy"));
89    // clang-format on
90
91    napi_value func;
92    auto status = napi_define_class(env, "ToneMappingSettings", NAPI_AUTO_LENGTH, BaseObject::ctor<ToneMapJS>(),
93        nullptr, node_props.size(), node_props.data(), &func);
94
95    NapiApi::MyInstanceState* mis;
96    napi_get_instance_data(env, (void**)&mis);
97    mis->StoreCtor("ToneMappingSettings", func);
98
99    NapiApi::Object exp(env, exports);
100
101    napi_value eType;
102    napi_value v;
103    napi_create_object(env, &eType);
104#define DECL_ENUM(enu, x)                            \
105    napi_create_uint32(env, ToneMappingType::x, &v); \
106    napi_set_named_property(env, enu, #x, v);
107
108    DECL_ENUM(eType, ACES);
109    DECL_ENUM(eType, ACES_2020);
110    DECL_ENUM(eType, FILMIC);
111#undef DECL_ENUM
112    exp.Set("ToneMappingType", eType);
113}
114
115napi_value ToneMapJS::Dispose(NapiApi::FunctionContext<>& ctx)
116{
117    LOG_F("ToneMapJS::Dispose");
118    DisposeNative();
119    return {};
120}
121void ToneMapJS::DisposeNative()
122{
123    if (!disposed_) {
124        disposed_ = true;
125        LOG_F("ToneMapJS::DisposeNative");
126        if (auto tmp = interface_pointer_cast<SCENE_NS::ITonemap>(GetNativeObject())) {
127            // reset the native object refs
128            SetNativeObject(nullptr, false);
129            SetNativeObject(nullptr, true);
130
131            ExecSyncTask([scn = BASE_NS::move(tmp)]() { return META_NS::IAny::Ptr {}; });
132        }
133    }
134}
135void* ToneMapJS::GetInstanceImpl(uint32_t id)
136{
137    if (id == ToneMapJS::ID) {
138        return this;
139    }
140    return nullptr;
141}
142void ToneMapJS::Finalize(napi_env env)
143{
144    // hmm.. do i need to do something BEFORE the object gets deleted..
145    DisposeNative();
146    BaseObject<ToneMapJS>::Finalize(env);
147}
148
149ToneMapJS::ToneMapJS(napi_env e, napi_callback_info i) : BaseObject<ToneMapJS>(e, i)
150{
151    LOG_F("ToneMapJS ++");
152    NapiApi::FunctionContext<NapiApi::Object, NapiApi::Object> fromJs(e, i);
153    if (!fromJs) {
154        // no arguments. so internal create.
155        // expecting caller to finish
156        return;
157    }
158    // postprocess that we bind to..
159    NapiApi::Object postProcJS = fromJs.Arg<0>();
160    auto postproc = GetNativeMeta<SCENE_NS::IPostProcess>(postProcJS);
161    NapiApi::Object toneMapArgs = fromJs.Arg<1>();
162    // now, based on parameters, initialize the object
163    // so it is a tonemap
164    float exposure = toneMapArgs.Get<float>("exposure").valueOrDefault(0.7);
165    SCENE_NS::ITonemap::TonemapType type =
166        ConvertTo(toneMapArgs.Get<uint32_t>("type").valueOrDefault(ToneMappingType::ACES));
167
168    auto tonemap = GetNativeObjectParam<SCENE_NS::ITonemap>(toneMapArgs);
169
170    ExecSyncTask([&tonemap, exposure, type, postproc]() -> META_NS::IAny::Ptr {
171        if (!tonemap) {
172            tonemap = META_NS::GetObjectRegistry().Create<SCENE_NS::ITonemap>(SCENE_NS::ClassId::Tonemap);
173        }
174        tonemap->Type()->SetValue(type);
175        tonemap->Exposure()->SetValue(exposure);
176        tonemap->Enabled()->SetValue(true);
177        postproc->Tonemap()->SetValue(tonemap);
178        return {};
179    });
180    auto obj = interface_pointer_cast<META_NS::IObject>(tonemap);
181    // process constructor args..
182    NapiApi::Object meJs(e, fromJs.This());
183    // weak ref, due to the ToneMap class being owned by the postprocess.
184    SetNativeObject(obj, false);
185    StoreJsObj(obj, meJs);
186}
187
188ToneMapJS::~ToneMapJS()
189{
190    LOG_F("ToneMapJS --");
191    DisposeNative();
192    if (!GetNativeObject()) {
193        return;
194    }
195}
196
197napi_value ToneMapJS::GetType(NapiApi::FunctionContext<>& ctx)
198{
199    SCENE_NS::ITonemap::TonemapType type = SCENE_NS::ITonemap::TonemapType::ACES; // default
200    if (auto toneMap = interface_cast<SCENE_NS::ITonemap>(GetNativeObject())) {
201        ExecSyncTask([toneMap, &type]() {
202            type = toneMap->Type()->GetValue();
203            return META_NS::IAny::Ptr {};
204        });
205    }
206
207    auto typeI = ConvertFrom(type);
208    napi_value value;
209    napi_status status = napi_create_uint32(ctx, static_cast<uint32_t>(typeI), &value);
210    return value;
211}
212void ToneMapJS::SetType(NapiApi::FunctionContext<uint32_t>& ctx)
213{
214    auto type = ConvertTo(ctx.Arg<0>());
215
216    if (auto toneMap = interface_cast<SCENE_NS::ITonemap>(GetNativeObject())) {
217        ExecSyncTask([toneMap, type]() {
218            toneMap->Type()->SetValue(type);
219            return META_NS::IAny::Ptr {};
220        });
221    }
222}
223
224napi_value ToneMapJS::GetExposure(NapiApi::FunctionContext<>& ctx)
225{
226    float exp = 0.0;
227    if (auto toneMap = interface_cast<SCENE_NS::ITonemap>(GetNativeObject())) {
228        ExecSyncTask([toneMap, &exp]() {
229            exp = toneMap->Exposure()->GetValue();
230            return META_NS::IAny::Ptr {};
231        });
232    }
233
234    napi_value value;
235    napi_status status = napi_create_double(ctx, exp, &value);
236    return value;
237}
238
239void ToneMapJS::SetExposure(NapiApi::FunctionContext<float>& ctx)
240{
241    float exp = ctx.Arg<0>();
242    if (auto toneMap = interface_cast<SCENE_NS::ITonemap>(GetNativeObject())) {
243        ExecSyncTask([toneMap, exp]() {
244            toneMap->Exposure()->SetValue(exp);
245            return META_NS::IAny::Ptr {};
246        });
247    }
248}
249