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  List: 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 fastList: Object = undefined;
27let arkPritvate: ArkPrivate = globalThis.ArkPrivate || undefined;
28if (arkPritvate !== undefined) {
29  fastList = arkPritvate.Load(arkPritvate.List);
30} else {
31  flag = true;
32}
33declare function requireNapi(s: string): errorUtil;
34if (flag || fastList === undefined) {
35  const errorUtil = requireNapi('util.struct');
36  class HandlerList<T> {
37    get(obj: List<T>, prop: string): T {
38      if (typeof prop === 'symbol') {
39        return obj[prop];
40      }
41      let index: number = Number.parseInt(prop);
42      if (Number.isInteger(index)) {
43        errorUtil.checkRangeError('index', index, 0, obj.length - 1);
44        return obj.get(index);
45      }
46      return obj[prop];
47    }
48    set(obj: List<T>, prop: string, value: T): boolean {
49      if (prop === 'elementNum' ||
50        prop === 'capacity' ||
51        prop === 'head' ||
52        prop === 'next') {
53        obj[prop] = value;
54        return true;
55      }
56      let index: number = Number.parseInt(prop);
57      if (Number.isInteger(index)) {
58        errorUtil.checkRangeError('index', index, 0, obj.length);
59        obj.set(index, value);
60        return true;
61      }
62      return false;
63    }
64    deleteProperty(obj: List<T>, prop: string): boolean {
65      let index: number = Number.parseInt(prop);
66      if (Number.isInteger(index)) {
67        errorUtil.checkRangeError('index', index, 0, obj.length - 1);
68        obj.removeByIndex(index);
69        return true;
70      }
71      return false;
72    }
73    has(obj: List<T>, prop: T): boolean {
74      return obj.has(prop);
75    }
76    ownKeys(obj: List<T>): Array<string> {
77      let keys: Array<string> = [];
78      for (let i: number = 0; i < obj.length; i++) {
79        keys.push(i.toString());
80      }
81      return keys;
82    }
83    defineProperty(): boolean {
84      return true;
85    }
86    getOwnPropertyDescriptor(obj: List<T>, prop: string): Object {
87      let index: number = Number.parseInt(prop);
88      if (Number.isInteger(index)) {
89        errorUtil.checkRangeError('index', index, 0, obj.length - 1);
90        return Object.getOwnPropertyDescriptor(obj, prop);
91      }
92      return Object;
93    }
94    setPrototypeOf(): T {
95      throw new Error(`Can't setPrototype on List Object`);
96    }
97  }
98  interface IterableIterator<T> {
99    next: () => {
100      value: T;
101      done: boolean;
102    };
103  }
104  class NodeObj<T> {
105    element: T;
106    next?: NodeObj<T>;
107    constructor(element: T, next?: NodeObj<T>) {
108      this.element = element;
109      this.next = next;
110    }
111  }
112  class List<T> {
113    private head: NodeObj<T>;
114    private elementNum: number;
115    private capacity: number;
116    constructor() {
117      errorUtil.checkNewTargetIsNullError('List', !new.target);
118      this.head = undefined;
119      this.elementNum = 0;
120      this.capacity = 10;
121      return new Proxy(this, new HandlerList());
122    }
123    get length(): number {
124      return this.elementNum;
125    }
126    private getNode(index: number): NodeObj<T> | undefined {
127      if (index >= 0 && index < this.elementNum) {
128        let current: NodeObj<T> = this.head;
129        for (let i: number = 0; i < index; i++) {
130          if (current !== undefined) {
131            current = current.next;
132          }
133        }
134        return current;
135      }
136      return undefined;
137    }
138    get(index: number): T {
139      errorUtil.checkBindError('get', List, this);
140      errorUtil.checkTypeError('index', 'Integer', index);
141      if (index >= 0 && index < this.elementNum) {
142        let current: NodeObj<T> = this.head;
143        for (let i: number = 0; i < index && current !== undefined; i++) {
144          current = current.next;
145        }
146        return current.element;
147      }
148      return undefined;
149    }
150    add(element: T): boolean {
151      errorUtil.checkBindError('add', List, this);
152      let node: NodeObj<T> = new NodeObj(element);
153      if (this.head === undefined) {
154        this.head = node;
155      } else {
156        let current: NodeObj<T> = this.head;
157        while (current.next !== undefined) {
158          current = current.next;
159        }
160        current.next = node;
161      }
162      this.elementNum++;
163      return true;
164    }
165    clear(): void {
166      errorUtil.checkBindError('clear', List, this);
167      this.head = undefined;
168      this.elementNum = 0;
169    }
170    has(element: T): boolean {
171      errorUtil.checkBindError('has', List, this);
172      if (this.head !== undefined) {
173        if (this.head.element === element) {
174          return true;
175        }
176        let current: NodeObj<T> = this.head;
177        while (current.next !== undefined) {
178          current = current.next;
179          if (current.element === element) {
180            return true;
181          }
182        }
183      }
184      return false;
185    }
186    equal(obj: Object): boolean {
187      errorUtil.checkBindError('equal', List, this);
188      if (obj === this) {
189        return true;
190      }
191      if (!(obj instanceof List)) {
192        return false;
193      } else {
194        let e1: NodeObj<T> = this.head;
195        let e2: NodeObj<T> = obj.head;
196        if (e1 !== undefined && e2 !== undefined) {
197          while (e1.next !== undefined && e2.next !== undefined) {
198            e1 = e1.next;
199            e2 = e2.next;
200            if (e1.element !== e2.element) {
201              return false;
202            }
203          }
204          return !(e1.next !== undefined || e2.next !== undefined);
205        } else if (e1 !== undefined && e2 === undefined) {
206          return false;
207        } else if (e1 === undefined && e2 !== undefined) {
208          return false;
209        } else {
210          return true;
211        }
212      }
213    }
214    getIndexOf(element: T): number {
215      errorUtil.checkBindError('getIndexOf', List, this);
216      for (let i: number = 0; i < this.elementNum; i++) {
217        let curNode: NodeObj<T> = undefined;
218        curNode = this.getNode(i);
219        if (curNode !== undefined && curNode.element === element) {
220          return i;
221        }
222      }
223      return -1;
224    }
225    getLastIndexOf(element: T): number {
226      errorUtil.checkBindError('getLastIndexOf', List, this);
227      for (let i: number = this.elementNum - 1; i >= 0; i--) {
228        let curNode: NodeObj<T> = undefined;
229        curNode = this.getNode(i);
230        if (curNode !== undefined && curNode.element === element) {
231          return i;
232        }
233      }
234      return -1;
235    }
236    removeByIndex(index: number): T {
237      errorUtil.checkBindError('removeByIndex', List, this);
238      errorUtil.checkTypeError('index', 'Integer', index);
239      errorUtil.checkRangeError('index', index, 0, this.elementNum - 1);
240      let oldNode: NodeObj<T> = this.head;
241      if (index === 0) {
242        oldNode = this.head;
243        this.head = oldNode && oldNode.next;
244      } else {
245        let prevNode: NodeObj<T> = undefined;
246        prevNode = this.getNode(index - 1);
247        oldNode = prevNode.next;
248        prevNode.next = oldNode.next;
249      }
250      this.elementNum--;
251      return oldNode && oldNode.element;
252    }
253    remove(element: T): boolean {
254      errorUtil.checkBindError('remove', List, this);
255      if (this.has(element)) {
256        let index: number = 0;
257        index = this.getIndexOf(element);
258        this.removeByIndex(index);
259        return true;
260      }
261      return false;
262    }
263    replaceAllElements(callbackfn: (value: T, index?: number, list?: List<T>) => T,
264      thisArg?: Object): void {
265      errorUtil.checkBindError('replaceAllElements', List, this);
266      errorUtil.checkTypeError('callbackfn', 'callable', callbackfn);
267      let index: number = 0;
268      if (this.head !== undefined) {
269        let current: NodeObj<T> = this.head;
270        if (this.elementNum > 0) {
271          this.getNode(index).element = callbackfn.call(thisArg, this.head.element, index, this);
272        }
273        while (current.next !== undefined) {
274          current = current.next;
275          this.getNode(++index).element = callbackfn.call(thisArg, current.element, index, this);
276        }
277      }
278    }
279    getFirst(): T {
280      errorUtil.checkBindError('getFirst', List, this);
281      if (this.isEmpty()) {
282        return undefined;
283      }
284      let element: T = this.head.element;
285      return element;
286    }
287    getLast(): T {
288      errorUtil.checkBindError('getLast', List, this);
289      if (this.isEmpty()) {
290        return undefined;
291      }
292      let newNode: NodeObj<T> = undefined;
293      newNode = this.getNode(this.elementNum - 1);
294      let element: T = newNode.element;
295      return element;
296    }
297    insert(element: T, index: number): void {
298      errorUtil.checkBindError('insert', List, this);
299      errorUtil.checkTypeError('index', 'Integer', index);
300      errorUtil.checkRangeError('index', index, 0, this.elementNum);
301      let newNode: NodeObj<T> = undefined;
302      newNode = new NodeObj(element);
303      if (index === 0) {
304        let current: NodeObj<T> = this.head;
305        newNode.next = current;
306        this.head = newNode;
307      } else {
308        let prevNode: NodeObj<T> = undefined;
309        prevNode = this.getNode(index - 1);
310        newNode.next = prevNode.next;
311        prevNode.next = newNode;
312      }
313      this.elementNum++;
314    }
315    set(index: number, element: T): T {
316      errorUtil.checkBindError('set', List, this);
317      errorUtil.checkTypeError('index', 'Integer', index);
318      errorUtil.checkRangeError('index', index, 0, this.length - 1);
319      let current: NodeObj<T> = undefined;
320      current = this.getNode(index);
321      current.element = element;
322      return current.element;
323    }
324    sort(comparator: (firstValue: T, secondValue: T) => number): void {
325      errorUtil.checkBindError('sort', List, this);
326      errorUtil.checkTypeError('comparator', 'callable', comparator);
327      let isSort: boolean = true;
328      for (let i: number = 0; i < this.elementNum; i++) {
329        for (let j: number = 0; j < this.elementNum - 1 - i; j++) {
330          if (
331            comparator(this.getNode(j).element, this.getNode(j + 1).element) > 0
332          ) {
333            isSort = false;
334            let temp: T = undefined;
335            temp = this.getNode(j).element;
336            this.getNode(j).element = this.getNode(j + 1).element;
337            this.getNode(j + 1).element = temp;
338          }
339        }
340        if (isSort) {
341          break;
342        }
343      }
344    }
345    getSubList(fromIndex: number, toIndex: number): List<T> {
346      errorUtil.checkBindError('getSubList', List, this);
347      errorUtil.checkTypeError('fromIndex', 'Integer', fromIndex);
348      errorUtil.checkTypeError('toIndex', 'Integer', toIndex);
349      errorUtil.checkRangeError('fromIndex', fromIndex, 0,
350        (toIndex > this.elementNum) ? this.elementNum - 1 : toIndex - 1);
351      errorUtil.checkRangeError('toIndex', toIndex, 0, this.elementNum);
352      let list: List<T> = new List<T>();
353      for (let i: number = fromIndex; i < toIndex; i++) {
354        let element: T = undefined;
355        element = this.getNode(i).element;
356        list.add(element);
357        if (element === undefined) {
358          break;
359        }
360      }
361      return list;
362    }
363    convertToArray(): Array<T> {
364      errorUtil.checkBindError('convertToArray', List, this);
365      let arr: Array<T> = [];
366      let index: number = 0;
367      if (this.elementNum <= 0) {
368        return arr;
369      }
370      if (this.head !== undefined) {
371        let current: NodeObj<T> = this.head;
372        arr[index] = this.head.element;
373        while (current.next !== undefined) {
374          current = current.next;
375          arr[++index] = current.element;
376        }
377      }
378      return arr;
379    }
380    isEmpty(): boolean {
381      errorUtil.checkBindError('isEmpty', List, this);
382      return this.elementNum === 0;
383    }
384    forEach(callbackfn: (value: T, index?: number, list?: List<T>) => void,
385      thisArg?: Object): void {
386      errorUtil.checkBindError('forEach', List, this);
387      errorUtil.checkTypeError('callbackfn', 'callable', callbackfn);
388      let index: number = 0;
389      if (this.head !== undefined) {
390        let current: NodeObj<T> = this.head;
391        if (this.elementNum > 0) {
392          callbackfn.call(thisArg, this.head.element, index, this);
393        }
394        while (current.next !== undefined) {
395          current = current.next;
396          callbackfn.call(thisArg, current.element, ++index, this);
397        }
398      }
399    }
400    [Symbol.iterator](): IterableIterator<T> {
401      errorUtil.checkBindError('Symbol.iterator', List, this);
402      let count: number = 0;
403      return {
404        next: function (): { done: boolean, value: T } {
405          let done: boolean = false;
406          let value: T = undefined;
407          done = count >= this.elementNum;
408          value = done ? undefined : this.getNode(count++).element;
409          return {
410            done: done,
411            value: value,
412          };
413        },
414      };
415    }
416  }
417  Object.freeze(List);
418  fastList = List;
419}
420export default fastList;
421