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_napi.h"
17#include "calendar_log.h"
18#include "napi_util.h"
19#include "napi_queue.h"
20#include "native_util.h"
21
22namespace {
23    const std::string CALENDAR_CLASS_NAME = "Calendar";
24}
25
26namespace OHOS::CalendarApi {
27
28napi_value CalendarNapi::Constructor(napi_env env)
29{
30    const napi_property_descriptor properties[] = {
31        DECLARE_NAPI_FUNCTION("addEvent", AddEvent),
32        DECLARE_NAPI_FUNCTION("addEvents", AddEvents),
33        DECLARE_NAPI_FUNCTION("deleteEvent", DeleteEvent),
34        DECLARE_NAPI_FUNCTION("deleteEvents", DeleteEvents),
35        DECLARE_NAPI_FUNCTION("updateEvent", UpdateEvent),
36        DECLARE_NAPI_FUNCTION("updateEvents", UpdateEvents),
37        DECLARE_NAPI_FUNCTION("getEvents", GetEvents),
38        DECLARE_NAPI_FUNCTION("getConfig", GetConfig),
39        DECLARE_NAPI_FUNCTION("setConfig", SetConfig),
40        DECLARE_NAPI_FUNCTION("getAccount", GetAccount),
41    };
42    size_t count = sizeof(properties) / sizeof(properties[0]);
43    return NapiUtil::DefineClass(env, "Calendar", properties, count, CalendarNapi::New);
44}
45
46/*
47 * [JS API Prototype]
48 *      var calendar = new Calendar();
49 */
50napi_value CalendarNapi::New(napi_env env, napi_callback_info info)
51{
52    auto ctxt = std::make_shared<ContextBase>();
53    auto input = [env, ctxt](size_t argc, napi_value* argv) {
54        CHECK_ARGS_RETURN_VOID(ctxt, argc <= 1, "invalid arguments!");
55    };
56    ctxt->GetCbInfoSync(env, info, input);
57    NAPI_ASSERT(env, ctxt->status == napi_ok, "invalid arguments!");
58
59    auto calendar = new (std::nothrow) CalendarNapi();
60    NAPI_ASSERT(env, calendar != nullptr, "no memory for calendar");
61
62    auto finalize = [](napi_env env, void* data, void* hint) {
63        LOG_DEBUG("calendar finalize.");
64        auto* calendar = reinterpret_cast<CalendarNapi*>(data);
65        CHECK_RETURN_VOID(calendar != nullptr, "finalize null!");
66        delete calendar;
67    };
68    if (napi_wrap(env, ctxt->self, calendar, finalize, nullptr, nullptr) != napi_ok) {
69        delete calendar;
70        GET_AND_THROW_LAST_ERROR(env);
71        return nullptr;
72    }
73    return ctxt->self;
74}
75
76napi_status CalendarNapi::ToJson(napi_env env, napi_value inner, CalendarNapi*& out)
77{
78    LOG_DEBUG("CalendarNapi::ToJson");
79    return NapiUtil::Unwrap(env, inner, reinterpret_cast<void**>(&out), CalendarNapi::Constructor(env));
80}
81
82void CalendarNapi::SetNative(std::shared_ptr<Native::Calendar>& calendar)
83{
84    calendar_ = calendar;
85}
86std::shared_ptr<Native::Calendar>& CalendarNapi::GetNative()
87{
88    return calendar_;
89}
90
91napi_value CalendarNapi::AddEvent(napi_env env, napi_callback_info info)
92{
93    LOG_INFO("AddEvent");
94    struct AddEventContext : public ContextBase {
95        Event event;
96        int eventId;
97    };
98    auto ctxt = std::make_shared<AddEventContext>();
99    auto input = [env, ctxt](size_t argc, napi_value* argv) {
100        // required atleast 1 arguments :: <eventFilter>
101        CHECK_ARGS_RETURN_VOID(ctxt, argc == 1, "invalid arguments!");
102        ctxt->status = NapiUtil::GetValue(env, argv[0], ctxt->event);
103        CHECK_STATUS_RETURN_VOID(ctxt, "AddEvent failed!");
104        Native::DumpEvent(ctxt->event);
105        CHECK_STATUS_RETURN_VOID(ctxt, "invalid arg[0], i.e. invalid keys!");
106    };
107    ctxt->GetCbInfo(env, info, input);
108    auto execute = [ctxt]() {
109        auto calendar = reinterpret_cast<CalendarNapi*>(ctxt->native);
110        CHECK_RETURN_VOID(calendar != nullptr, "CalendarNapi nullptr");
111        auto nativeCalendar = calendar->GetNative();
112        CHECK_RETURN_VOID(nativeCalendar != nullptr, "nativeCalendar nullptr");
113        ctxt->eventId = nativeCalendar->AddEvent(ctxt->event);
114        ctxt->status = (ctxt->eventId > 0) ? napi_ok : napi_generic_failure;
115        CHECK_STATUS_RETURN_VOID(ctxt, "AddEvent failed!");
116    };
117    auto output = [env, ctxt](napi_value& result) {
118        ctxt->status = NapiUtil::SetValue(ctxt->env, ctxt->eventId, result);
119        CHECK_STATUS_RETURN_VOID(ctxt, "output failed");
120    };
121    return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute, output);
122}
123
124napi_value CalendarNapi::AddEvents(napi_env env, napi_callback_info info)
125{
126    LOG_INFO("AddEvents");
127    struct AddEventsContext : public ContextBase {
128        std::vector<Event> events;
129        int count;
130    };
131    auto ctxt = std::make_shared<AddEventsContext>();
132    auto input = [env, ctxt](size_t argc, napi_value* argv) {
133        // required atleast 1 arguments :: <eventFilter>
134        CHECK_ARGS_RETURN_VOID(ctxt, argc == 1, "invalid arguments!");
135        ctxt->status = NapiUtil::GetValue(env, argv[0], ctxt->events);
136        CHECK_STATUS_RETURN_VOID(ctxt, "GetValue failed!");
137        CHECK_STATUS_RETURN_VOID(ctxt, "invalid arg[0], i.e. invalid keys!");
138    };
139    ctxt->GetCbInfo(env, info, input);
140    auto execute = [ctxt]() {
141        auto calendar = reinterpret_cast<CalendarNapi*>(ctxt->native);
142        CHECK_RETURN_VOID(calendar != nullptr, "CalendarNapi nullptr");
143        auto nativeCalendar = calendar->GetNative();
144        CHECK_RETURN_VOID(nativeCalendar != nullptr, "nativeCalendar nullptr");
145        ctxt->count = nativeCalendar->AddEvents(ctxt->events);
146        ctxt->status = (ctxt->count > 0) ? napi_ok : napi_generic_failure;
147        CHECK_STATUS_RETURN_VOID(ctxt, "AddEvent failed!");
148    };
149    return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute);
150}
151
152napi_value CalendarNapi::DeleteEvent(napi_env env, napi_callback_info info)
153{
154    LOG_INFO("DeleteEvent");
155    struct DeleteEventContext : public ContextBase {
156        bool result;
157        int eventId;
158    };
159    auto ctxt = std::make_shared<DeleteEventContext>();
160    auto input = [env, ctxt](size_t argc, napi_value* argv) {
161        // required atleast 1 arguments :: <number>
162        CHECK_ARGS_RETURN_VOID(ctxt, argc == 1, "invalid arguments!");
163        ctxt->status = NapiUtil::GetValue(env, argv[0], ctxt->eventId);
164        CHECK_STATUS_RETURN_VOID(ctxt, "invalid arg[0], i.e. invalid keys!");
165    };
166    ctxt->GetCbInfo(env, info, input);
167    auto execute = [ctxt]() {
168        auto calendar = reinterpret_cast<CalendarNapi*>(ctxt->native);
169        CHECK_RETURN_VOID(calendar != nullptr, "CalendarNapi nullptr");
170        auto nativeCalendar = calendar->GetNative();
171        CHECK_RETURN_VOID(nativeCalendar != nullptr, "nativeCalendar nullptr");
172        ctxt->result = nativeCalendar->DeleteEvent(ctxt->eventId);
173    };
174    return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute);
175}
176
177napi_value CalendarNapi::DeleteEvents(napi_env env, napi_callback_info info)
178{
179    LOG_INFO("DeleteEvents");
180    struct DeleteEventsContext : public ContextBase {
181        int result;
182        std::vector<int> ids;
183    };
184    auto ctxt = std::make_shared<DeleteEventsContext>();
185    auto input = [env, ctxt](size_t argc, napi_value* argv) {
186        // required atleast 1 arguments :: <number>
187        CHECK_ARGS_RETURN_VOID(ctxt, argc == 1, "invalid arguments!");
188        ctxt->status = NapiUtil::GetValue(env, argv[0], ctxt->ids);
189        CHECK_STATUS_RETURN_VOID(ctxt, "invalid arg[0], i.e. invalid keys!");
190    };
191    ctxt->GetCbInfo(env, info, input);
192    auto execute = [ctxt]() {
193        auto calendar = reinterpret_cast<CalendarNapi*>(ctxt->native);
194        CHECK_RETURN_VOID(calendar != nullptr, "CalendarNapi nullptr");
195        auto nativeCalendar = calendar->GetNative();
196        CHECK_RETURN_VOID(nativeCalendar != nullptr, "nativeCalendar nullptr");
197        ctxt->result = nativeCalendar->DeleteEvents(ctxt->ids);
198    };
199    return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute);
200}
201
202napi_value CalendarNapi::UpdateEvent(napi_env env, napi_callback_info info)
203{
204    LOG_INFO("UpdateEvent");
205    struct UpdateEventContext : public ContextBase {
206        bool result;
207        Event event;
208    };
209    auto ctxt = std::make_shared<UpdateEventContext>();
210    auto input = [env, ctxt](size_t argc, napi_value* argv) {
211        // required atleast 1 arguments :: <number>
212        CHECK_ARGS_RETURN_VOID(ctxt, argc == 1, "invalid arguments!");
213        ctxt->status = NapiUtil::GetValue(env, argv[0], ctxt->event);
214        CHECK_STATUS_RETURN_VOID(ctxt, "invalid arg[0], i.e. invalid keys!");
215    };
216    ctxt->GetCbInfo(env, info, input);
217    auto execute = [ctxt]() {
218        auto calendar = reinterpret_cast<CalendarNapi*>(ctxt->native);
219        CHECK_RETURN_VOID(calendar != nullptr, "CalendarNapi nullptr");
220        auto nativeCalendar = calendar->GetNative();
221        CHECK_RETURN_VOID(nativeCalendar != nullptr, "nativeCalendar nullptr");
222        ctxt->result = nativeCalendar->UpdateEvent(ctxt->event);
223    };
224    return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute);
225}
226
227napi_value CalendarNapi::UpdateEvents(napi_env env, napi_callback_info info)
228{
229    LOG_INFO("UpdateEvents");
230    struct DeleteEventsContext : public ContextBase {
231        int result;
232        std::vector<Event> events;
233        CalendarNapi *calendar;
234    };
235    auto ctxt = std::make_shared<DeleteEventsContext>();
236    auto input = [env, ctxt](size_t argc, napi_value* argv) {
237        // required atleast 1 arguments :: <number>
238        CHECK_ARGS_RETURN_VOID(ctxt, argc == 1, "invalid arguments!");
239        ctxt->status = NapiUtil::GetValue(env, argv[0], ctxt->events);
240        CHECK_STATUS_RETURN_VOID(ctxt, "invalid arg[0], i.e. invalid keys!");
241    };
242    ctxt->GetCbInfo(env, info, input);
243    auto execute = [ctxt]() {
244        auto calendar = reinterpret_cast<CalendarNapi*>(ctxt->native);
245        CHECK_RETURN_VOID(calendar != nullptr, "CalendarNapi nullptr");
246        auto nativeCalendar = calendar->GetNative();
247        CHECK_RETURN_VOID(nativeCalendar != nullptr, "nativeCalendar nullptr");
248        ctxt->result = nativeCalendar->UpdateEvents(ctxt->events);
249    };
250    return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute);
251}
252
253napi_value CalendarNapi::GetEvents(napi_env env, napi_callback_info info)
254{
255    struct GetEventsContext : public ContextBase {
256        EventFilterNapi* eventFilter;
257        std::vector<std::string> eventKeys;
258        std::vector<Event> events;
259    };
260    auto ctxt = std::make_shared<GetEventsContext>();
261    auto input = [env, ctxt](size_t argc, napi_value* argv) {
262        CHECK_ARGS_RETURN_VOID(ctxt, argc <= 2, "invalid arguments!");
263        if (argc >= 1) {
264            napi_valuetype type = napi_undefined;
265            napi_typeof(env, argv[0], &type);
266            CHECK_ARGS_RETURN_VOID(ctxt, type == napi_object, "type error!");
267            ctxt->status = NapiUtil::GetValue(env, argv[0], ctxt->eventFilter);
268            CHECK_STATUS_RETURN_VOID(ctxt, "invalid arg[0], i.e. invalid keys!");
269        }
270        if (argc == 2) {
271            // required atleast 2 arguments :: <eventKey>
272            napi_valuetype type = napi_undefined;
273            napi_typeof(env, argv[0], &type);
274            ctxt->status = NapiUtil::GetValue(env, argv[1], ctxt->eventKeys);
275            CHECK_STATUS_RETURN_VOID(ctxt, "invalid arg[1], i.e. invalid keys!");
276        }
277    };
278    ctxt->GetCbInfo(env, info, input);
279
280    auto execute = [ctxt]() {
281        std::shared_ptr<Native::EventFilter> nativeFilter = nullptr;
282        if (ctxt->eventFilter != nullptr) {
283            nativeFilter = ctxt->eventFilter->GetNative();
284        }
285        auto calendar = reinterpret_cast<CalendarNapi*>(ctxt->native);
286        CHECK_RETURN_VOID(calendar != nullptr, "CalendarNapi nullptr");
287        auto nativeCalendar = calendar->GetNative();
288        CHECK_RETURN_VOID(nativeCalendar != nullptr, "nativeCalendar nullptr");
289        ctxt->events = nativeCalendar->GetEvents(nativeFilter, ctxt->eventKeys);
290        ctxt->status = (true) ? napi_ok : napi_generic_failure;
291        CHECK_STATUS_RETURN_VOID(ctxt, "GetEvents failed!");
292    };
293    auto output = [env, ctxt](napi_value& result) {
294        ctxt->status = NapiUtil::SetValue(ctxt->env, ctxt->events, result);
295        CHECK_STATUS_RETURN_VOID(ctxt, "output failed");
296    };
297    return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute, output);
298}
299
300napi_value CalendarNapi::GetConfig(napi_env env, napi_callback_info info)
301{
302    LOG_INFO("GetConfig");
303    size_t argc = 0;
304    napi_value thisVar = nullptr;
305    auto status = napi_get_cb_info(env, info, &argc, nullptr, &thisVar, nullptr);
306    if (status != napi_ok) {
307        LOG_ERROR("GetConfig napi_get_cb_info failed %{public}d", status);
308        return nullptr;
309    }
310    CalendarNapi *calendarNapi = nullptr;
311    status = napi_unwrap(env, thisVar, (void **)&calendarNapi);
312    if (status != napi_ok) {
313        LOG_ERROR("GetConfig napi_unwrap failed %{public}d", status);
314        return nullptr;
315    }
316    if (calendarNapi == nullptr) {
317        LOG_ERROR("GetConfig reinterpret_cast failed");
318        return nullptr;
319    }
320    auto nativeCalendar = calendarNapi->GetNative();
321    CHECK_RETURN(nativeCalendar != nullptr, "GetConfig -> get nativeCalendar nullptr", nullptr);
322    auto config = nativeCalendar->GetConfig();
323    LOG_DEBUG("config.enableReminder:%{public}d", config.enableReminder.value_or(-1));
324    if (std::get_if<1>(&config.color)) {
325        LOG_DEBUG("config.color:%{public}s", std::to_string(std::get<1>(config.color)).c_str());
326    } else {
327        LOG_ERROR("config.color is null");
328    }
329    napi_value result;
330    status = NapiUtil::SetValue(env, config, result);
331    if (status != napi_ok) {
332        LOG_ERROR("SetValue failed %{public}d", status);
333        return nullptr;
334    }
335    return result;
336}
337
338napi_value CalendarNapi::SetConfig(napi_env env, napi_callback_info info)
339{
340    LOG_INFO("SetConfig");
341    struct SetConfigContext : public ContextBase {
342        int result;
343        CalendarConfig config;
344        CalendarNapi *calendar;
345    };
346    auto ctxt = std::make_shared<SetConfigContext>();
347    auto input = [env, ctxt](size_t argc, napi_value* argv) {
348        // required atleast 1 arguments :: <CalendarConfig>
349        CHECK_ARGS_RETURN_VOID(ctxt, argc == 1, "invalid arguments!");
350        ctxt->status = NapiUtil::GetValue(env, argv[0], ctxt->config);
351        CHECK_STATUS_RETURN_VOID(ctxt, "invalid arg[0], i.e. invalid config!");
352    };
353    ctxt->GetCbInfo(env, info, input);
354    auto execute = [ctxt]() {
355        auto calendar = reinterpret_cast<CalendarNapi*>(ctxt->native);
356        CHECK_RETURN_VOID(calendar != nullptr, "CalendarNapi nullptr");
357        auto nativeCalendar = calendar->GetNative();
358        CHECK_RETURN_VOID(nativeCalendar != nullptr, "nativeCalendar nullptr");
359        ctxt->result = nativeCalendar->SetConfig(ctxt->config);
360    };
361    return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute);
362}
363
364napi_value CalendarNapi::GetAccount(napi_env env, napi_callback_info info)
365{
366    LOG_INFO("GetAccount");
367    size_t argc = 0;
368    napi_value thisVar = nullptr;
369    auto status = napi_get_cb_info(env, info, &argc, nullptr, &thisVar, nullptr);
370    if (status != napi_ok) {
371        LOG_ERROR("GetAccount napi_get_cb_info failed %{public}d", status);
372        return nullptr;
373    }
374    CalendarNapi *calendarNapi = nullptr;
375    status = napi_unwrap(env, thisVar, (void **)&calendarNapi);
376    if (status != napi_ok) {
377        LOG_ERROR("GetAccount napi_unwrap failed %{public}d", status);
378        return nullptr;
379    }
380    if (calendarNapi == nullptr) {
381        LOG_ERROR("GetAccount reinterpret_cast failed");
382        return nullptr;
383    }
384    auto nativeCalendar = calendarNapi->GetNative();
385    CHECK_RETURN(nativeCalendar != nullptr, "GetAccount -> get nativeCalendar nullptr", nullptr);
386    auto account = nativeCalendar->GetAccount();
387    LOG_DEBUG("account.name:%{private}s", account.name.c_str());
388    LOG_DEBUG("account.type:%{private}s", account.type.c_str());
389    if (account.displayName) {
390        LOG_DEBUG("account.displayName:%{private}s", account.displayName.value().c_str());
391    }
392    napi_value result;
393    status = NapiUtil::SetValue(env, account, result);
394    if (status != napi_ok) {
395        LOG_ERROR("SetValue failed %{public}d", status);
396        return nullptr;
397    }
398    return result;
399}
400
401napi_value CalendarAccountConstructor(napi_env env, napi_callback_info info)
402{
403    napi_value thisArg = nullptr;
404    void* data = nullptr;
405
406    napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, &data);
407
408    napi_value global = nullptr;
409    napi_get_global(env, &global);
410
411    return thisArg;
412}
413
414static void CalendarAccountInit(napi_env env, napi_value exports)
415{
416    napi_value name = nullptr;
417    napi_value type = nullptr;
418    napi_value displayName = nullptr;
419
420    napi_create_string_utf8(env, "name", NAPI_AUTO_LENGTH, &name);
421    napi_create_string_utf8(env, "type", NAPI_AUTO_LENGTH, &type);
422    napi_create_string_utf8(env, "displayName", NAPI_AUTO_LENGTH, &displayName);
423
424    napi_property_descriptor calendarAccountProperties[] = {
425        DECLARE_NAPI_PROPERTY("name", name),
426        DECLARE_NAPI_PROPERTY("type", type),
427        DECLARE_NAPI_PROPERTY("displayName", displayName),
428    };
429    napi_value result = nullptr;
430    napi_define_class(env, "CalendarAccount", NAPI_AUTO_LENGTH, CalendarAccountConstructor, nullptr,
431        sizeof(calendarAccountProperties) / sizeof(napi_property_descriptor), calendarAccountProperties, &result);
432    napi_set_named_property(env, exports, "CalendarAccount", result);
433}
434
435napi_value CalendarNapi::Init(napi_env env, napi_value exports)
436{
437    auto status = napi_set_named_property(env, exports, "Calendar", CalendarNapi::Constructor(env));
438    LOG_INFO("init Calendar %{public}d", status);
439    CalendarAccountInit(env, exports);
440    return exports;
441}
442}