/**
 * Protobuf binary reader for ArkTS
 *
 * Implements varint/zigzag and fixed-width primitives, plus length-delimited
 * string/bytes. Maintains cursor position for sequential decoding.
 *
 * Version 2.0 - 增强功能:
 * - 递归深度保护(防止栈溢出攻击)
 * - view() 方法(用于获取未知字段的原始数据)
 */
import { decodeUTF8 } from './util'
export class Reader {
  private b: Uint8Array
  pos: number
  len: number

  // 递归深度保护
  private recursionDepth: number = 0
  private readonly maxRecursionDepth: number = 100

  constructor(buf: Uint8Array) {
    this.b = buf
    this.pos = 0
    this.len = buf.length
  }

  /**
   * 增加递归深度
   * @throws 如果超过最大递归深度
   */
  incrementRecursionDepth(): void {
    this.recursionDepth++
    if (this.recursionDepth > this.maxRecursionDepth) {
      throw new Error(`Message nesting depth exceeds limit: ${this.maxRecursionDepth}`)
    }
  }

  /**
   * 减少递归深度
   */
  decrementRecursionDepth(): void {
    this.recursionDepth--
  }

  /**
   * 获取当前递归深度
   */
  getRecursionDepth(): number {
    return this.recursionDepth
  }
  /** Read unsigned varint32 (returns uint32) */
  uint32(): number {
    let x = 0, s = 0
    while (true) { const b = this.b[this.pos++]; x |= (b & 0x7F) << s; if ((b & 0x80) === 0) break; s += 7 }
    return x >>> 0
  }
  /** Read boolean (varint) */
  bool(): boolean { return this.uint32() !== 0 }
  /** Read int32 (two's complement) */
  int32(): number { return this.uint32() | 0 }
  /** Read sint32 (zigzag) */
  sint32(): number { const v = this.uint32(); return (v >>> 1) ^ (-(v & 1)) }
  /** Read signed varint64 as bigint */
  int64(): bigint {
    let x = 0n, s = 0n
    while (true) { const b = BigInt(this.b[this.pos++]); x |= (b & 0x7Fn) << s; if ((b & 0x80n) === 0n) break; s += 7n }
    const signBit = 1n << 63n
    return (x & signBit) !== 0n ? (x - (1n << 64n)) : x
  }
  /** Read sint64 (zigzag) as bigint */
  sint64(): bigint { const v = this.int64(); return (v >> 1n) ^ (-(v & 1n)) }
  /** Read signed varint64 as number (may lose precision) */
  int64Number(): number { const v = this.int64(); const max = BigInt(Number.MAX_SAFE_INTEGER); return v <= max ? Number(v) : Number(v) }
  /** Read unsigned varint64 as bigint */
  uint64(): bigint {
    let x = 0n, s = 0n
    while (true) { const b = BigInt(this.b[this.pos++]); x |= (b & 0x7Fn) << s; if ((b & 0x80n) === 0n) break; s += 7n }
    return x
  }
  /** Read unsigned varint64 as number (may lose precision) */
  uint64Number(): number { const v = this.uint64(); const max = BigInt(Number.MAX_SAFE_INTEGER); return v <= max ? Number(v) : Number(v) }
  /** Read fixed32 little-endian */
  fixed32(): number { const v = new DataView(this.b.buffer, this.b.byteOffset + this.pos, 4).getUint32(0, true); this.pos += 4; return v >>> 0 }
  /** Read sfixed32 little-endian */
  sfixed32(): number { const v = new DataView(this.b.buffer, this.b.byteOffset + this.pos, 4).getInt32(0, true); this.pos += 4; return v | 0 }
  /** Read fixed64 little-endian */
  fixed64(): bigint { const dv = new DataView(this.b.buffer, this.b.byteOffset + this.pos, 8); const lo = BigInt(dv.getUint32(0, true)); const hi = BigInt(dv.getUint32(4, true)); this.pos += 8; return (hi << 32n) | lo }
  /** Read sfixed64 little-endian */
  sfixed64(): bigint { const dv = new DataView(this.b.buffer, this.b.byteOffset + this.pos, 8); const lo = BigInt(dv.getInt32(0, true)); const hi = BigInt(dv.getInt32(4, true)); this.pos += 8; return (hi << 32n) | (lo & 0xFFFFFFFFn) }
  /** Read float32 little-endian */
  float(): number { const v = new DataView(this.b.buffer, this.b.byteOffset + this.pos, 4).getFloat32(0, true); this.pos += 4; return v }
  /** Read float64 little-endian */
  double(): number { const v = new DataView(this.b.buffer, this.b.byteOffset + this.pos, 8).getFloat64(0, true); this.pos += 8; return v }
  /** Read length-delimited UTF-8 string */
  string(): string {
    const l = this.uint32()
    const s = this.b.subarray(this.pos, this.pos + l)
    this.pos += l
    return decodeUTF8(s)
  }
  /** Read length-delimited bytes */
  bytes(): Uint8Array {
    const l = this.uint32()
    const s = this.b.subarray(this.pos, this.pos + l)
    this.pos += l
    return s
  }
  /** Skip unknown field by wire type */
  skipType(wt: number): void {
    switch (wt) {
      case 0: this.uint32(); break
      case 1: this.pos += 8; break
      case 2: { const l = this.uint32(); this.pos += l; break }
      case 5: this.pos += 4; break
      default: throw new Error('invalid wire type')
    }
  }

  /**
   * 获取原始字节数据(用于保存未知字段)
   *
   * 返回 [start, end) 范围的字节数据(不包含 end)
   *
   * @param start 起始位置(包含)
   * @param end 结束位置(不包含)
   * @returns 原始字节切片
   *
   * 使用示例:
   * ```typescript
   * const start = reader.pos
   * reader.skipType(wireType)
   * const unknownFieldData = reader.view(start, reader.pos)
   * ```
   */
  view(start: number, end: number): Uint8Array {
    return this.b.subarray(start, end)
  }
}