CCCPaste

methodPrinter

-- modem_methods.lua
-- If target is a modem: list methods of device(s) connected to that modem.
-- CC:Tweaked

local function clamp(x,a,b) return math.max(a, math.min(b, x)) end

local function drawList(x, y, w, h, title, items, sel, scroll)
  local function line(s)
    term.setCursorPos(x, y)
    term.write((" "):rep(w))
    if s then
      term.setCursorPos(x + 1, y)
      term.write(s:sub(1, math.max(0, w-2)))
    end
    y = y + 1
  end

  term.setCursorPos(x, y)
  term.write((" "):rep(w))
  if title then
    term.setCursorPos(x + 1, y)
    term.write(title:sub(1, math.max(0, w-2)))
  end
  y = y + 1

  local visible = h - 1
  sel = clamp(sel, 1, math.max(1, #items))
  scroll = clamp(scroll, 0, math.max(0, #items - visible))

  for i = 1, visible do
    local idx = scroll + i
    local s = items[idx] or ""
    if idx == sel then
      term.setBackgroundColor(colors.gray)
      term.setTextColor(colors.black)
      line(s)
      term.setBackgroundColor(colors.black)
      term.setTextColor(colors.white)
    else
      line(s)
    end
  end
  return sel, scroll
end

local function move(sel, scroll, total, maxRows, delta)
  sel = clamp(sel + delta, 1, math.max(1, total))
  local top = sel - 1
  local bottom = sel
  if top < scroll then
    scroll = top
  elseif bottom > scroll + maxRows then
    scroll = bottom - maxRows
  end
  scroll = clamp(scroll, 0, math.max(0, total - maxRows))
  return sel, scroll
end

local function pager(title, lines)
  term.setBackgroundColor(colors.black)
  term.setTextColor(colors.white)
  term.clear()
  local w, h = term.getSize()
  local header = title or ""
  local listH = h - 2
  local sel, scr = 1, 0

  while true do
    term.clear()
    term.setCursorPos(1,1)
    term.write(header:sub(1, w))
    sel, scr = drawList(1, 2, w, listH, nil, lines, sel, scr)

    local e = { os.pullEvent() }
    local ev = e[1]
    if ev == "key" then
      local k = e[2]
      if k == keys.q or k == keys.enter then return
      elseif k == keys.up then sel, scr = move(sel, scr, #lines, listH, -1)
      elseif k == keys.down then sel, scr = move(sel, scr, #lines, listH, 1)
      elseif k == keys.pageUp then sel, scr = move(sel, scr, #lines, listH, -(listH-1))
      elseif k == keys.pageDown then sel, scr = move(sel, scr, #lines, listH, (listH-1))
      elseif k == keys.home then sel, scr = move(1, 0, #lines, listH, 0)
      elseif k == keys["end"] then sel, scr = move(#lines, math.max(0,#lines-listH), #lines, listH, 0)
      end
    elseif ev == "mouse_scroll" then
      local d = e[2]
      sel, scr = move(sel, scr, #lines, listH, d)
    elseif ev == "term_resize" then
      w, h = term.getSize()
      listH = h - 2
      scr = clamp(scr, 0, math.max(0, #lines - listH))
      sel = clamp(sel, 1, math.max(1, #lines))
    end
  end
end

local function pickFromList(title, items)
  if #items == 0 then return nil end
  if #items == 1 then return items[1] end

  term.setBackgroundColor(colors.black)
  term.setTextColor(colors.white)
  term.clear()

  local w, h = term.getSize()
  local listH = h - 2
  local sel, scr = 1, 0
  while true do
    term.clear()
    term.setCursorPos(1,1)
    term.write((title or "Select"):sub(1, w))
    sel, scr = drawList(1, 2, w, listH, nil, items, sel, scr)

    local e = { os.pullEvent() }
    local ev = e[1]
    if ev == "key" then
      local k = e[2]
      if k == keys.enter then return items[sel]
      elseif k == keys.q then return nil
      elseif k == keys.up then sel, scr = move(sel, scr, #items, listH, -1)
      elseif k == keys.down then sel, scr = move(sel, scr, #items, listH, 1)
      elseif k == keys.pageUp then sel, scr = move(sel, scr, #items, listH, -(listH-1))
      elseif k == keys.pageDown then sel, scr = move(sel, scr, #items, listH, (listH-1))
      elseif k == keys.home then sel, scr = move(1, 0, #items, listH, 0)
      elseif k == keys["end"] then sel, scr = move(#items, math.max(0,#items-listH), #items, listH, 0)
      end
    elseif ev == "mouse_scroll" then
      local d = e[2]
      sel, scr = move(sel, scr, #items, listH, d)
    elseif ev == "term_resize" then
      w, h = term.getSize()
      listH = h - 2
      scr = clamp(scr, 0, math.max(0, #items - listH))
      sel = clamp(sel, 1, math.max(1, #items))
    end
  end
end

-- Find a modem (optionally from arg)
local argName = ({...})[1]
local modemName

if argName and peripheral.isPresent(argName) and peripheral.getType(argName) == "modem" then
  modemName = argName
else
  -- collect all modems
  local all = {}
  for _, n in ipairs(peripheral.getNames()) do
    if peripheral.getType(n) == "modem" then table.insert(all, n) end
  end
  if #all == 0 then
    print("No modem peripheral found.")
    return
  end
  modemName = pickFromList("Select a modem (Enter to confirm, Q to cancel)", all)
  if not modemName then return end
end

local modem = peripheral.wrap(modemName)
if not modem then
  print("Failed to wrap modem: "..modemName)
  return
end

-- Check wired vs wireless
local isWireless = false
if modem.isWireless then
  -- wired modems also have isWireless() (returns false); wireless returns true
  local ok, res = pcall(modem.isWireless)
  if ok then isWireless = res and true or false end
end

if isWireless then
  print(("'%s' is a wireless modem: no cabled peripherals to inspect."):format(modemName))
  print("Tip: Use a WIRED modem connected to a peripheral via modem cable.")
  return
end

-- Enumerate peripherals on the wired network via this modem
local remotes = {}
do
  local ok, names = pcall(modem.getNamesRemote)
  if not ok or type(names) ~= "table" then
    print("This modem does not support remote peripherals (is it wired and enabled?).")
    return
  end
  table.sort(names)
  for _, n in ipairs(names) do table.insert(remotes, n) end
end

if #remotes == 0 then
  print("No peripherals are connected/enabled on the wired network for modem '"..modemName.."'.")
  print("Right-click the wired modem on each device to toggle its connection, then try again.")
  return
end

local target = pickFromList("Select a device on the wired network", remotes)
if not target then return end

-- Fetch and show methods of the selected remote device
local methods = {}
do
  local ok, m = pcall(modem.getMethodsRemote, target)
  if not ok or type(m) ~= "table" then
    print("Failed to get methods for "..target)
    return
  end
  table.sort(m)
  table.insert(methods, ("Remote device: %s (%s)"):format(
    target, (modem.getTypeRemote and (modem.getTypeRemote(target) or "unknown") or "unknown")
  ))
  table.insert(methods, string.rep("-", 40))
  for _, name in ipairs(m) do table.insert(methods, name) end
end

pager("Methods of "..target.."  (Enter/Q to exit, arrows to scroll)", methods)