1/*
2 * Copyright (c) 2021 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  ArrayList: 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 fastArrayList: Object = undefined;
27let arkPritvate: ArkPrivate = globalThis.ArkPrivate || undefined;
28if (arkPritvate !== undefined) {
29  fastArrayList = arkPritvate.Load(arkPritvate.ArrayList);
30} else {
31  flag = true;
32}
33declare function requireNapi(s: string): errorUtil;
34if (flag || fastArrayList === undefined) {
35  const errorUtil = requireNapi('util.struct');
36  class HandlerArrayList<T> {
37    private isOutBounds(obj: ArrayList<T>, prop: string): void {
38      let index: number = Number.parseInt(prop);
39      if (Number.isInteger(index)) {
40        errorUtil.checkRangeError('index', index, 0, obj.length - 1);
41      }
42    }
43    get(obj: ArrayList<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: ArrayList<T>, prop: any, value: T): boolean {
51      if (prop === 'elementNum' || prop === 'capacity') {
52        obj[prop] = value;
53        return true;
54      }
55      let index: number = Number.parseInt(prop);
56      if (Number.isInteger(index)) {
57        errorUtil.checkRangeError('index', index, 0, obj.length);
58        obj[index] = value;
59        return true;
60      }
61      return false;
62    }
63    deleteProperty(obj: ArrayList<T>, prop: string): boolean {
64      this.isOutBounds(obj, prop);
65      let index: number = Number.parseInt(prop);
66      if (index >= 0 && index < obj.length && Number.isInteger(index)) {
67        obj.removeByIndex(index);
68        return true;
69      }
70      return false;
71    }
72    has(obj: ArrayList<T>, prop: T): boolean {
73      return obj.has(prop);
74    }
75    ownKeys(obj: ArrayList<T>): Array<string> {
76      let keys: Array<string> = [];
77      for (let i: number = 0; i < obj.length; i++) {
78        keys.push(i.toString());
79      }
80      return keys;
81    }
82    defineProperty(): boolean {
83      return true;
84    }
85    getOwnPropertyDescriptor(obj: ArrayList<T>, prop: string): Object {
86      this.isOutBounds(obj, prop);
87      let index: number = Number.parseInt(prop);
88      if (index >= 0 && index < obj.length && Number.isInteger(index)) {
89        return Object.getOwnPropertyDescriptor(obj, prop);
90      }
91      return Object;
92    }
93    setPrototypeOf(): T {
94      throw new Error(`Can't setPrototype on ArrayList Object`);
95    }
96  }
97  interface IterableIterator<T> {
98    next: () => {
99      value: T;
100      done: boolean;
101    };
102  }
103  class ArrayList<T> {
104    private elementNum: number = 0;
105    private capacity: number = 10; // 10 : means number
106    constructor() {
107      errorUtil.checkNewTargetIsNullError('ArrayList', !new.target);
108      return new Proxy(this, new HandlerArrayList());
109    }
110    get length(): number {
111      return this.elementNum;
112    }
113    add(element: T): boolean {
114      errorUtil.checkBindError('add', ArrayList, this);
115      if (this.isFull()) {
116        this.resize();
117      }
118      this[this.elementNum++] = element;
119      return true;
120    }
121    insert(element: T, index: number): void {
122      errorUtil.checkBindError('insert', ArrayList, this);
123      errorUtil.checkTypeError('index', 'Integer', index);
124      errorUtil.checkRangeError('index', index, 0, this.elementNum);
125      if (this.isFull()) {
126        this.resize();
127      }
128      for (let i: number = this.elementNum; i > index; i--) {
129        this[i] = this[i - 1];
130      }
131      this[index] = element;
132      this.elementNum++;
133    }
134    has(element: T): boolean {
135      errorUtil.checkBindError('has', ArrayList, this);
136      for (let i: number = 0; i < this.elementNum; i++) {
137        if (this[i] === element) {
138          return true;
139        }
140      }
141      return false;
142    }
143    getIndexOf(element: T): number {
144      errorUtil.checkBindError('getIndexOf', ArrayList, this);
145      for (let i: number = 0; i < this.elementNum; i++) {
146        if (element === this[i]) {
147          return i;
148        }
149      }
150      return -1;
151    }
152    removeByIndex(index: number): T {
153      errorUtil.checkBindError('removeByIndex', ArrayList, this);
154      errorUtil.checkTypeError('index', 'Integer', index);
155      errorUtil.checkRangeError('index', index, 0, this.elementNum - 1);
156      let result: T = this[index];
157      for (let i: number = index; i < this.elementNum - 1; i++) {
158        this[i] = this[i + 1];
159      }
160      this.elementNum--;
161      return result;
162    }
163    remove(element: T): boolean {
164      errorUtil.checkBindError('remove', ArrayList, this);
165      if (this.has(element)) {
166        let index: number = this.getIndexOf(element);
167        for (let i: number = index; i < this.elementNum - 1; i++) {
168          this[i] = this[i + 1];
169        }
170        this.elementNum--;
171        return true;
172      }
173      return false;
174    }
175    getLastIndexOf(element: T): number {
176      errorUtil.checkBindError('getLastIndexOf', ArrayList, this);
177      for (let i: number = this.elementNum - 1; i >= 0; i--) {
178        if (element === this[i]) {
179          return i;
180        }
181      }
182      return -1;
183    }
184    removeByRange(fromIndex: number, toIndex: number): void {
185      errorUtil.checkBindError('removeByRange', ArrayList, this);
186      errorUtil.checkTypeError('fromIndex', 'Integer', fromIndex);
187      errorUtil.checkTypeError('toIndex', 'Integer', toIndex);
188      errorUtil.checkRangeError('fromIndex', fromIndex, 0,
189        (toIndex > this.elementNum) ? this.elementNum - 1 : toIndex - 1);
190      errorUtil.checkRangeError('toIndex', toIndex, 0, this.elementNum);
191      let i: number = fromIndex;
192      for (let j: number = toIndex; j < this.elementNum; j++) {
193        this[i] = this[j];
194        i++;
195      }
196      this.elementNum -= toIndex - fromIndex;
197    }
198    replaceAllElements(callbackfn: (value: T, index?: number, arrList?: ArrayList<T>) => T,
199      thisArg?: Object): void {
200      errorUtil.checkBindError('replaceAllElements', ArrayList, this);
201      errorUtil.checkTypeError('callbackfn', 'callable', callbackfn);
202      for (let i: number = 0; i < this.elementNum; i++) {
203        this[i] = callbackfn.call(thisArg, this[i], i, this);
204      }
205    }
206    forEach(callbackfn: (value: T, index?: number, arrList?: ArrayList<T>) => void,
207      thisArg?: Object): void {
208      errorUtil.checkBindError('forEach', ArrayList, this);
209      errorUtil.checkTypeError('callbackfn', 'callable', callbackfn);
210      for (let i: number = 0; i < this.elementNum; i++) {
211        callbackfn.call(thisArg, this[i], i, this);
212      }
213    }
214    sort(comparator?: (firstValue: T, secondValue: T) => number): void {
215      errorUtil.checkBindError('sort', ArrayList, this);
216      let isSort: boolean = true;
217      if (comparator) {
218        errorUtil.checkTypeError('comparator', 'callable', comparator);
219        for (let i: number = 0; i < this.elementNum; i++) {
220          for (let j: number = 0; j < this.elementNum - 1 - i; j++) {
221            if (comparator(this[j], this[j + 1]) > 0) {
222              isSort = false;
223              let temp: T = this[j];
224              this[j] = this[j + 1];
225              this[j + 1] = temp;
226            }
227          }
228        }
229      } else {
230        for (let i: number = 0; i < this.length - 1; i++) {
231          for (let j: number = 0; j < this.elementNum - 1 - i; j++) {
232            if (this.asciSort(this[j], this[j + 1])) {
233              isSort = false;
234              let temp: T = this[j];
235              this[j] = this[j + 1];
236              this[j + 1] = temp;
237            }
238          }
239          if (isSort) {
240            break;
241          }
242        }
243      }
244    }
245    private asciSort(curElement: string, nextElement: string): boolean {
246      if ((Object.prototype.toString.call(curElement) === '[object String]' ||
247        Object.prototype.toString.call(curElement) === '[object Number]') &&
248        (Object.prototype.toString.call(nextElement) === '[object String]' ||
249          Object.prototype.toString.call(nextElement) === '[object Number]')) {
250        curElement = curElement.toString();
251        nextElement = nextElement.toString();
252        if (curElement > nextElement) {
253          return true;
254        }
255        return false;
256      }
257      return false;
258    }
259    subArrayList(fromIndex: number, toIndex: number): ArrayList<T> {
260      errorUtil.checkBindError('subArrayList', ArrayList, this);
261      errorUtil.checkTypeError('fromIndex', 'Integer', fromIndex);
262      errorUtil.checkTypeError('toIndex', 'Integer', toIndex);
263      errorUtil.checkRangeError('fromIndex', fromIndex, 0,
264        (toIndex > this.elementNum) ? this.elementNum - 1 : toIndex - 1);
265      errorUtil.checkRangeError('toIndex', toIndex, 0, this.elementNum);
266      let arraylist: ArrayList<T> = new ArrayList<T>();
267      for (let i: number = fromIndex; i < toIndex; i++) {
268        arraylist.add(this[i]);
269      }
270      return arraylist;
271    }
272    clear(): void {
273      errorUtil.checkBindError('clear', ArrayList, this);
274      this.elementNum = 0;
275    }
276    clone(): ArrayList<T> {
277      errorUtil.checkBindError('clone', ArrayList, this);
278      let clone: ArrayList<T> = new ArrayList<T>();
279      for (let i: number = 0; i < this.elementNum; i++) {
280        clone.add(this[i]);
281      }
282      return clone;
283    }
284    getCapacity(): number {
285      errorUtil.checkBindError('getCapacity', ArrayList, this);
286      return this.capacity;
287    }
288    convertToArray(): Array<T> {
289      errorUtil.checkBindError('convertToArray', ArrayList, this);
290      let arr: Array<T> = [];
291      for (let i: number = 0; i < this.elementNum; i++) {
292        arr[i] = this[i];
293      }
294      return arr;
295    }
296    private isFull(): boolean {
297      return this.elementNum === this.capacity;
298    }
299    private resize(): void {
300      this.capacity = 1.5 * this.capacity; // 1.5 : means number
301    }
302    isEmpty(): boolean {
303      errorUtil.checkBindError('isEmpty', ArrayList, this);
304      return this.elementNum === 0;
305    }
306    increaseCapacityTo(newCapacity: number): void {
307      errorUtil.checkBindError('increaseCapacityTo', ArrayList, this);
308      errorUtil.checkTypeError('newCapacity', 'Integer', newCapacity);
309      if (newCapacity >= this.elementNum) {
310        this.capacity = newCapacity;
311      }
312    }
313    trimToCurrentLength(): void {
314      errorUtil.checkBindError('trimToCurrentLength', ArrayList, this);
315      this.capacity = this.elementNum;
316    }
317    [Symbol.iterator](): IterableIterator<T> {
318      errorUtil.checkBindError('Symbol.iterator', ArrayList, this);
319      let count: number = 0;
320      return {
321        next: function (): { done: boolean, value: T } {
322          let done: boolean = false;
323          let value: T = undefined;
324          done = count >= this.elementNum;
325          value = done ? undefined : this[count++];
326          return {
327            done: done,
328            value: value,
329          };
330        },
331      };
332    }
333  }
334  Object.freeze(ArrayList);
335  fastArrayList = ArrayList;
336}
337export default fastArrayList;
338