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