///|
let stereo_topo_edit_level : @ref.Ref[Double] = @ref.new(1.0)

///|
let compiled_stereo_topology_edit : GraphSlot[
  @moondsp.CompiledStereoDspTopologyController,
  StereoOut,
] = GraphSlot::new(output=StereoOut::new(), compile=fn(ctx) {
  @moondsp.CompiledStereoDspTopologyController::from_nodes(
    [
      @moondsp.DspNode::constant(1.0),
      @moondsp.DspNode::pan(0, BROWSER_STEREO_TOPOLOGY_EDIT_OLD_PAN),
      @moondsp.DspNode::stereo_output(1),
    ],
    ctx,
    crossfade_samples=BROWSER_STEREO_TOPOLOGY_EDIT_CROSSFADE_SAMPLES,
  )
})

///|
/// Initialize the browser proof graph that runs through
/// `CompiledStereoDspTopologyController`.
pub fn init_compiled_stereo_topology_edit_graph(
  sample_rate : Double,
  block_size : Int,
) -> Bool {
  if compiled_stereo_topology_edit.ensure(sample_rate, block_size) {
    clear_browser_error()
    true
  } else {
    set_browser_init_error("compiled_stereo_topology_edit.init")
  }
}

///|
/// Queue the fixed browser stereo topology edit that replaces the pan node.
/// WHY no idempotency guard (unlike mono topology_edit's `inserted` bool):
/// replace_node is inherently idempotent — replacing with the same node is a no-op.
pub fn queue_compiled_stereo_topology_edit() -> Bool {
  let topology = match compiled_stereo_topology_edit.graph.val {
    Some(t) => t
    None =>
      return set_browser_not_initialized("compiled_stereo_topology_edit.queue")
  }
  match
    topology.queue_topology_edit(
      @moondsp.GraphTopologyEdit::replace_node(
        1,
        @moondsp.DspNode::pan(0, BROWSER_STEREO_TOPOLOGY_EDIT_NEW_PAN),
      ),
    ) {
    Ok(_) => {
      clear_browser_error()
      true
    }
    Err(error) =>
      set_browser_topology_queue_error(
        "compiled_stereo_topology_edit.queue", error,
      )
  }
}

///|
/// Update the fixed browser stereo topology-edit level control.
pub fn set_compiled_stereo_topology_edit_level(level : Double) -> Bool {
  if compiled_stereo_topology_edit.graph.val is None {
    return set_browser_not_initialized(
      "compiled_stereo_topology_edit.set_level",
    )
  }
  stereo_topo_edit_level.val = level
  clear_browser_error()
  true
}

///|
/// Run one render quantum through the browser proof
/// `CompiledStereoDspTopologyController` graph.
pub fn process_compiled_stereo_topology_edit_block(
  sample_rate : Double,
  block_size : Int,
) -> Bool {
  if !compiled_stereo_topology_edit.ensure(sample_rate, block_size) {
    return set_browser_init_error("compiled_stereo_topology_edit.process")
  }
  let topology = compiled_stereo_topology_edit.graph_val()
  match
    topology.apply_control(
      @moondsp.GraphControl::set_param(
        0,
        @moondsp.GraphParamSlot::Value0,
        stereo_topo_edit_level.val,
      ),
    ) {
    Ok(_) => ()
    Err(error) =>
      return set_browser_control_error(
        "compiled_stereo_topology_edit.process", error,
      )
  }
  topology.process(
    compiled_stereo_topology_edit.ctx_val(),
    compiled_stereo_topology_edit.output.left_buf(),
    compiled_stereo_topology_edit.output.right_buf(),
  )
  clear_browser_error()
  true
}

///|
/// Read one sample from the most recent stereo topology-edit browser left block.
pub fn compiled_stereo_topology_edit_left_sample(index : Int) -> Double {
  compiled_stereo_topology_edit.output.left_sample(index)
}

///|
/// Read one sample from the most recent stereo topology-edit browser right block.
pub fn compiled_stereo_topology_edit_right_sample(index : Int) -> Double {
  compiled_stereo_topology_edit.output.right_sample(index)
}

///|
fn reset_compiled_stereo_topology_edit_graph() -> Unit {
  compiled_stereo_topology_edit.reset()
  stereo_topo_edit_level.val = 1.0
  clear_browser_error()
}