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  Deque: 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 fastDeque: Object = undefined;
27let arkPritvate: ArkPrivate = globalThis.ArkPrivate || undefined;
28if (arkPritvate !== undefined) {
29  fastDeque = arkPritvate.Load(arkPritvate.Deque);
30} else {
31  flag = true;
32}
33declare function requireNapi(s: string): errorUtil;
34if (flag || fastDeque === undefined) {
35  const errorUtil = requireNapi('util.struct');
36  class HandlerDeque<T> {
37    private isOutBounds(prop: string): void {
38      let index: number = Number.parseInt(prop);
39      if (Number.isInteger(index)) {
40        errorUtil.checkRangeError('index', index, 0);
41      }
42    }
43    get(obj: Deque<T>, prop: string): T {
44      if (typeof prop === 'symbol') {
45        return obj[prop];
46      }
47      this.isOutBounds(prop);
48      return obj[prop];
49    }
50    set(obj: Deque<T>, prop: any, value: T): boolean {
51      if (prop === 'front' || prop === 'capacity' || prop === 'rear') {
52        obj[prop] = value;
53        return true;
54      }
55      let index: number = Number(prop);
56      if (Number.isInteger(index)) {
57        errorUtil.checkRangeError('index', index, 0);
58        obj[index] = value;
59        return true;
60      }
61      return false;
62    }
63    has(obj: Deque<T>, prop: T): boolean {
64      return obj.has(prop);
65    }
66    ownKeys(obj: Deque<T>): Array<string> {
67      let keys: Array<string> = [];
68      for (let i: number = 0; i < obj.length; i++) {
69        keys.push(i.toString());
70      }
71      return keys;
72    }
73    defineProperty(): boolean {
74      return true;
75    }
76    getOwnPropertyDescriptor(obj: Deque<T>, prop: string): Object {
77      this.isOutBounds(prop);
78      let index: number = Number.parseInt(prop);
79      if (index >= 0 && Number.isInteger(index)) {
80        return Object.getOwnPropertyDescriptor(obj, prop);
81      }
82      return Object;
83    }
84    setPrototypeOf(): T {
85      throw new Error(`Can't setPrototype on Deque Object`);
86    }
87  }
88  interface IterableIterator<T> {
89    next: () => {
90      value: T;
91      done: boolean;
92    };
93  }
94  class Deque<T> {
95    private front: number;
96    private capacity: number;
97    private rear: number;
98    constructor() {
99      errorUtil.checkNewTargetIsNullError('Deque', !new.target);
100      this.front = 0;
101      this.capacity = 8;
102      this.rear = 0;
103      return new Proxy(this, new HandlerDeque());
104    }
105    get length(): number {
106      let result: number = 0;
107      result = (this.rear - this.front + this.capacity) % this.capacity;
108      return result;
109    }
110    insertFront(element: T): void {
111      errorUtil.checkBindError('insertFront', Deque, this);
112      if (this.isFull()) {
113        this.increaseCapacity();
114      }
115      this.front = (this.front - 1 + this.capacity) % this.capacity;
116      this[this.front] = element;
117    }
118    insertEnd(element: T): void {
119      errorUtil.checkBindError('insertEnd', Deque, this);
120      if (this.isFull()) {
121        this.increaseCapacity();
122      }
123      this[this.rear] = element;
124      this.rear = (this.rear + 1) % (this.capacity + 1);
125    }
126    getFirst(): T {
127      errorUtil.checkBindError('getFirst', Deque, this);
128      if (this.isEmpty()) {
129        return undefined;
130      }
131      return this[this.front];
132    }
133    getLast(): T {
134      errorUtil.checkBindError('getLast', Deque, this);
135      if (this.isEmpty()) {
136        return undefined;
137      }
138      return this[this.rear - 1];
139    }
140    has(element: T): boolean {
141      errorUtil.checkBindError('has', Deque, this);
142      let result: boolean = false;
143      this.forEach(function (value) {
144        if (value === element) {
145          result = true;
146        }
147      });
148      return result;
149    }
150    popFirst(): T {
151      errorUtil.checkBindError('popFirst', Deque, this);
152      if (this.isEmpty()) {
153        return undefined;
154      }
155      let result: T = undefined;
156      result = this[this.front];
157      this.front = (this.front + 1) % (this.capacity + 1);
158      return result;
159    }
160    popLast(): T {
161      errorUtil.checkBindError('popLast', Deque, this);
162      if (this.isEmpty()) {
163        return undefined;
164      }
165      let result: T = undefined;
166      result = this[this.rear - 1];
167      this.rear = (this.rear + this.capacity) % (this.capacity + 1);
168      return result;
169    }
170    forEach(callbackfn: (value: T, index?: number, deque?: Deque<T>) => void,
171      thisArg?: Object): void {
172      errorUtil.checkBindError('forEach', Deque, this);
173      errorUtil.checkTypeError('callbackfn', 'callable', callbackfn);
174      let k: number = 0;
175      let i: number = this.front;
176      while (true) {
177        callbackfn.call(thisArg, this[i], k, this);
178        i = (i + 1) % this.capacity;
179        k++;
180        if (i === this.rear) {
181          break;
182        }
183      }
184    }
185    private increaseCapacity(): void {
186      let count: number = 0;
187      let arr: Array<T> = [];
188      let length: number = this.length;
189      while (true) {
190        arr[count++] = this[this.front];
191        this.front = (this.front + 1) % this.capacity;
192        if (this.front === this.rear) {
193          break;
194        }
195      }
196      for (let i: number = 0; i < length; i++) {
197        this[i] = arr[i];
198      }
199      this.capacity = 2 * this.capacity; // 2 : means number
200      this.front = 0;
201      this.rear = length;
202    }
203    private isFull(): boolean {
204      return (this.rear + 1) % this.capacity === this.front;
205    }
206    private isEmpty(): boolean {
207      return this.length === 0;
208    }
209    [Symbol.iterator](): IterableIterator<T> {
210      errorUtil.checkBindError('Symbol.iterator', Deque, this);
211      let count: number = this.front;
212      return {
213        next: function (): { done: boolean, value: T } {
214          let done: boolean = false;
215          let value: T = undefined;
216          done = count === this.rear;
217          value = done ? undefined : this[count];
218          count = (count + 1) % this.capacity;
219          return {
220            done: done,
221            value: value,
222          };
223        },
224      };
225    }
226  }
227  Object.freeze(Deque);
228  fastDeque = Deque;
229}
230export default fastDeque;
231