1/*
2 * Copyright (c) 2023 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 "calendar_manager_napi.h"
17#include <optional>
18
19using namespace OHOS::AppExecFwk;
20using namespace OHOS::DataShare;
21static const int INVALID_EVENT_ID = -1;
22static const int ARGS_INDEX_ONE = 1;
23static const int ARGS_INDEX_TWO = 2;
24static const int ARGS_INDEX_THREE = 3;
25
26namespace {
27    const std::string CALENDAR_MANAGER_CLASS_NAME = "CalendarManager";
28    static thread_local napi_ref g_constructorRef = nullptr;
29    constexpr uint32_t INITIAL_REFCOUNT = 1;
30}
31namespace OHOS::CalendarApi {
32napi_value CalendarManagerNapi::CreateCalendar(napi_env env, napi_callback_info info)
33{
34    LOG_INFO("napi CreateCalendar called");
35    struct CreateCalendarContext : public ContextBase {
36        CalendarAccount account;
37        CalendarNapi *calendar;
38        int id;
39        napi_ref ref = nullptr;
40    };
41    auto ctxt = std::make_shared<CreateCalendarContext>();
42    auto input = [env, ctxt](size_t argc, napi_value* argv) {
43        // required 1 arguments :: <CalendarAccount>
44        CHECK_ARGS_RETURN_VOID(ctxt, argc == 1, "invalid arguments!");
45        NapiUtil::GetValue(env, argv[0], ctxt->account);
46        CHECK_STATUS_RETURN_VOID(ctxt, "invalid arg[0], i.e. invalid keys!");
47        ctxt->ref = NapiUtil::NewWithRef(env, argc, argv, reinterpret_cast<void**>(&ctxt->calendar),
48            CalendarNapi::Constructor(env));
49    };
50    ctxt->GetCbInfo(env, info, input);
51
52    auto execute = [ctxt]() {
53        auto nativteCalendar = Native::CalendarManager::GetInstance().CreateCalendar(ctxt->account);
54        ctxt->status = (nativteCalendar != nullptr) ? napi_ok : napi_generic_failure;
55        CHECK_STATUS_RETURN_VOID(ctxt, "CreateCalendar failed!");
56        ctxt->calendar->SetNative(nativteCalendar);
57        ctxt->id = nativteCalendar->GetId();
58    };
59    auto output = [env, ctxt](napi_value& result) {
60        ctxt->status = napi_get_reference_value(env, ctxt->ref, &result);
61        CHECK_STATUS_RETURN_VOID(ctxt, "CreateCalendar output get ref value failed");
62        ctxt->status = NapiUtil::SetNamedProperty(env, "id", ctxt->id, result);
63        CHECK_STATUS_RETURN_VOID(ctxt, "CreateCalendar SetNamedProperty id failed");
64        ctxt->status = napi_delete_reference(env, ctxt->ref);
65        CHECK_STATUS_RETURN_VOID(ctxt, "CreateCalendar output del ref failed");
66    };
67    return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute, output);
68}
69
70napi_value CalendarManagerNapi::DeleteCalendar(napi_env env, napi_callback_info info)
71{
72    LOG_INFO("napi DeleteCalendar called");
73    struct DelCalendarContext : public ContextBase {
74        CalendarAccount account;
75        CalendarNapi *calendar;
76        bool delResult = false;
77    };
78    auto ctxt = std::make_shared<DelCalendarContext>();
79    auto input = [env, ctxt](size_t argc, napi_value* argv) {
80        // required 1 arguments :: <Calendar>
81        CHECK_ARGS_RETURN_VOID(ctxt, argc == 1, "invalid arguments!");
82        napi_valuetype type = napi_undefined;
83        napi_typeof(env, argv[0], &type);
84        CHECK_ARGS_RETURN_VOID(ctxt, type == napi_object, "type error!");
85        ctxt->status = CalendarNapi::ToJson(env, argv[0], ctxt->calendar);
86        CHECK_STATUS_RETURN_VOID(ctxt, "invalid arg[0], i.e. invalid keys!");
87    };
88    ctxt->GetCbInfo(env, info, input);
89
90    auto execute = [ctxt]() {
91        CHECK_RETURN_VOID(ctxt->calendar, "calendar is nullptr");
92        auto nativeCalendar = ctxt->calendar->GetNative();
93        CHECK_RETURN_VOID(nativeCalendar, "calendar is nullptr");
94        ctxt->delResult = Native::CalendarManager::GetInstance()
95            .DeleteCalendar(*(nativeCalendar.get()));
96        CHECK_RETURN_VOID(ctxt->delResult, "DeleteCalendar failed!");
97    };
98    auto output = [env, ctxt](napi_value& result) {
99        NapiUtil::SetValue(env, ctxt->delResult, result);
100        CHECK_STATUS_RETURN_VOID(ctxt, "output del ref failed");
101    };
102    return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute, output);
103}
104
105struct GetCalendarContext : public ContextBase {
106    std::optional<CalendarAccount> account;
107    CalendarNapi *calendar = nullptr;
108    napi_ref ref = nullptr;
109    int id = -1;
110
111    void GetCbInfo(napi_env env, napi_callback_info info)
112    {
113        auto input = [env, this](size_t argc, napi_value* argv) {
114            // required at least 1 arguments :: <CalendarAccount>
115            CHECK_ARGS_RETURN_VOID(this, argc <= 1, "invalid arguments!");
116            if (argc == 0) {
117                this->account = std::nullopt;
118            } else {
119                CalendarAccount tmpAccount;
120                NapiUtil::GetValue(env, argv[0], tmpAccount);
121                this->account = tmpAccount;
122            }
123            CHECK_STATUS_RETURN_VOID(this, "invalid arg[0], i.e. invalid keys!");
124            ref = NapiUtil::NewWithRef(env, argc, argv, reinterpret_cast<void**>(&calendar),
125                CalendarNapi::Constructor(env));
126        };
127        LOG_DEBUG("call ContextBase::GetCbInfo");
128        ContextBase::GetCbInfo(env, info, input);
129    }
130};
131
132napi_value CalendarManagerNapi::GetCalendar(napi_env env, napi_callback_info info)
133{
134    LOG_DEBUG("GetCalendar in");
135    auto ctxt = std::make_shared<GetCalendarContext>();
136    ctxt->GetCbInfo(env, info);
137
138    auto execute = [ctxt]() {
139        auto nativteCalendar = Native::CalendarManager::GetInstance().GetCalendar(ctxt->account);
140        ctxt->status = (nativteCalendar != nullptr) ? napi_ok : napi_generic_failure;
141        CHECK_STATUS_RETURN_VOID(ctxt, "GetCalendar error!");
142        if (nativteCalendar->GetId() == -1) {
143            ctxt->status = napi_generic_failure;
144            CHECK_STATUS_RETURN_VOID(ctxt, "GetCalendar failed!");
145        }
146        ctxt->calendar->SetNative(nativteCalendar);
147        ctxt->id = nativteCalendar->GetId();
148    };
149    auto output = [env, ctxt](napi_value& result) {
150        ctxt->status = napi_get_reference_value(env, ctxt->ref, &result);
151        CHECK_STATUS_RETURN_VOID(ctxt, "GetCalendar output get ref value failed");
152        ctxt->status = NapiUtil::SetNamedProperty(env, "id", ctxt->id, result);
153        CHECK_STATUS_RETURN_VOID(ctxt, "GetCalendar SetNamedProperty id failed");
154        ctxt->status = napi_delete_reference(env, ctxt->ref);
155        CHECK_STATUS_RETURN_VOID(ctxt, "GetCalendar output del ref failed");
156    };
157    return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute, output);
158}
159
160napi_value CalendarManagerNapi::GetAllCalendars(napi_env env, napi_callback_info info)
161{
162    LOG_DEBUG("napi GetAllCalendars called");
163    struct GetAllCalendarContext : public ContextBase {
164        napi_callback_info info;
165        std::vector<napi_ref> refs;
166    };
167
168    auto ctxt = std::make_shared<GetAllCalendarContext>();
169    auto input = [env, ctxt](size_t argc, napi_value* argv) {
170        CHECK_ARGS_RETURN_VOID(ctxt, argc == 0, "invalid arguments!");
171        auto nativteCalendars = Native::CalendarManager::GetInstance().GetAllCalendars();
172        for (auto &calendar : nativteCalendars) {
173            CalendarNapi *calendarNapi = nullptr;
174            auto ref = NapiUtil::NewWithRef(env, argc, argv, reinterpret_cast<void**>(&calendarNapi),
175                CalendarNapi::Constructor(env));
176            CHECK_RETURN_VOID(calendarNapi != nullptr, "new CalendarNapi failed!");
177            calendarNapi->SetNative(calendar);
178            napi_value value;
179            ctxt->status = napi_get_reference_value(env, ref, &value);
180            CHECK_STATUS_RETURN_VOID(ctxt, "napi_get_reference_value failed");
181            ctxt->status = NapiUtil::SetNamedProperty(env, "id", calendar->GetId(), value);
182            CHECK_STATUS_RETURN_VOID(ctxt, "SetNamedProperty id failed");
183            ctxt->refs.emplace_back(ref);
184        }
185    };
186    ctxt->GetCbInfo(env, info, input);
187
188    auto execute = [env, ctxt]()->void {
189    };
190
191    auto output = [env, ctxt](napi_value& result) {
192        ctxt->status = napi_create_array_with_length(env, ctxt->refs.size(), &result);
193        CHECK_STATUS_RETURN_VOID(ctxt, "create array failed!");
194        int index = 0;
195        for (auto& ref : ctxt->refs) {
196            napi_value value;
197            ctxt->status = napi_get_reference_value(env, ref, &value);
198            CHECK_STATUS_RETURN_VOID(ctxt, "get ref value failed!");
199            ctxt->status = napi_set_element(env, result, index++, value);
200            CHECK_STATUS_RETURN_VOID(ctxt, "napi_set_element failed!");
201            ctxt->status = napi_delete_reference(env, ref);
202            CHECK_STATUS_RETURN_VOID(ctxt, "napi_delete_reference failed!");
203        }
204    };
205    return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute, output);
206}
207
208napi_value GetCalendarManager(napi_env env, napi_callback_info info)
209{
210    LOG_INFO("napi GetCalendarManager called");
211    const int argsOne = 1;
212    napi_value result = nullptr;
213    napi_value cons = nullptr;
214    size_t requireArgc = argsOne;
215    size_t argc = argsOne;
216    napi_value args[argsOne] = {nullptr};
217    if (napi_get_cb_info(env, info, &argc, args, nullptr, nullptr) != napi_ok) {
218        return nullptr;
219    }
220
221    if (argc > requireArgc || napi_get_reference_value(env, g_constructorRef, &cons) != napi_ok) {
222        return nullptr;
223    }
224
225    if (napi_new_instance(env, cons, argsOne, args, &result) != napi_ok) {
226        return nullptr;
227    }
228    CalendarManagerNapi *calendarManager = nullptr;
229    if (napi_unwrap(env, result, (void **)&calendarManager) != napi_ok) {
230        LOG_ERROR("Faild to get fileAccessHelper");
231        return nullptr;
232    }
233
234    if (calendarManager == nullptr) {
235        LOG_ERROR("fileAccessHelper is nullptr");
236        return nullptr;
237    }
238    return result;
239}
240
241napi_value CalendarManagerNapi::EditEvent(napi_env env, napi_callback_info info)
242{
243    LOG_INFO("editEvent called");
244    napi_value eventId = nullptr;
245    NapiUtil::SetValue(env, INVALID_EVENT_ID, eventId);
246    auto ctxt = std::make_shared<EditEventContext>();
247    auto input = [env, ctxt](size_t argc, napi_value* argv) {
248        CHECK_ARGS_RETURN_VOID(ctxt, argc == ARGS_INDEX_THREE, "invalid arguments!");
249        ctxt-> _jsContext = argv[0];
250        NapiUtil::GetValue(env, argv[ARGS_INDEX_ONE], ctxt->event);
251        NapiUtil::GetValue(env, argv[ARGS_INDEX_TWO], ctxt->caller);
252    };
253    ctxt->GetCbInfo(env, info, input, true);
254
255    bool isStageMode = false;
256    auto jsContext = ctxt->_jsContext;
257    auto status = OHOS::AbilityRuntime::IsStageContext(env, jsContext, isStageMode);
258    if (status != napi_ok || !isStageMode) {
259        LOG_ERROR("editEvent No support FA Model");
260        return eventId;
261    }
262    auto stageContext = OHOS::AbilityRuntime::GetStageModeContext(env, jsContext);
263    if (stageContext == nullptr) {
264        LOG_ERROR("editEvent stageContext == nullptr.");
265        return eventId;
266    }
267    auto abilityContext = OHOS::AbilityRuntime::Context::ConvertTo<AbilityRuntime::AbilityContext>(stageContext);
268    if (abilityContext == nullptr) {
269        LOG_ERROR("editEvent only support for UIAbility Context.");
270        return eventId;
271    }
272    ctxt->_uiContent = abilityContext->GetUIContent();
273    return LaunchEditorPage(env, ctxt);
274}
275
276napi_value CalendarManagerNapi::LaunchEditorPage(napi_env env, std::shared_ptr<EditEventContext> ctxt)
277{
278    napi_value promise = nullptr;
279    napi_deferred deferred = nullptr;
280    napi_create_promise(env, &deferred, &promise);
281    AAFwk::Want want;
282    want.SetElementName("com.ohos.calendardata", "EditorUIExtensionAbility");
283    const std::string uiExtType = "sys/commonUI";
284    want.SetParam("ability.want.params.uiExtensionType", uiExtType);
285    want.SetParam("event", ctxt->event);
286    want.SetParam("caller", ctxt->caller);
287    Ace::ModalUIExtensionCallbacks callbacks;
288    callbacks = {
289        .onRelease = [env, ctxt, deferred](int32_t code) {
290            LOG_INFO("editEvent onRelease callback.");
291            ctxt->_uiContent->CloseModalUIExtension(ctxt->_sessionId);
292            napi_resolve_deferred(env, deferred, ctxt->id);
293            LOG_INFO("editEvent onRelease done.");
294        },
295        .onResult = [env, ctxt, deferred](int32_t code, const AAFwk::Want &wantRes) {
296            auto eventId = wantRes.GetIntParam("eventId", INVALID_EVENT_ID);
297            LOG_INFO("editEvent onResult. eventId=%{public}d", eventId);
298            NapiUtil::SetValue(env, eventId, ctxt->id);
299        },
300        .onReceive = [env, ctxt](const AAFwk::WantParams &wantParams) {
301            LOG_INFO("editEvent onReceive.");
302        },
303        .onError = [env, ctxt, deferred](int32_t code, const std::string &event, const std::string &msg) {
304            LOG_ERROR("editEvent onError.%{public}s", msg.c_str());
305            ctxt->_uiContent->CloseModalUIExtension(ctxt->_sessionId);
306            napi_reject_deferred(env, deferred, ctxt->id);
307        },
308        .onRemoteReady = [env, ctxt, deferred](const std::shared_ptr<Ace::ModalUIExtensionProxy> &proxy) {
309            LOG_INFO("editEvent onRemoteReady.");
310        },
311        .onDestroy = [env, ctxt, deferred]{
312            LOG_INFO("editEvent onDestroy.");
313        },
314    };
315    Ace::ModalUIExtensionConfig config;
316    config = {
317        .isProhibitBack = false,
318    };
319    ctxt->_sessionId = ctxt->_uiContent->CreateModalUIExtension(want, callbacks, config);
320    LOG_INFO("editEvent CreateModalUI sessionId=%{public}d", ctxt->_sessionId);
321    return promise;
322}
323
324napi_value CalendarManagerNapi::New(napi_env env, napi_callback_info info)
325{
326    auto ctxt = std::make_shared<ContextBase>();
327    auto input = [env, ctxt](size_t argc, napi_value* argv) {
328        CHECK_ARGS_RETURN_VOID(ctxt, argc == 1, "invalid arguments!");
329        CalendarEnvNapi::GetInstance().Init(env, argv[0]);
330    };
331    ctxt->GetCbInfoSync(env, info, input);
332    NAPI_ASSERT(env, ctxt->status == napi_ok, "invalid arguments!");
333
334    auto calendarManager = new (std::nothrow) CalendarManagerNapi();
335    NAPI_ASSERT(env, calendarManager != nullptr, "no memory for calendarManager");
336    auto finalize = [](napi_env env, void *data, void *hint) {
337        CalendarManagerNapi *objectInfo = static_cast<CalendarManagerNapi *>(data);
338        if (objectInfo != nullptr) {
339            delete objectInfo;
340            objectInfo = nullptr;
341        }
342    };
343    if (napi_wrap(env, ctxt->self, calendarManager, finalize, nullptr, nullptr) != napi_ok) {
344        finalize(env, calendarManager, nullptr);
345        return nullptr;
346    }
347    return ctxt->self;
348}
349
350napi_value CalendarManagerNapi::Init(napi_env env, napi_value exports)
351{
352    napi_property_descriptor properties[] = {
353        DECLARE_NAPI_FUNCTION("createCalendar", CreateCalendar),
354        DECLARE_NAPI_FUNCTION("deleteCalendar", DeleteCalendar),
355        DECLARE_NAPI_FUNCTION("getCalendar", GetCalendar),
356        DECLARE_NAPI_FUNCTION("getAllCalendars", GetAllCalendars),
357        DECLARE_NAPI_FUNCTION("editEvent", EditEvent),
358    };
359    napi_value cons = nullptr;
360    NAPI_CALL(env,
361        napi_define_class(env,
362            CALENDAR_MANAGER_CLASS_NAME.c_str(),
363            NAPI_AUTO_LENGTH,
364            New,
365            nullptr,
366            sizeof(properties) / sizeof(*properties),
367            properties,
368            &cons));
369    NAPI_CALL(env, napi_create_reference(env, cons, INITIAL_REFCOUNT, &g_constructorRef));
370    NAPI_CALL(env, napi_set_named_property(env, exports, CALENDAR_MANAGER_CLASS_NAME.c_str(), cons));
371
372    napi_property_descriptor export_properties[] = {
373        DECLARE_NAPI_FUNCTION("getCalendarManager", GetCalendarManager),
374    };
375    NAPI_CALL(env, napi_define_properties(env, exports, sizeof(export_properties) / sizeof(export_properties[0]),
376        export_properties));
377    return exports;
378}
379}