import { Hash } from "@calcit/ternary-tree";

import { CalcitValue } from "./js-primes.mjs";
import { _$n__$e_, newTag, toString } from "./calcit-data.mjs";
import { CalcitRecord } from "./js-record.mjs";

export class CalcitTuple {
  tag: CalcitValue;
  extra: CalcitValue[];
  klass: CalcitRecord;
  cachedHash: Hash;
  constructor(tagName: CalcitValue, extra: CalcitValue[], klass: CalcitRecord) {
    this.tag = tagName;
    this.extra = extra;
    this.klass = klass;
    this.cachedHash = null;
  }
  get(n: number) {
    if (n === 0) {
      return this.tag;
    } else if (n - 1 < this.extra.length) {
      return this.extra[n - 1];
    } else {
      throw new Error("Tuple only have 2 elements");
    }
  }
  assoc(n: number, v: CalcitValue) {
    if (n === 0) {
      return new CalcitTuple(v, this.extra, this.klass);
    } else if (n - 1 < this.extra.length) {
      let next_extra = this.extra.slice();
      next_extra[n - 1] = v;
      return new CalcitTuple(this.tag, next_extra, this.klass);
    } else {
      throw new Error(`Tuple only have ${this.extra.length} elements`);
    }
  }
  count() {
    return 1 + this.extra.length;
  }
  eq(y: CalcitTuple): boolean {
    if (!_$n__$e_(this.tag, y.tag)) {
      return false;
    }
    if (this.extra.length !== y.extra.length) {
      return false;
    }
    for (let idx = 0; idx < this.extra.length; idx++) {
      if (!_$n__$e_(this.extra[idx], y.extra[idx])) {
        return false;
      }
    }
    return true;
  }
  toString(disableJsDataWarning: boolean = false): string {
    let args = [this.tag, ...this.extra];
    let content = "";
    for (let i = 0; i < args.length; i++) {
      if (i > 0) {
        content += " ";
      }
      content += toString(args[i], true, disableJsDataWarning);
    }
    if (this.klass instanceof CalcitRecord) {
      return `(%:: ${content} (:class ${this.klass.name.value}))`;
    } else {
      return `(:: ${content})`;
    }
  }
}
