use core::fmt::{Display, Formatter, Result};
use melodium_common::descriptor::{
    Buildable, Context, Documented, Identified, Identifier, Input, Model, Output, Parameter,
    Parameterized, Treatment as TreatmentDescriptor, TreatmentBuildMode,
};
use melodium_common::executive::Treatment as ExecutiveTreatment;
use once_cell::sync::OnceCell;
use std::collections::HashMap;
use std::iter::FromIterator;
use std::sync::{Arc, Weak};
#[derive(Debug)]
pub struct Treatment {
    identifier: Identifier,
    #[cfg(feature = "doc")]
    documentation: String,
    models: HashMap<String, Arc<dyn Model>>,
    parameters: HashMap<String, Parameter>,
    inputs: HashMap<String, Input>,
    outputs: HashMap<String, Output>,
    source_from: HashMap<String, Vec<String>>,
    build_fn: fn() -> Arc<dyn ExecutiveTreatment>,
    auto_reference: Weak<Self>,
}
impl Treatment {
    pub fn new(
        identifier: Identifier,
        documentation: String,
        models: Vec<(String, Arc<dyn Model>)>,
        source_from: Vec<(String, Vec<String>)>,
        parameters: Vec<Parameter>,
        inputs: Vec<Input>,
        outputs: Vec<Output>,
        build_fn: fn() -> Arc<dyn ExecutiveTreatment>,
    ) -> Arc<Self> {
        #[cfg(not(feature = "doc"))]
        let _ = documentation;
        Arc::new_cyclic(|me| Self {
            identifier,
            #[cfg(feature = "doc")]
            documentation,
            models: HashMap::from_iter(models.into_iter().map(|(n, m)| (n, m))),
            parameters: HashMap::from_iter(
                parameters.into_iter().map(|p| (p.name().to_string(), p)),
            ),
            inputs: HashMap::from_iter(inputs.into_iter().map(|i| (i.name().to_string(), i))),
            outputs: HashMap::from_iter(outputs.into_iter().map(|o| (o.name().to_string(), o))),
            source_from: HashMap::from_iter(source_from.into_iter()),
            build_fn,
            auto_reference: me.clone(),
        })
    }
}
impl Identified for Treatment {
    fn identifier(&self) -> &Identifier {
        &self.identifier
    }
}
impl Documented for Treatment {
    fn documentation(&self) -> &str {
        #[cfg(feature = "doc")]
        {
            &self.documentation
        }
        #[cfg(not(feature = "doc"))]
        {
            &""
        }
    }
}
impl Parameterized for Treatment {
    fn parameters(&self) -> &HashMap<String, Parameter> {
        &self.parameters
    }
    fn as_identified(&self) -> Arc<dyn Identified> {
        self.auto_reference.upgrade().unwrap()
    }
}
impl Buildable<TreatmentBuildMode> for Treatment {
    fn build_mode(&self) -> TreatmentBuildMode {
        TreatmentBuildMode::Compiled(self.build_fn, self.auto_reference.clone())
    }
    fn make_use(&self, identifier: &Identifier) -> bool {
        self.models
            .iter()
            .any(|(_, model)| model.identifier() == identifier)
    }
}
impl Display for Treatment {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
        write!(f, "treatment {}", self.identifier.to_string())?;
        if !self.models.is_empty() {
            write!(
                f,
                "[{}]",
                self.models
                    .iter()
                    .map(|(n, m)| format!("{}: {}", n, m.identifier().to_string()))
                    .collect::<Vec<_>>()
                    .join(", "),
            )?;
        }
        write!(
            f,
            "({})",
            self.parameters()
                .iter()
                .map(|(_, p)| p.to_string())
                .collect::<Vec<_>>()
                .join(", ")
        )?;
        Ok(())
    }
}
impl TreatmentDescriptor for Treatment {
    fn inputs(&self) -> &HashMap<String, Input> {
        &self.inputs
    }
    fn outputs(&self) -> &HashMap<String, Output> {
        &self.outputs
    }
    fn models(&self) -> &HashMap<String, Arc<dyn Model>> {
        &self.models
    }
    fn contexts(&self) -> &HashMap<String, Arc<dyn Context>> {
        static HASHMAP: OnceCell<HashMap<String, Arc<dyn Context>>> = OnceCell::new();
        HASHMAP.get_or_init(|| HashMap::new())
    }
    fn source_from(&self) -> &HashMap<String, Vec<String>> {
        &self.source_from
    }
    fn as_identified(&self) -> Arc<dyn Identified> {
        self.auto_reference.upgrade().unwrap()
    }
    fn as_buildable(&self) -> Arc<dyn Buildable<TreatmentBuildMode>> {
        self.auto_reference.upgrade().unwrap()
    }
    fn as_parameterized(&self) -> Arc<dyn Parameterized> {
        self.auto_reference.upgrade().unwrap()
    }
}