type ListValue = number
type ListValues = ListValue[]

type ConsturctorValue = ListValue | List
type ConsturctorValues = ConsturctorValue[]


export class List {
  public values: ListValue[]
  public constructor(...values: ListValues) { this.values = values }

  public static create = (...values: ConsturctorValues): List => {
    if (values[0] instanceof List) {
      return values[0].append(List.create(...values.slice(1)))
    } else if (typeof values[0] === 'number') {
      return new List(values[0]).append(List.create(...values.slice(1)))
    } else {
      return new List()
    }
  }

  length(): number {
    let count = 0
    for (let _ of this.values) count++
    return count
  }

  append(list: List): List { return new List(...[...this.values, ...list.values]); }

  forEach(callback: (value: unknown) => void): void {
    for (const value of this.values) {
      callback(value)
    }
  }

  concat(...lists: List[]): List {
    if (!lists.length) return this
    return this.append(lists[0]).concat(...lists.slice(1))
  }

  filter<T extends ListValue>(predicate: (value: ListValue) => boolean): List {
    if (!this.length()) return this
    if (predicate(this.values[0])) {
      return List.create(this.values[0]).concat(List.create(...this.values.slice(1)).filter<T>(predicate));
    }
    return List.create(...this.values.slice(1)).filter<T>(predicate)
  }

  map<T extends ListValue>(predicate: (value: ListValue) => ListValue): List {
    if (!this.length()) return this
    return List.create(predicate(this.values[0])).concat(List.create(...this.values.slice(1)).map<T>(predicate));
  }
}
