## Terminal oscilloscope with CRT phosphor physics.
##
##   - CRT boot/shutdown animations (ported from AetherTune)
##   - Y-T (time-domain) and X-Y (Lissajous) display modes
##   - Phosphor persistence with bloom and decay
##   - Half-block rendering for 2× vertical resolution
##   - Live audio via libavdevice (dlopen, zero dependencies)

import illwill, os
import osc/[effects, phosphor, scope, audio]

proc exitProc() {.noconv.} =
  illwillDeinit()
  showCursor()
  quit(0)

proc main() =
  illwillInit(fullscreen = true)
  setControlCHook(exitProc)
  hideCursor()

  var w = terminalWidth()
  var h = terminalHeight()
  var tb = newTerminalBuffer(w, h)

  crtTurnOn(tb, w, h)

  var scope = initScope(w, h)
  var audio = startAudio()
  var running = true

  while running:
    let nw = terminalWidth()
    let nh = terminalHeight()
    if nw != w or nh != h:
      w = nw
      h = nh
      scope.resize(w, h)

    if not scope.frozen:
      audio.readSamples(scope)

    scope.phosphor.decay()

    if not scope.frozen:
      scope.renderTrace()

    tb = newTerminalBuffer(w, h)
    scope.phosphor.render(tb)
    drawGraticule(tb, w, h, scope.grid)
    drawHUD(tb, w, h, scope, audio.sourceLabel)
    tb.display()

    let key = getKey()
    case key
    of Key.Q, Key.Escape:
      running = false
    of Key.M:
      scope.mode = if scope.mode == ModeYT: ModeXY else: ModeYT
    of Key.Plus, Key.Equals:
      scope.gain = min(scope.gain * 1.3, 20.0)
    of Key.Minus:
      scope.gain = max(scope.gain / 1.3, 0.5)
    of Key.RightBracket:
      scope.timeDiv = min(scope.timeDiv * 1.5, 16.0)
    of Key.LeftBracket:
      scope.timeDiv = max(scope.timeDiv / 1.5, 0.25)
    of Key.Space:
      scope.frozen = not scope.frozen
    of Key.G:
      scope.grid = if scope.grid == gsGrid: gsOff else: gsGrid
    else:
      discard

    sleep(16)

  audio.stop()
  crtTurnOff(tb, w, h)

  tb = newTerminalBuffer(w, h)
  tb.display()
  sleep(100)
  illwillDeinit()
  showCursor()

when isMainModule:
  main()
