# =============================================================================
# OS EXPERIMENT 4: I/O AS FACTS
# =============================================================================
# Exploring how HERB might interface with hardware/external events.
#
# HERB's derivation engine is a CLOSED WORLD: rules fire based on existing
# facts, derive new facts, until fixpoint. No external input during derivation.
#
# Hardware I/O is an OPEN WORLD: events arrive at unpredictable times.
# Keyboard presses, network packets, disk completion, timer interrupts.
#
# Goal: Explore the boundary between closed derivation and open events.
# =============================================================================

# =============================================================================
# OPTION 1: EVENTS AS ASSERTED FACTS
# =============================================================================
#
# External events are asserted into the world before derivation runs.
# The derivation processes them, producing reactions.
#
# Between ticks:
#   keyboard driver: world.assert_fact("keyboard", "key_pressed", "a")
#   network driver: world.assert_fact("eth0", "packet_received", packet_001)
#   timer: world.assert_fact("timer0", "expired", True)
#
# Then: world.advance()
#
# Rules react to these events:

# Input handling
FUNCTIONAL last_key       # Most recent keypress

RULE handle_keypress
  WHEN keyboard key_pressed ?key
  THEN keyboard last_key ?key
  RETRACT keyboard key_pressed ?key

# Timer handling
RULE handle_timer
  WHEN ?timer expired true
  THEN ?timer fired_at_tick current_tick  # PROBLEM: current_tick doesn't exist!
  RETRACT ?timer expired true

# Network handling
RULE handle_packet
  WHEN ?iface packet_received ?pkt
  THEN ?iface processing ?pkt
  RETRACT ?iface packet_received ?pkt

# This model works! Events are facts. Rules process them. RETRACT consumes them.
#
# PROBLEMS:
# 1. Event ordering: multiple events per tick? Which fires first?
# 2. Event timing: "key pressed at 47.3ms" — HERB ticks are logical, not timed.
# 3. Backpressure: events pile up faster than derivation can process them?
# 4. Interrupt priority: NMI should preempt keyboard. How to express priority?

# =============================================================================
# EVENT QUEUES AND PRIORITIES
# =============================================================================

# Model events with priority and timestamp
# (event_001 is_a event)
# (event_001 source keyboard)
# (event_001 priority 10)
# (event_001 timestamp 47300)  # microseconds
# (event_001 payload "a")

# Process highest priority first
RULE find_highest_priority_event
  WHEN system has_pending_events true
  THEN system next_priority (max ?p WHERE ?e is_a event ?e priority ?p)

RULE select_next_event
  WHEN system next_priority ?maxp
  AND ?e is_a event
  AND ?e priority ?maxp
  THEN ?e is_next true

# PROBLEM: Same tie-breaking issue as scheduler. Multiple events at same
# priority — which one is "next"? FIFO requires ordered queue, not set.

# =============================================================================
# THE FUNDAMENTAL TIMING MISMATCH
# =============================================================================
#
# Real hardware: events arrive with nanosecond precision.
# Network packet at t=47,382,947ns. Keyboard at t=47,382,952ns.
# The OS must handle them in order, at speed.
#
# HERB: events are facts in a tick. Derivation runs to fixpoint.
# A tick might take 1ms of real time. Events during that tick?
#   Option A: Queue them for next tick. Adds latency.
#   Option B: Pause derivation to handle interrupt. Not in current model.
#
# For a GAME: Option A is fine. 16ms ticks (60fps) work great.
# For an OS: Interrupt latency in milliseconds is unacceptable.
#
# HERB's paradigm assumes time advances in discrete, atomic chunks.
# Hardware doesn't. Hardware has continuous time with interrupts.
#
# Potential solutions:
# 1. HERB ticks are very fast (1µs?). Derivation must be very fast.
# 2. Events queue and bulk-process. Latency-insensitive applications only.
# 3. Special "interrupt" rules that can fire mid-derivation. Breaks atomicity.
# 4. Hardware abstraction layer buffers events, presents them between ticks.

# =============================================================================
# OPTION 2: I/O AS WORLD MODIFICATION (REACTIVE)
# =============================================================================
#
# Instead of events-as-facts, I/O directly modifies world state.
# More like memory-mapped I/O.
#
# Screen: facts represent pixel state. GPU "reads" facts to render.
# (pixel (32 100) color 0xFF0000)
# (pixel (32 101) color 0xFF0000)
#
# Keyboard: hardware writes directly to world.
# keyboard_hw.write_to_world("keyboard", "key_down", "a")  # External
#
# HERB rules then react to changed state.
#
# PROBLEMS:
# - Who calls keyboard_hw.write_to_world()? Not HERB rules. External code.
# - Breaks the self-contained paradigm. HERB needs a "driver interface."
# - When does the write happen? Between ticks? During tick?

# =============================================================================
# SKETCH: DEVICE DRIVERS AS EXTERNAL ORACLES
# =============================================================================
#
# A "driver" is external code (Python, C, whatever) that:
# 1. Receives hardware interrupts/events
# 2. Asserts corresponding facts into a World
# 3. Reads facts to send commands to hardware
#
# class KeyboardDriver:
#     def __init__(self, world):
#         self.world = world
#
#     def on_keypress(self, key):
#         # Called by hardware interrupt handler
#         self.world.assert_fact("keyboard", "key_pressed", key)
#
#     def run_tick(self):
#         # Process any pending commands from HERB
#         for cmd in self.world.query(("keyboard", "command", var("c"))):
#             self.hardware.send(cmd['c'])
#             self.world.retract_fact("keyboard", "command", cmd['c'])
#
# The driver is the BOUNDARY between HERB and hardware.
# HERB never directly touches hardware. Drivers translate.
#
# This is exactly how traditional OSes work! HERB rules are like the kernel's
# policy code. Drivers are like... drivers. The boundary is clear.

FACT keyboard is_a device
FACT keyboard type keyboard
FACT keyboard driver keyboard_driver
FACT keyboard status initializing

FACT display is_a device
FACT display type display
FACT display driver display_driver
FACT display width 1920
FACT display height 1080
FACT display status initializing

FACT eth0 is_a device
FACT eth0 type network
FACT eth0 driver eth_driver
FACT eth0 mac_addr "00:11:22:33:44:55"
FACT eth0 status initializing

# Rules for device management
# NOTE: Can't use not-exists to set default status because that creates
# a negative cycle (reads status, writes status). Instead, mark devices
# with needs_init and have external driver assert status.

RULE mark_device_initialized
  WHEN ?dev is_a device
  AND ?dev status initializing
  AND ?drv driver_ready true
  AND ?dev driver ?drv
  THEN ?dev status ready
  RETRACT ?dev status initializing

RULE device_ready
  WHEN ?dev is_a device
  AND ?dev status ready
  THEN ?dev can_handle_io true

# =============================================================================
# OUTPUT: DISPLAY AS FACTS
# =============================================================================
#
# Screen rendering could work as facts:
# (framebuffer dirty true)
# (pixel (0,0) color 0x000000)
# ...millions of pixel facts...
#
# PROBLEM: 1920x1080 = 2,073,600 pixels. Each is a fact.
# Even with compound indices, this is massive overhead.
#
# Alternative: Buffer objects
# (framebuffer data <blob>)
# Where <blob> is an opaque binary buffer handled specially.
#
# HERB doesn't have blob/binary types. Would need to add.
# Or: facts don't represent pixels, they represent COMMANDS.
#
# (framebuffer draw_rect (0 0 100 100) color 0xFF0000)
# (framebuffer draw_text (50 50) "Hello" font default)
#
# Driver reads commands, executes them, retracts.
# This is more like a command buffer than pixel state.

RULE request_display_clear
  WHEN display needs_clear true
  THEN display command clear
  RETRACT display needs_clear true

RULE request_draw_text
  WHEN ?widget display_text ?text at ?pos
  THEN display command draw_text
  AND display_cmd_draw_text text ?text
  AND display_cmd_draw_text position ?pos
  RETRACT ?widget display_text ?text at ?pos

# =============================================================================
# NOTES: WHAT'S PAINFUL
# =============================================================================
#
# 1. **REAL-TIME REQUIREMENTS UNMET**
#    HERB ticks are logical, not timed. Microsecond interrupt handling
#    requires sub-microsecond derivation or a fundamentally different model.
#
# 2. **NO BINARY/BLOB TYPES**
#    Network packets, framebuffers, disk blocks are binary data.
#    HERB facts are (atom, atom, atom). Can't efficiently represent 4KB blocks.
#
# 3. **NO DMA/MEMORY-MAPPED I/O**
#    Real hardware reads/writes memory regions. HERB has a fact store.
#    Translation layer needed. Adds overhead and complexity.
#
# 4. **EVENT ORDERING**
#    Facts in a tick are unordered. Hardware events are ordered by time.
#    Need explicit timestamp facts or event queues to preserve ordering.
#
# =============================================================================
# NOTES: WHAT WORKS
# =============================================================================
#
# 1. **DRIVER BOUNDARY IS CLEAN**
#    External drivers assert facts, HERB processes them, drivers read results.
#    Clear separation of concerns.
#
# 2. **EVENT REACTIONS ARE NATURAL**
#    (keyboard key_pressed "a") → rules fire → state changes.
#    Exactly how you'd model it.
#
# 3. **COMMAND BUFFERS WORK**
#    Instead of direct I/O, assert commands as facts.
#    Driver executes and retracts. Queue-like semantics.
#
# 4. **PROVENANCE FOR DEBUGGING**
#    "Why did the screen show X?" traces to the draw command, to the
#    widget that requested it, to the user event that triggered it.
#
# =============================================================================
# MISSING LANGUAGE FEATURES (proposed)
# =============================================================================
#
# 1. **BLOB TYPE**: Opaque binary data for buffers, packets, blocks.
#    (packet data <binary>)
#    Blobs are values, not facts. Can be in fact objects.
#
# 2. **TIMESTAMP PRIMITIVE**: (current-time-us) returns microseconds.
#    For event ordering when ticks are too coarse.
#
# 3. **INTERRUPT HOOKS**: External code can register callbacks that fire
#    when certain patterns match. Not rules — direct Python/C calls.
#    For latency-critical paths.
#
# 4. **BATCH ASSERTION**: Assert many facts atomically, then trigger one
#    derivation. For efficiency when processing many events.
#
# =============================================================================
# ARCHITECTURAL PROPOSAL: THE HERB I/O MODEL
# =============================================================================
#
# 1. HERB World is the "policy layer" — pure logic, no hardware.
#
# 2. Drivers are external code (Python, C, Rust) that:
#    - Receive hardware interrupts
#    - Assert event facts into World
#    - Read command facts from World
#    - Execute commands on hardware
#
# 3. A "Kernel Loop" orchestrates:
#    while True:
#        # Collect events from all drivers
#        for driver in drivers:
#            driver.poll_hardware_to_world(world)
#
#        # Run derivation
#        world.advance()
#
#        # Execute commands to hardware
#        for driver in drivers:
#            driver.execute_commands_from_world(world)
#
#        # Optionally: sleep until next event or timer
#
# 4. For real-time needs, drivers can have fast paths that bypass HERB
#    for interrupt handling, then log to HERB for audit/provenance.
#
# This positions HERB as the BRAIN (high-level policy) and drivers as
# the NERVOUS SYSTEM (low-level I/O). Brain doesn't touch hardware.
# Hardware doesn't run rules. Clear boundary.
#
# =============================================================================
