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

use qubit_function::{
    BiFunction,
    BiFunctionOnce,
    BiMutatingFunction,
    BiMutatingFunctionOnce,
    BoxBiFunction,
    BoxBiFunctionOnce,
    BoxBiMutatingFunction,
    BoxBiMutatingFunctionOnce,
    BoxFunction,
    BoxFunctionOnce,
    BoxMutatingFunction,
    BoxMutatingFunctionOnce,
    BoxStatefulFunction,
    BoxStatefulMutatingFunction,
    Function,
    FunctionOnce,
    MutatingFunction,
    MutatingFunctionOnce,
    StatefulFunction,
    StatefulMutatingFunction,
};

fn main() {
    println!("=== Function Family Demo ===\n");

    demo_borrowed_functions();
    demo_mutating_functions();
    demo_bi_functions();
    demo_bi_mutating_functions();
}

fn demo_borrowed_functions() {
    println!("--- Borrowed-input functions ---");

    let len = BoxFunction::new(|value: &String| value.len());
    let value = String::from("qubit");
    println!("Function length: {}", len.apply(&value));
    println!("Original value is still available: {value}");

    let greeting = BoxFunctionOnce::new(|name: &String| format!("hello, {name}"));
    println!("FunctionOnce greeting: {}", greeting.apply(&value));

    let mut call_count = 0;
    let mut stateful = BoxStatefulFunction::new(move |input: &i32| {
        call_count += 1;
        input + call_count
    });
    println!("StatefulFunction first call: {}", stateful.apply(&40));
    println!("StatefulFunction second call: {}", stateful.apply(&40));
    println!();
}

fn demo_mutating_functions() {
    println!("--- Mutating functions ---");

    let push_answer = BoxMutatingFunction::new(|items: &mut Vec<i32>| {
        items.push(42);
        items.len()
    });
    let mut items = vec![1, 2, 3];
    println!(
        "MutatingFunction new length: {}",
        push_answer.apply(&mut items)
    );
    println!("Items after mutation: {items:?}");

    let append_once = BoxMutatingFunctionOnce::new(|text: &mut String| {
        text.push_str(" once");
        text.len()
    });
    let mut text = String::from("called");
    println!(
        "MutatingFunctionOnce length: {}",
        append_once.apply(&mut text)
    );
    println!("Text after one-time mutation: {text}");

    let mut step = 0;
    let mut stateful = BoxStatefulMutatingFunction::new(move |value: &mut i32| {
        step += 1;
        *value += step;
        *value
    });
    let mut value = 40;
    println!(
        "StatefulMutatingFunction first call: {}",
        stateful.apply(&mut value)
    );
    println!(
        "StatefulMutatingFunction second call: {}",
        stateful.apply(&mut value)
    );
    println!();
}

fn demo_bi_functions() {
    println!("--- Bi-functions ---");

    let describe =
        BoxBiFunction::new(|name: &String, score: &i32| format!("{name} scored {score}"));
    let name = String::from("Alice");
    let score = 98;
    println!("BiFunction: {}", describe.apply(&name, &score));

    let once = BoxBiFunctionOnce::new(|left: &String, right: &String| format!("{left}-{right}"));
    let left = String::from("task");
    let right = String::from("done");
    println!("BiFunctionOnce: {}", once.apply(&left, &right));
    println!();
}

fn demo_bi_mutating_functions() {
    println!("--- Bi-mutating functions ---");

    let move_one = BoxBiMutatingFunction::new(|source: &mut Vec<i32>, target: &mut Vec<i32>| {
        if let Some(value) = source.pop() {
            target.push(value);
        }
        target.len()
    });
    let mut source = vec![1, 2, 3];
    let mut target = vec![10];
    println!(
        "BiMutatingFunction target length: {}",
        move_one.apply(&mut source, &mut target)
    );
    println!("Source: {source:?}, target: {target:?}");

    let merge_once = BoxBiMutatingFunctionOnce::new(|left: &mut String, right: &mut String| {
        left.push_str(right);
        right.clear();
        left.len()
    });
    let mut left = String::from("hello");
    let mut right = String::from(" world");
    println!(
        "BiMutatingFunctionOnce merged length: {}",
        merge_once.apply(&mut left, &mut right)
    );
    println!("Left: {left}, right is empty: {}", right.is_empty());
}
