///|
/// User-facing push-mode effect.
///
/// Runs the provided function immediately on creation to establish
/// dependencies, then re-runs whenever any dependency changes.
pub struct Effect {
  priv cell_id : CellId
  priv rt : Runtime
}

///|
/// Creates a new push-mode effect.
///
/// `f` is invoked immediately to establish dependencies and run the initial
/// side effect. It is re-run by push propagation whenever any dependency changes.
pub fn Effect::Effect(rt : Runtime, f : () -> Unit) -> Effect {
  let (cell_id, effect_idx) = rt.install_cell(
    rt.push.free_effects,
    rt.push.effects,
    idx => PushEffect(idx),
    cell_id => {
      meta: {
        cell_id,
        label: None,
        changed_at: Revision::initial(),
        durability: Low,
        subscribers: @hashset.HashSet([]),
        push_reachable_count: 0,
      },
      execute: f,
      sources: [],
      level: 1,
      dirty: false,
    },
  )
  rt.push.node_count = rt.push.node_count + 1
  let effect : Effect = { cell_id, rt }
  // Run immediately to establish dependencies and execute the initial side effect.
  rt.begin_tracking(cell_id)
  f()
  let (new_sources, new_seen) = rt.end_tracking()
  rt.push.effects[effect_idx].sources = new_sources
  rt.push.effects[effect_idx].level = rt.recompute_level(cell_id, new_sources)
  rt.finish_tracking(cell_id, [], new_sources, new_seen)
  if rt.has_pending_events() {
    rt.drain_pending_events_if_idle()
  }
  effect
}

///|
/// Deprecated alias of the `Effect::Effect` constructor.
#deprecated("Use the constructor form `Effect(rt, f)` (`Effect::Effect`) instead.")
pub fn Effect::new(rt : Runtime, f : () -> Unit) -> Effect {
  Effect(rt, f)
}

///|
/// Returns true if this effect has been disposed.
pub fn Effect::is_disposed(self : Effect) -> Bool {
  self.rt.is_cell_disposed(self.cell_id)
}

///|
/// Returns the unique identifier for this effect cell.
pub fn Effect::id(self : Effect) -> CellId {
  self.cell_id
}

///|
/// Removes this effect from the dependency graph.
pub fn Effect::dispose(self : Effect) -> Unit {
  self.rt.dispose_cell(self.cell_id)
}

///|
/// InputFieldOwner implementation for effect cells.
pub impl InputFieldOwner for Effect with fn cell_ids(self) -> Array[CellId] {
  [self.id()]
}