import Foundation
import PhysarumEngine
/// The ATV-side application of decoded remote messages onto the model. A pure mapping (no
/// Network.framework) so the message→effect is host-testable; the NWListener (added per-platform)
/// feeds decoded messages through `apply`. The model (AppState/AmbientController) is main-only, like
/// the existing tick path — so the network layer MUST hop each delivery to the main queue before
/// calling in. The `dispatchPrecondition` enforces that contract: a stray off-main delivery would
/// race the SwiftUI render/tick, and this fails it loudly in dev/test instead of corrupting silently.
enum AmbientHost {
/// Apply one decoded message to the same AmbientController sink the Siri Remote drives. Must run
/// on the main queue.
static func apply(_ message: RemoteMessage, to controller: AmbientController) {
dispatchPrecondition(condition: .onQueue(.main))
switch message {
case .hello(let state):
controller.adoptRemote(selectedID: state.selectedID, params: state.params, cycling: state.cycling)
case .intent(let intent):
controller.apply(intent)
case .params(let p):
controller.setLiveParams(p) // transition-aware: a live edit survives an in-flight crossfade
case .audioFactors(let frame):
controller.receiveAudioFactors(frame) // T2b: phone-streamed mic reactivity
}
}
/// On disconnect (user action or network loss) the ATV resumes autonomous T1 cycling. Main queue.
static func handleDisconnect(_ controller: AmbientController) {
dispatchPrecondition(condition: .onQueue(.main))
controller.resumeAutonomousCycle()
}
}