1/*
2 * Copyright (C) 2022 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#ifndef CALLBACK_MANAGER_H
17#define CALLBACK_MANAGER_H
18
19#include <mutex>
20#include "location_napi_errcode.h"
21
22namespace OHOS {
23namespace Location {
24template <typename T>
25class CallbackManager {
26public:
27    CallbackManager() = default;
28    virtual ~CallbackManager() = default;
29    bool IsCallbackInMap(const napi_env& env, const napi_value& handler);
30    void AddCallback(const napi_env& env, const napi_ref& handlerRef, const sptr<T>& callback);
31    void DeleteCallback(const napi_env& env, const napi_value& handler);
32    sptr<T> GetCallbackPtr(const napi_env& env, const napi_value& handler);
33    void DeleteCallbackByEnv(const napi_env& env);
34    std::map<napi_env, std::map<napi_ref, sptr<T>>> GetCallbackMap();
35    bool RegCallback(const napi_env& env, const size_t argc, const napi_value* argv);
36    LocationErrCode SubscribeChange(const napi_env& env, const napi_ref& handlerRef, sptr<T>& callbackHost);
37private:
38    std::map<napi_env, std::map<napi_ref, sptr<T>>> callbackMap_;
39    std::mutex mutex_;
40};
41
42template <typename T>
43std::map<napi_env, std::map<napi_ref, sptr<T>>> CallbackManager<T>::GetCallbackMap()
44{
45    std::unique_lock<std::mutex> lock(mutex_);
46    return callbackMap_;
47}
48
49template <typename T>
50void CallbackManager<T>::DeleteCallbackByEnv(const napi_env& env)
51{
52    std::unique_lock<std::mutex> lock(mutex_);
53    auto iter = callbackMap_.find(env);
54    if (iter == callbackMap_.end()) {
55        return;
56    }
57    iter->second.clear();
58    callbackMap_.erase(iter);
59}
60
61template <typename T>
62bool CallbackManager<T>::IsCallbackInMap(const napi_env& env, const napi_value& handler)
63{
64    std::unique_lock<std::mutex> lock(mutex_);
65    auto iter = callbackMap_.find(env);
66    if (iter == callbackMap_.end()) {
67        return false;
68    }
69    for (auto innerIter = iter->second.begin(); innerIter != iter->second.end(); innerIter++) {
70        auto ref = innerIter->first;
71        if (IsCallbackEquals(env, handler, ref)) {
72            return true;
73        }
74    }
75    return false;
76}
77
78template <typename T>
79void CallbackManager<T>::AddCallback(const napi_env& env, const napi_ref& handlerRef, const sptr<T>& callback)
80{
81    std::unique_lock<std::mutex> lock(mutex_);
82    auto iter = callbackMap_.find(env);
83    if (iter == callbackMap_.end()) {
84        std::map<napi_ref, sptr<T>> innerMap;
85        innerMap.insert(std::make_pair(handlerRef, callback));
86        callbackMap_.insert(std::make_pair(env, innerMap));
87        return;
88    }
89    iter->second.insert(std::make_pair(handlerRef, callback));
90}
91
92template <typename T>
93void CallbackManager<T>::DeleteCallback(const napi_env& env, const napi_value& handler)
94{
95    std::unique_lock<std::mutex> lock(mutex_);
96    auto iter = callbackMap_.find(env);
97    if (iter == callbackMap_.end()) {
98        return;
99    }
100    for (auto innerIter = iter->second.begin(); innerIter != iter->second.end(); innerIter++) {
101        auto ref = innerIter->first;
102        if (IsCallbackEquals(env, handler, ref)) {
103            innerIter = iter->second.erase(innerIter);
104            if (iter->second.size() == 0) {
105                callbackMap_.erase(iter);
106            }
107            break;
108        }
109    }
110}
111
112template <typename T>
113sptr<T> CallbackManager<T>::GetCallbackPtr(const napi_env& env, const napi_value& handler)
114{
115    std::unique_lock<std::mutex> lock(mutex_);
116    auto iter = callbackMap_.find(env);
117    if (iter == callbackMap_.end()) {
118        return nullptr;
119    }
120    for (auto innerIter = iter->second.begin(); innerIter != iter->second.end(); innerIter++) {
121        auto ref = innerIter->first;
122        if (IsCallbackEquals(env, handler, ref)) {
123            return innerIter->second;
124        }
125    }
126    return nullptr;
127}
128
129template<typename T>
130bool CallbackManager<T>::RegCallback(const napi_env& env, const size_t argc, const napi_value* argv)
131{
132    if (argc != PARAM2) {
133        HandleSyncErrCode(env, ERRCODE_INVALID_PARAM);
134        return false;
135    }
136    if (!CheckIfParamIsFunctionType(env, argv[PARAM1])) {
137        HandleSyncErrCode(env, ERRCODE_INVALID_PARAM);
138        return false;
139    }
140    if (IsCallbackInMap(env, argv[PARAM1])) {
141        LBSLOGE(LOCATION_NAPI, "%{public}s, This request already exists", __func__);
142        return false;
143    }
144    auto callbackHost = sptr<T>(new (std::nothrow) T());
145    if (callbackHost != nullptr) {
146        napi_ref handlerRef = nullptr;
147        NAPI_CALL_BASE(env, napi_create_reference(env, argv[PARAM1], 1, &handlerRef), false);
148
149        LocationErrCode errorCode = SubscribeChange(env, handlerRef, callbackHost);
150        if (errorCode != ERRCODE_SUCCESS) {
151            HandleSyncErrCode(env, errorCode);
152            return false;
153        }
154        AddCallback(env, handlerRef, callbackHost);
155    }
156    return true;
157}
158
159template<typename T>
160LocationErrCode CallbackManager<T>::SubscribeChange(const napi_env& env,
161    const napi_ref& handlerRef, sptr<T>& callbackHost)
162{
163    callbackHost->SetEnv(env);
164    callbackHost->SetHandleCb(handlerRef);
165    return ERRCODE_SUCCESS;
166}
167} // namespace Location
168} // namespace OHOS
169#endif // CALLBACK_MANAGER_H
170