/// Thread-safe key-value store shared across all processes.
pub mod kv;
pub(crate) mod process_access;
mod topology;
mod tso;

use std::any::Any;
use std::cell::RefCell;
use std::sync::Arc;
use std::sync::atomic::AtomicUsize;

use dashmap::DashMap;
pub use process_access::broadcast;
pub use process_access::broadcast_within_pool;
pub use process_access::choose_from_pool;
pub use process_access::now;
pub use process_access::pid;
pub use process_access::schedule_timer_after;
pub use process_access::send_random;
pub use process_access::send_random_from_pool;
pub use process_access::send_to;

use rustc_hash::FxBuildHasher;
pub use topology::list_pool;
pub use tso::unique_id;

use crate::services::process_access::PROCESS_ACCESS;
use crate::topology::Topology;

thread_local! {
    pub(super) static SERVICES: RefCell<Option<Arc<Services>>> = RefCell::new(None);
}

type AnyAllocated = Box<dyn Any + Send + Sync>;
type FastKV = DashMap<String, AnyAllocated, FxBuildHasher>;

pub(crate) struct Services {
    topology: Arc<Topology>,
    tso: AtomicUsize,
    kv: FastKV,
}

pub(super) fn with_services<R>(f: impl FnOnce(&Arc<Services>) -> R) -> R {
    SERVICES.with_borrow_mut(|opt| f(opt.as_ref().expect("services are not initialized")))
}

pub(crate) fn setup_services(services: Arc<Services>) {
    SERVICES.set(Some(services));
}

impl Services {
    pub(crate) fn new(topology: Arc<Topology>) -> Self {
        Self {
            topology,
            tso: AtomicUsize::new(0),
            kv: FastKV::with_hasher(FxBuildHasher),
        }
    }
}

pub(crate) fn reset() {
    SERVICES.take();
    PROCESS_ACCESS.take();
}
