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
16import { Log } from '../../utils/Log'
17
18type Callback = (args: any) => void
19
20export class EventBus {
21  private TAG = '[EventBus]:'
22  private events: Map<string, Set<Callback>> = new Map()
23
24  constructor() {
25  }
26
27  /**
28    * Register events and handlers
29    *
30    * @param event event to handle.
31    * @param callback event related callbacck
32    */
33  public on(event: string | string[], callback: Callback): void {
34    Log.info(`${this.TAG} on event = ${JSON.stringify(event)}  ${JSON.stringify(callback)}`);
35    if (Array.isArray(event)) {
36      for (let i = 0, l = event.length; i < l; i++) {
37        this.on(event[i], callback)
38      }
39    } else {
40      let callbacks = this.events.get(event)
41      if (!callbacks) {
42        callbacks = new Set()
43      }
44      callbacks.add(callback)
45      this.events.set(event, callbacks)
46      Log.info(`${this.TAG} on event = ${JSON.stringify(event)}  ${JSON.stringify(this.events.get(event))}`)
47    }
48  }
49
50  /**
51    * Register events and processing functions, and destroy them after triggering once
52    *
53    * @param event event to handle
54    * @param callback event related callback
55    */
56  public once(event: string | string[], callback: Callback): void {
57    const _self = this
58
59    function handler(): void {
60      const args: any = arguments;
61      _self.off(event, handler);
62      callback.apply(_self, args); // When called in emit, it will pass parameters to the on method
63    }
64
65    handler.callback = callback; // off determines the destruction event based on this
66    _self.on(event, handler);
67  }
68
69  /**
70    * Destroy events and handlers
71    *
72    * @param event event to handle
73    * @param callback event related callback
74    */
75  public off(event: string | string[], callback: Callback | undefined): void {
76    // Array cyclic emptying
77    if (Array.isArray(event)) {
78      for (let i = 0, l = event.length; i < l; i++) {
79        this.off(event[i], callback)
80      }
81      return;
82    }
83    const callbacks = this.events.get(event);
84    if (callback == undefined) {
85      callbacks?.clear()
86      return
87    }
88    let cb = null
89    callbacks.forEach((item) => {
90      if (item.name === callback.name) {
91        cb = item
92      }
93    })
94    callbacks?.delete(cb);
95    Log.info(`${this.TAG} off event = ${JSON.stringify(event)}  ${JSON.stringify(this.events.get(event))}`)
96  }
97
98  /**
99    * Trigger all callbacks of an event with parameters
100    *
101    * @param event event to handle
102    * @param argument parameter for the related callback
103    */
104  public emit(event: string, argument: any): void {
105    // once deleting the event will cause this in the following loop this._events moves forward in fn,
106    // so it is copied here as a new array
107    const _self = this
108    Log.info(`${this.TAG} emit event = ${JSON.stringify(event)}`);
109    const tempCall = _self.events.get(event);
110    if (!tempCall) {
111      return
112    }
113    const callbacks = [...tempCall];
114    if (callbacks) {
115      for (let i = 0, l = callbacks.length; i < l; i++) {
116        try {
117          callbacks[i].apply(_self, argument)
118        } catch (e: any) {
119          new Error(e)
120        }
121      }
122    }
123  }
124}