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 */
15interface ArkPrivate {
16  Queue: number;
17  Load(key: number): Object;
18}
19interface errorUtil{
20  checkRangeError(paramName: string, receivedValue: unknown, min?: number, max?: number, options?: string): void;
21  checkNewTargetIsNullError(className: string, isNull: boolean): void;
22  checkBindError(methodName: string, className: Function, self: unknown): void;
23  checkTypeError(paramName: string, type: string, receivedValue: unknown): void;
24}
25let flag: boolean = false;
26let fastQueue: Object = undefined;
27let arkPritvate: ArkPrivate = globalThis.ArkPrivate || undefined;
28if (arkPritvate !== undefined) {
29  fastQueue = arkPritvate.Load(arkPritvate.Queue);
30} else {
31  flag = true;
32}
33declare function requireNapi(s: string): errorUtil;
34if (flag || fastQueue === undefined) {
35  const errorUtil = requireNapi('util.struct');
36  class HandlerQueue<T> {
37    private isOutBounds(obj: Queue<T>, prop: string): void {
38      let index: number = Number.parseInt(prop);
39      if (Number.isInteger(index)) {
40        errorUtil.checkRangeError('index', index, 0, obj.length);
41      }
42    }
43    get(obj: Queue<T>, prop: string): T {
44      if (typeof prop === 'symbol') {
45        return obj[prop];
46      }
47      this.isOutBounds(obj, prop);
48      return obj[prop];
49    }
50    set(obj: Queue<T>, prop: any, value: T): boolean {
51      if (prop === 'front' || prop === 'capacity' || prop === 'rear') {
52        obj[prop] = value;
53        return true;
54      }
55      this.isOutBounds(obj, prop);
56      let index: number = Number(prop);
57      if (index >= 0 && index <= obj.length && Number.isInteger(index)) {
58        obj[index] = value;
59        return true;
60      }
61      return false;
62    }
63    ownKeys(obj: Queue<T>): Array<string> {
64      let keys: string[] = [];
65      for (let i: number = 0; i < obj.length; i++) {
66        keys.push(i.toString());
67      }
68      return keys;
69    }
70    defineProperty(): boolean {
71      return true;
72    }
73    getOwnPropertyDescriptor(obj: Queue<T>, prop: string): Object {
74      this.isOutBounds(obj, prop);
75      let index: number = Number.parseInt(prop);
76      if (index >= 0 && index < obj.length && Number.isInteger(index)) {
77        return Object.getOwnPropertyDescriptor(obj, prop);
78      }
79      return Object;
80    }
81    setPrototypeOf(): T {
82      throw new Error(`Can't setPrototype on Queue Object`);
83    }
84  }
85  interface IterableIterator<T> {
86    next: () => {
87      value: T;
88      done: boolean;
89    };
90  }
91  class Queue<T> {
92    private front: number;
93    private capacity: number;
94    private rear: number;
95    constructor() {
96      errorUtil.checkNewTargetIsNullError('Queue', !new.target);
97      this.front = 0;
98      this.capacity = 8;
99      this.rear = 0;
100      return new Proxy(this, new HandlerQueue());
101    }
102    get length(): number {
103      return this.rear - this.front;
104    }
105    add(element: T): boolean {
106      errorUtil.checkBindError('add', Queue, this);
107      if (this.isFull()) {
108        this.increaseCapacity();
109      }
110      this[this.rear] = element;
111      this.rear = (this.rear + 1) % (this.capacity + 1);
112      return true;
113    }
114    getFirst(): T {
115      errorUtil.checkBindError('getFirst', Queue, this);
116      if (this.isEmpty()) {
117        return undefined;
118      }
119      return this[this.front];
120    }
121    pop(): T {
122      errorUtil.checkBindError('pop', Queue, this);
123      if (this.isEmpty()) {
124        return undefined;
125      }
126      let result: T = undefined;
127      result = this[this.front];
128      this.front = (this.front + 1) % (this.capacity + 1);
129      return result;
130    }
131    forEach(callbackfn: (value: T, index?: number, queue?: Queue<T>) => void,
132      thisArg?: Object): void {
133      errorUtil.checkBindError('forEach', Queue, this);
134      errorUtil.checkTypeError('callbackfn', 'callable', callbackfn);
135      let k: number = 0;
136      let i: number = this.front;
137      if (this.isEmpty()) {
138        return;
139      } else {
140        while (true) {
141          callbackfn.call(thisArg, this[i], k, this);
142          i = (i + 1) % this.capacity;
143          k++;
144          if (i === this.rear) {
145            break;
146          }
147        }
148      }
149    }
150    private isFull(): boolean {
151      return this.length === this.capacity;
152    }
153    private isEmpty(): boolean {
154      return this.length === 0;
155    }
156    [Symbol.iterator](): IterableIterator<T> {
157      errorUtil.checkBindError('Symbol.iterator', Queue, this);
158      let count: number = this.front;
159      return {
160        next: function (): { done: boolean, value: T } {
161          let done: boolean = false;
162          let value: T = undefined;
163          done = count === this.rear;
164          value = done ? undefined : this[count];
165          count = (count + 1) % this.capacity;
166          return {
167            done: done,
168            value: value,
169          };
170        },
171      };
172    }
173    private increaseCapacity(): void {
174      this.capacity = 2 * this.capacity; // 2 : means number
175    }
176  }
177  Object.freeze(Queue);
178  fastQueue = Queue;
179}
180export default fastQueue;
181