import {
  throwError,
  ErrorEnum,
  ERROR_SUBSCRIPT_OUT_OF_BOUNDS,
  ANY_OBJ,
  ELEMENT_NOT_FOUNT,
} from '@/shared';
export interface ISingleLinkedList<T> {
  
  size: number;
  
  add(element: T, index?: number): void;
  
  get(index: number): T;
  
  set(index: number, element: T): T;
  
  delete(index: number): T;
  
  find(element: T): number;
}
export class SingleLinkedListNode<T> {
  constructor(public val: T, public next: SingleLinkedListNode<T> | null = null) {}
}
export class SingleLinkedList<T> implements ISingleLinkedList<T> {
  private dummyHead = new SingleLinkedListNode(ANY_OBJ);
  private _size: number = 0;
  get size() {
    return this._size;
  }
  add(element: T, index: number = this.size): void {
    this.checkAddRange(index) || throwError(ERROR_SUBSCRIPT_OUT_OF_BOUNDS, ErrorEnum.range);
    let p = this.dummyHead;
    while (index--) p = p.next!;
    const newNode = new SingleLinkedListNode(element, p.next);
    p.next = newNode;
    this._size++;
  }
  set(index: number, element: T): T {
    this.checkRange(index) || throwError(ERROR_SUBSCRIPT_OUT_OF_BOUNDS, ErrorEnum.range);
    let p = this.dummyHead;
    while (index--) p = p.next!;
    const val = p.next!.val;
    p.next!.val = element;
    return val;
  }
  delete(index: number): T {
    this.checkRange(index) || throwError(ERROR_SUBSCRIPT_OUT_OF_BOUNDS, ErrorEnum.range);
    let p = this.dummyHead;
    while (index--) p = p.next!;
    const val = p.next!.val;
    p.next = p.next!.next;
    this._size--;
    return val;
  }
  get(index: number): T {
    this.checkRange(index) || throwError(ERROR_SUBSCRIPT_OUT_OF_BOUNDS, ErrorEnum.range);
    let p = this.dummyHead;
    while (index--) p = p.next!;
    return p.next!.val;
  }
  find(element: T): number {
    let p = this.dummyHead;
    let index = 0;
    while (p.next !== null && p.next.val !== element) {
      p = p.next!;
      index++;
    }
    return p.next ? index : ELEMENT_NOT_FOUNT;
  }
  private checkAddRange(index: number): boolean {
    return this.checkRange(index) || index === this.size;
  }
  private checkRange(index: number): boolean {
    return index >= 0 && index < this._size;
  }
}