// Implements an unsigned long.
// This is a subset of https://github.com/dcodeIO/Long.js
// `| 0` assures the runtime that we are using integer arithmetic

// This works... but don't try to compare one to a real Long.js Long!
// For internal use only.
export class Long {
  low: number;
  high: number;
  __isUnsignedLong__: boolean;

  static isLong(obj: Long) {
    return (obj && obj.__isUnsignedLong__) === true;
  }

  constructor(low: number, high: number) {
    this.low = low | 0;
    this.high = high | 0;
    this.__isUnsignedLong__ = true;
  }

  // prettier-ignore
  static fromBytesLE(bytes: number[]): Long {
    return new Long(
      bytes[0] |
      bytes[1] << 8 |
      bytes[2] << 16 |
      bytes[3] << 24,
      bytes[4] |
      bytes[5] << 8 |
      bytes[6] << 16 |
      bytes[7] << 24,
    );
  }

  // prettier-ignore
  toBytesLE() {
    const hi = this.high;
    const lo = this.low;
    return [
      lo & 0xff,
      lo >>> 8 & 0xff,
      lo >>> 16 & 0xff,
      lo >>> 24,
      hi & 0xff,
      hi >>> 8 & 0xff,
      hi >>> 16 & 0xff,
      hi >>> 24
    ];
  }

  static fromNumber(value: number) {
    if (isNaN(value)) return UZERO;
    if (value < 0) return UZERO;
    if (value >= TWO_PWR_64_DBL) return MAX_UNSIGNED_VALUE;
    return new Long(value % TWO_PWR_32_DBL | 0, (value / TWO_PWR_32_DBL) | 0);
  }

  equals(other: Long) {
    if (!Long.isLong(other)) other = Long.fromValue(other);
    if (this.high >>> 31 === 1 && other.high >>> 31 === 1) return false;
    return this.high === other.high && this.low === other.low;
  }

  notEquals(other: Long) {
    return !this.equals(other);
  }

  comp(other: Long) {
    if (!Long.isLong(other)) other = Long.fromValue(other);
    if (this.equals(other)) return 0;
    return other.high >>> 0 > this.high >>> 0 ||
      (other.high === this.high && other.low >>> 0 > this.low >>> 0)
      ? -1
      : 1;
  }

  lessThanOrEqual(other: Long) {
    return this.comp(/* validates */ other) <= 0;
  }

  static fromValue(val: any) {
    if (typeof val === "number") return Long.fromNumber(val);
    // Throws for non-objects, converts non-instanceof Long:
    return new Long(val.low, val.high);
  }
}

const UZERO = new Long(0, 0);
const TWO_PWR_16_DBL = 1 << 16;
const TWO_PWR_32_DBL = TWO_PWR_16_DBL * TWO_PWR_16_DBL;
const TWO_PWR_64_DBL = TWO_PWR_32_DBL * TWO_PWR_32_DBL;
const MAX_UNSIGNED_VALUE = new Long(0xffffffff | 0, 0xffffffff | 0);
