import { dlopen, FFIType, ptr } from "bun:ffi"

export const cstr = (s: string) => ptr(Buffer.from(s + "\0"))

const findLibgpiod = () => {
  try {
    dlopen("libgpiod.so.3", {
      gpiod_chip_open: {
        args: [FFIType.cstring],
        returns: FFIType.ptr,
      },
    })
    return "libgpiod.so.3"
  } catch {
    throw new Error(
      "libgpiod v2 (libgpiod.so.3) not found. Install with: sudo apt-get install libgpiod-dev"
    )
  }
}

// Constants MUST match C header exactly
export const GPIOD_LINE_DIRECTION_AS_IS = 1
export const GPIOD_LINE_DIRECTION_INPUT = 2
export const GPIOD_LINE_DIRECTION_OUTPUT = 3

export const GPIOD_LINE_BIAS_AS_IS = 1
export const GPIOD_LINE_BIAS_UNKNOWN = 2
export const GPIOD_LINE_BIAS_DISABLED = 3
export const GPIOD_LINE_BIAS_PULL_UP = 4
export const GPIOD_LINE_BIAS_PULL_DOWN = 5

export const GPIOD_LINE_EDGE_NONE = 1
export const GPIOD_LINE_EDGE_RISING = 2
export const GPIOD_LINE_EDGE_FALLING = 3
export const GPIOD_LINE_EDGE_BOTH = 4

export const GPIOD_EDGE_EVENT_RISING_EDGE = 1
export const GPIOD_EDGE_EVENT_FALLING_EDGE = 2

const lib = dlopen(findLibgpiod(), {
  gpiod_chip_open: {
    args: [FFIType.cstring],
    returns: FFIType.ptr,
  },
  gpiod_chip_close: {
    args: [FFIType.ptr],
    returns: FFIType.void,
  },
  gpiod_chip_get_info: {
    args: [FFIType.ptr],
    returns: FFIType.ptr,
  },
  gpiod_chip_info_get_name: {
    args: [FFIType.ptr],
    returns: FFIType.cstring,
  },
  gpiod_chip_info_get_label: {
    args: [FFIType.ptr],
    returns: FFIType.cstring,
  },
  gpiod_chip_info_get_num_lines: {
    args: [FFIType.ptr],
    returns: FFIType.u64,
  },
  gpiod_request_config_new: {
    args: [],
    returns: FFIType.ptr,
  },
  gpiod_request_config_set_consumer: {
    args: [FFIType.ptr, FFIType.cstring],
    returns: FFIType.void,
  },
  gpiod_request_config_free: {
    args: [FFIType.ptr],
    returns: FFIType.void,
  },
  gpiod_line_config_new: {
    args: [],
    returns: FFIType.ptr,
  },
  gpiod_line_config_add_line_settings: {
    args: [FFIType.ptr, FFIType.ptr, FFIType.u64, FFIType.ptr],
    returns: FFIType.i32,
  },
  gpiod_line_config_free: {
    args: [FFIType.ptr],
    returns: FFIType.void,
  },
  gpiod_line_settings_new: {
    args: [],
    returns: FFIType.ptr,
  },
  gpiod_line_settings_set_direction: {
    args: [FFIType.ptr, FFIType.i32],
    returns: FFIType.i32,
  },
  gpiod_line_settings_set_output_value: {
    args: [FFIType.ptr, FFIType.i32],
    returns: FFIType.i32,
  },
  gpiod_line_settings_set_bias: {
    args: [FFIType.ptr, FFIType.i32],
    returns: FFIType.i32,
  },
  gpiod_line_settings_set_edge_detection: {
    args: [FFIType.ptr, FFIType.i32],
    returns: FFIType.i32,
  },
  gpiod_line_settings_set_debounce_period_us: {
    args: [FFIType.ptr, FFIType.u64],
    returns: FFIType.i32,
  },
  gpiod_line_settings_free: {
    args: [FFIType.ptr],
    returns: FFIType.void,
  },
  gpiod_chip_request_lines: {
    args: [FFIType.ptr, FFIType.ptr, FFIType.ptr],
    returns: FFIType.ptr,
  },
  gpiod_line_request_release: {
    args: [FFIType.ptr],
    returns: FFIType.void,
  },
  gpiod_line_request_get_fd: {
    args: [FFIType.ptr],
    returns: FFIType.i32,
  },
  gpiod_line_request_set_value: {
    args: [FFIType.ptr, FFIType.u32, FFIType.i32],
    returns: FFIType.i32,
  },
  gpiod_line_request_get_value: {
    args: [FFIType.ptr, FFIType.u32],
    returns: FFIType.i32,
  },
  gpiod_line_request_wait_edge_events: {
    args: [FFIType.ptr, FFIType.i64],
    returns: FFIType.i32,
  },
  gpiod_line_request_read_edge_events: {
    args: [FFIType.ptr, FFIType.ptr, FFIType.u64],
    returns: FFIType.i32,
  },
  gpiod_edge_event_buffer_new: {
    args: [FFIType.u64],
    returns: FFIType.ptr,
  },
  gpiod_edge_event_buffer_free: {
    args: [FFIType.ptr],
    returns: FFIType.void,
  },
  gpiod_edge_event_buffer_get_event: {
    args: [FFIType.ptr, FFIType.u64],
    returns: FFIType.ptr,
  },
  gpiod_edge_event_get_line_offset: {
    args: [FFIType.ptr],
    returns: FFIType.u32,
  },
  gpiod_edge_event_get_event_type: {
    args: [FFIType.ptr],
    returns: FFIType.i32,
  },
  gpiod_edge_event_get_timestamp_ns: {
    args: [FFIType.ptr],
    returns: FFIType.u64,
  },
})

export const gpiod = lib.symbols
