//! Example 5 (firing-policy probe) — combine two signals driven by two DIFFERENT
//! sources that update on different cycles.
//!
//! This is the genuinely-heterogeneous combine the north-star reaches for: a
//! fast price tick × a slower second feed, summed by LinComb. The interesting
//! question — invisible from the public surface — is LinComb's per-input firing
//! policy (C6): does a fired-this-cycle leg get combined with the OTHER leg's
//! *held* value (mode A, as-of join), or does LinComb only fire when both legs
//! are fresh in the same cycle (mode B barrier)?
//!
//! The rustdoc says only "Emits None until all inputs have a value" — it does
//! not state the firing policy. We feed two sources at interleaved timestamps
//! and RECORD what LinComb emits, to see which reading the shipped node took.
//!
//!   source A (f64, fast) ─> LinComb slot 0 ┐
//!   source B (f64, slow) ─> LinComb slot 1 ┴─> Recorder

mod recorder;

use std::cell::RefCell;
use std::rc::Rc;

use aura_core::ScalarKind;
use aura_core::{Scalar, Timestamp};
use aura_engine::{Edge, Harness, SourceSpec, Target};
use aura_std::LinComb;

use recorder::{Recorder, Tape};

fn main() {
    const LINCOMB: usize = 0;
    const REC: usize = 1;

    let tape: Tape = Rc::new(RefCell::new(Vec::new()));

    let nodes: Vec<Box<dyn aura_core::Node>> = vec![
        Box::new(LinComb::new(vec![1.0, 1.0])),
        Box::new(Recorder::new(tape.clone())),
    ];

    // Two independent sources, both f64, each feeding one LinComb slot.
    let sources = vec![
        SourceSpec { kind: ScalarKind::F64, targets: vec![Target { node: LINCOMB, slot: 0 }] },
        SourceSpec { kind: ScalarKind::F64, targets: vec![Target { node: LINCOMB, slot: 1 }] },
    ];

    let edges = vec![Edge { from: LINCOMB, to: REC, slot: 0, from_field: 0 }];

    let mut harness = Harness::bootstrap(nodes, sources, edges).expect("bootstrap");

    // Source A fires at t = 0,1,2,3,4,5; source B fires only at t = 1 and t = 4.
    // (Distinct timestamps => distinct cycles, C4.)
    let stream_a: Vec<(Timestamp, Scalar)> = (0..6)
        .map(|i| (Timestamp(i), Scalar::F64(100.0 + i as f64)))
        .collect();
    let stream_b: Vec<(Timestamp, Scalar)> = vec![
        (Timestamp(1), Scalar::F64(10.0)),
        (Timestamp(4), Scalar::F64(20.0)),
    ];

    harness.run(vec![stream_a, stream_b]);

    let tape = tape.borrow();
    println!("LinComb emitted {} rows from two interleaved sources:", tape.len());
    println!("  (source A: a=100+t at t=0..5;  source B: b=10 at t=1, b=20 at t=4)");
    for (ts, v) in tape.iter() {
        println!("    t={:>2}  combined={:.4}", ts.0, v);
    }
    println!();
    println!("Interpretation (public surface is SILENT on the firing policy):");
    println!("  - mode A (as-of/hold): once both legs seen, every A-fresh cycle re-fires");
    println!("    combining a's fresh value with b's HELD value.");
    println!("  - mode B (barrier): LinComb fires only on cycles where BOTH legs are");
    println!("    fresh at the same timestamp -> here only t=1 and t=4 would emit.");
    println!("  The recorded rows above reveal which reading shipped.");
}
