import type { Pointer } from "bun:ffi"
import { gpiod } from "./ffi"
import { requestOutputLine } from "./gpio-helpers"
import type { OutputOptions } from "./types"

export class Output {
  #closed = false
  #chip: Pointer
  #request: Pointer
  #offset: number
  #resetValue?: 0 | 1

  constructor(chipPath: string, offset: number, options: OutputOptions = {}) {
    const initialValue = options.initialValue ?? 0
    const { chip, request } = requestOutputLine({ chipPath, offset, initialValue })

    this.#chip = chip
    this.#request = request
    this.#offset = offset

    if (options.resetOnClose) {
      const currentValue = gpiod.gpiod_line_request_get_value(request, offset)
      this.#resetValue = currentValue === -1 ? 0 : (currentValue as 0 | 1)
    }
  }

  get value(): 0 | 1 {
    if (this.#closed) throw new Error("Output is closed")
    const ret = gpiod.gpiod_line_request_get_value(this.#request, this.#offset)
    if (ret === -1) throw new Error("Failed to get pin value")
    return ret as 0 | 1
  }

  set value(val: 0 | 1) {
    if (this.#closed) throw new Error("Output is closed")
    const ret = gpiod.gpiod_line_request_set_value(this.#request, this.#offset, val)
    if (ret === -1) throw new Error("Failed to set pin value")
  }

  toggle() {
    this.value = this.value === 0 ? 1 : 0
  }

  close() {
    if (this.#closed) return
    this.#closed = true

    if (this.#resetValue !== undefined) {
      gpiod.gpiod_line_request_set_value(this.#request, this.#offset, this.#resetValue)
    }

    gpiod.gpiod_line_request_release(this.#request)
    gpiod.gpiod_chip_close(this.#chip)
  }

  [Symbol.dispose]() {
    this.close()
  }
}
