/*******************************************************************************
 *
 *    Copyright (c) 2025 - 2026 Haixing Hu.
 *
 *    SPDX-License-Identifier: Apache-2.0
 *
 *    Licensed under the Apache License, Version 2.0.
 *
 ******************************************************************************/

//! BiConsumer observation demonstration
//!
//! This example demonstrates stateless BiConsumer types that observe two input
//! values without mutating them.

use qubit_function::{
    ArcBiConsumer,
    BiConsumer,
    BoxBiConsumer,
    RcBiConsumer,
};
use std::rc::Rc;
use std::sync::{
    Arc,
    atomic::AtomicUsize,
    atomic::Ordering,
};
use std::thread;

fn main() {
    println!("=== BiConsumer Observation Demo ===\n");

    // 1. BoxBiConsumer - Single ownership
    println!("1. BoxBiConsumer - Single ownership:");
    let box_consumer = BoxBiConsumer::new(|x: &i32, y: &i32| {
        println!("  Values: x={}, y={}, sum={}", x, y, x + y);
    });
    box_consumer.accept(&10, &5);
    println!();

    // 2. Method chaining with BoxBiConsumer
    println!("2. BoxBiConsumer with method chaining:");
    let chained = BoxBiConsumer::new(|x: &i32, y: &i32| {
        println!("  First operation: x={}, y={}", x, y);
    })
    .and_then(|x: &i32, y: &i32| {
        println!("  Second operation: sum={}", x + y);
    })
    .and_then(|x: &i32, y: &i32| {
        println!("  Third operation: product={}", x * y);
    });
    chained.accept(&5, &3);
    println!();

    // 3. ArcBiConsumer - Thread-safe shared ownership
    println!("3. ArcBiConsumer - Thread-safe shared ownership:");
    let counter = Arc::new(AtomicUsize::new(0));
    let c = counter.clone();
    let arc_consumer = ArcBiConsumer::new(move |x: &i32, y: &i32| {
        c.fetch_add(1, Ordering::SeqCst);
        println!("  Thread {:?}: sum={}", thread::current().id(), x + y);
    });

    let consumer1 = arc_consumer.clone();
    let consumer2 = arc_consumer.clone();

    let handle1 = thread::spawn(move || {
        consumer1.accept(&10, &5);
    });

    let handle2 = thread::spawn(move || {
        consumer2.accept(&20, &8);
    });

    handle1.join().unwrap();
    handle2.join().unwrap();
    println!("  Total calls: {}\n", counter.load(Ordering::SeqCst));

    // 4. RcBiConsumer - Single-threaded shared ownership
    println!("4. RcBiConsumer - Single-threaded shared ownership:");
    let counter = Rc::new(std::cell::Cell::new(0));
    let c = counter.clone();
    let rc_consumer = RcBiConsumer::new(move |x: &i32, y: &i32| {
        c.set(c.get() + 1);
        println!("  Call {}: sum={}", c.get(), x + y);
    });

    let clone1 = rc_consumer.clone();
    let clone2 = rc_consumer.clone();

    clone1.accept(&5, &3);
    clone2.accept(&7, &2);
    println!("  Total calls: {}\n", counter.get());

    // 5. Working with closures directly
    println!("5. Working with closures directly:");
    let closure = |x: &i32, y: &i32| {
        println!("  x={}, y={}, product={}", x, y, x * y);
    };
    closure.accept(&10, &20);
    println!();

    // 6. Pure observation - logging
    println!("6. Pure observation - logging:");
    let logger = BoxBiConsumer::new(|x: &i32, y: &i32| {
        println!("  [LOG] Processing pair: ({}, {})", x, y);
    });
    logger.accept(&5, &3);
    logger.accept(&10, &7);
    println!();

    // 7. Chaining observations
    println!("7. Chaining observations:");
    let log_input = BoxBiConsumer::new(|x: &i32, y: &i32| {
        println!("  [INPUT] x={}, y={}", x, y);
    });
    let log_sum = BoxBiConsumer::new(|x: &i32, y: &i32| {
        println!("  [SUM] {}", x + y);
    });
    let log_product = BoxBiConsumer::new(|x: &i32, y: &i32| {
        println!("  [PRODUCT] {}", x * y);
    });

    let chained = log_input.and_then(log_sum).and_then(log_product);
    chained.accept(&5, &3);
    println!();

    // 8. ArcBiConsumer - Reusability
    println!("8. ArcBiConsumer - Reusability:");
    let first = ArcBiConsumer::new(|x: &i32, y: &i32| {
        println!("  First: x={}, y={}", x, y);
    });
    let second = ArcBiConsumer::new(|x: &i32, y: &i32| {
        println!("  Second: sum={}", x + y);
    });

    // Both first and second can be reused after chaining
    let chained1 = first.and_then(second.clone());
    let chained2 = first.and_then(second.clone());

    println!("  Using chained1:");
    chained1.accept(&5, &3);

    println!("  Using chained2:");
    chained2.accept(&10, &2);
    println!();

    // 9. Name support
    println!("9. Name support:");
    let mut named_consumer = BoxBiConsumer::<i32, i32>::noop();
    println!("  Initial name: {:?}", named_consumer.name());

    named_consumer.set_name("sum_logger");
    println!("  After setting name: {:?}", named_consumer.name());
    println!("  Display: {}\n", named_consumer);

    // 10. No-op consumer
    println!("10. No-op consumer:");
    let noop = BoxBiConsumer::<i32, i32>::noop();
    noop.accept(&42, &10);
    println!("  No-op completed (no output expected)\n");

    println!("=== Demo Complete ===");
}
