//! Example 2 — weighted combine via `LinComb` with non-trivial weights.
//!
//! A consumer combines two moving averages with weights [0.7, 0.3] and wants to
//! confirm the *weighted* sum (not a plain sum) is what reaches the exposure
//! node. We tap all three streams (Sma(2), Sma(4), and the LinComb output) with
//! recorders and assert `combined == 0.7*sma2 + 0.3*sma4` on every fired cycle.
//!
//!   price ─┬─> Sma(2) ─┬────────────> Recorder (a)
//!          └─> Sma(4) ─┼┬───────────> Recorder (b)
//!                      ││
//!                LinComb([0.7,0.3]) ─> Recorder (c)

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, Sma};

use recorder::{Recorder, Tape};

fn main() {
    const SMA2: usize = 0;
    const SMA4: usize = 1;
    const LINCOMB: usize = 2;
    const REC_A: usize = 3;
    const REC_B: usize = 4;
    const REC_C: usize = 5;

    let tape_a: Tape = Rc::new(RefCell::new(Vec::new()));
    let tape_b: Tape = Rc::new(RefCell::new(Vec::new()));
    let tape_c: Tape = Rc::new(RefCell::new(Vec::new()));

    let nodes: Vec<Box<dyn aura_core::Node>> = vec![
        Box::new(Sma::new(2)),
        Box::new(Sma::new(4)),
        Box::new(LinComb::new(vec![0.7, 0.3])),
        Box::new(Recorder::new(tape_a.clone())),
        Box::new(Recorder::new(tape_b.clone())),
        Box::new(Recorder::new(tape_c.clone())),
    ];

    let sources = vec![SourceSpec {
        kind: ScalarKind::F64,
        targets: vec![
            Target { node: SMA2, slot: 0 },
            Target { node: SMA4, slot: 0 },
        ],
    }];

    let edges = vec![
        Edge { from: SMA2, to: LINCOMB, slot: 0, from_field: 0 },
        Edge { from: SMA4, to: LINCOMB, slot: 1, from_field: 0 },
        Edge { from: SMA2, to: REC_A, slot: 0, from_field: 0 },
        Edge { from: SMA4, to: REC_B, slot: 0, from_field: 0 },
        Edge { from: LINCOMB, to: REC_C, slot: 0, from_field: 0 },
    ];

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

    let prices: &[f64] = &[10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0];
    let stream: Vec<(Timestamp, Scalar)> = prices
        .iter()
        .enumerate()
        .map(|(i, &p)| (Timestamp(i as i64), Scalar::F64(p)))
        .collect();

    harness.run(vec![stream]);

    let (a, b, c) = (tape_a.borrow(), tape_b.borrow(), tape_c.borrow());
    println!("sma2 rows={}, sma4 rows={}, lincomb rows={}", a.len(), b.len(), c.len());

    // LinComb withholds output until BOTH inputs are present, so it fires only
    // from the cycle where the slower Sma(4) has warmed up.
    println!("\n  t   sma2     sma4     lincomb   expected(0.7*sma2+0.3*sma4)");
    for &(ts, combined) in c.iter() {
        let s2 = a.iter().find(|&&(t, _)| t == ts).map(|&(_, v)| v).unwrap();
        let s4 = b.iter().find(|&&(t, _)| t == ts).map(|&(_, v)| v).unwrap();
        let expected = 0.7 * s2 + 0.3 * s4;
        let ok = (combined - expected).abs() < 1e-12;
        println!(
            "  {:>2}  {:>7.3}  {:>7.3}  {:>8.4}  {:>8.4}  {}",
            ts.0, s2, s4, combined, expected, if ok { "OK" } else { "MISMATCH" }
        );
        assert!(ok, "weighted sum mismatch at t={}", ts.0);
    }
    println!("\nall weighted sums match 0.7*sma2 + 0.3*sma4 = OK");
}
