//! Example demonstrating the generic `Coords1DInDomain` functionality.
//!
//! This example shows how `Coords1DInDomain` can store coordinates either owned
//! or borrowed, enabling flexible memory management strategies.

use grid1d::{
    coords::{
        Coords1D, Coords1DInDomain, Coords1DInDomainArc, Coords1DInDomainBorrowed,
        Coords1DInDomainOwned, Coords1DInDomainRc,
    },
    intervals::{
        GetLowerBoundValue, GetUpperBoundValue, IntervalClosed, IntervalFinitePositiveLengthTrait,
    },
    traits::{HasCoords1D, HasDomain1D},
};
use sorted_vec::partial::SortedSet;
use std::rc::Rc;
use std::sync::Arc;

fn main() {
    // Create some coordinates
    let coords =
        Coords1D::try_from(SortedSet::from_unsorted(vec![0.0, 0.25, 0.5, 0.75, 1.0])).unwrap();
    let domain = IntervalClosed::new(0.0, 1.0);

    println!("=== Generic Coords1DInDomain Example ===\n");

    // 1. Owned coordinates
    println("1. Owned coordinates");
    let points_owned = Coords1DInDomain::try_new(coords.clone(), domain.clone()).unwrap();
    println!(
        "   Points (owned): {:?}",
        points_owned.coords().as_ref().as_slice()
    );
    println!("   Domain: {:?}", points_owned.domain());

    // 2. Borrowed coordinates
    println("\n2. Borrowed coordinates");
    let points_borrowed = Coords1DInDomain::try_new(&coords, domain.clone()).unwrap();
    println!(
        "   Points (borrowed): {:?}",
        points_borrowed.coords().as_ref().as_slice()
    );
    println!("   Domain: {:?}", points_borrowed.domain());

    // 3. Type aliases for convenience
    println("\n3. Using type aliases");
    let _owned_alias: Coords1DInDomainOwned<IntervalClosed<f64>> = points_owned.clone();
    let _borrowed_alias: Coords1DInDomainBorrowed<IntervalClosed<f64>> = points_borrowed;
    println!("   Type aliases work correctly!");

    // 4. Conversion between owned and borrowed
    println("\n4. Converting between owned and borrowed");
    let borrowed_view = points_owned.as_borrowed();
    println!(
        "   Borrowed view from owned: {:?}",
        borrowed_view.coords().as_ref().as_slice()
    );

    let owned_copy = borrowed_view.to_owned_coords();
    println!(
        "   Owned copy from borrowed: {:?}",
        owned_copy.coords().as_ref().as_slice()
    );

    // 5. Generic function demonstration
    println("\n5. Generic function that accepts both");
    analyze_points(&points_owned);
    analyze_points(&owned_copy);

    // 6. Memory efficiency demonstration
    println("\n6. Memory efficiency with smart pointers");

    // First, demonstrate borrowed coordinates
    let large_coords = create_large_coordinates();
    println!(
        "   Created large coordinate set with {} points",
        large_coords.len()
    );
    let borrowed_large =
        Coords1DInDomain::try_new(&large_coords, IntervalClosed::new(0.0, 100.0)).unwrap();
    println!("   Using borrowed coordinates - no copying overhead!");
    process_without_ownership(&borrowed_large);

    // Now use Arc for shared ownership across threads
    let large_coords_for_arc = create_large_coordinates(); // Create a separate instance
    let arc_coords = Arc::new(large_coords_for_arc);
    let arc_points: Coords1DInDomainArc<IntervalClosed<f64>> =
        Coords1DInDomain::try_new(arc_coords.clone(), IntervalClosed::new(0.0, 100.0)).unwrap();
    println!("   Using Arc coordinates - shared ownership across threads!");
    process_without_ownership(&arc_points);

    // Use Rc for shared ownership in single-threaded scenarios
    let rc_coords = Rc::new(create_medium_coordinates());
    let rc_points: Coords1DInDomainRc<IntervalClosed<f64>> =
        Coords1DInDomain::try_new(rc_coords.clone(), IntervalClosed::new(0.0, 50.0)).unwrap();
    println!("   Using Rc coordinates - shared ownership single-threaded!");
    process_without_ownership(&rc_points);

    println("\n=== Example completed successfully! ===");
}

/// Generic function that works with any coordinate storage type
fn analyze_points<Storage>(points: &Coords1DInDomain<IntervalClosed<f64>, Storage>)
where
    Storage: std::borrow::Borrow<Coords1D<f64>>,
{
    let coords = points.coords();
    let domain = points.domain();

    println!(
        "   Analysis: {} points in domain [{}, {}]",
        coords.len(),
        domain.lower_bound_value(),
        domain.upper_bound_value()
    );
}

/// Function that borrows coordinates without taking ownership
fn process_without_ownership<Storage>(points: &Coords1DInDomain<IntervalClosed<f64>, Storage>)
where
    Storage: std::borrow::Borrow<Coords1D<f64>>,
{
    let coords = points.coords();
    let first = coords.first();
    let last = coords.last();
    println!(
        "   Processing range: [{}, {}] with {} points",
        first,
        last,
        coords.len()
    );
}

/// Create a large coordinate set to demonstrate memory efficiency
fn create_large_coordinates() -> Coords1D<f64> {
    let values: Vec<f64> = (0..=1000).map(|i| i as f64 / 10.0).collect();
    Coords1D::try_from(SortedSet::from_unsorted(values)).unwrap()
}

/// Create a medium coordinate set for Rc example
fn create_medium_coordinates() -> Coords1D<f64> {
    let values: Vec<f64> = (0..=500).map(|i| i as f64 / 10.0).collect();
    Coords1D::try_from(SortedSet::from_unsorted(values)).unwrap()
}

// Fix for println macro issue
fn println(s: &str) {
    println!("{}", s);
}
