1use super::{Attributes, Identifier, ModelDesign, Parameter, SharingError, SharingResult};
2use melodium_common::descriptor::{
3    Collection, Entry as CommonEntry, Identifier as CommonIdentifier, Model as CommonModel,
4};
5use melodium_engine::{descriptor::Model as DesignedModel, LogicError};
6use serde::{Deserialize, Serialize};
7use std::{
8    collections::{BTreeMap, HashMap},
9    sync::Arc,
10};
11
12#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
13#[cfg_attr(feature = "webassembly", derive(tsify::Tsify))]
14#[cfg_attr(feature = "webassembly", tsify(into_wasm_abi, from_wasm_abi))]
15pub struct Model {
16    pub identifier: Identifier,
17    pub documentation: String,
18    pub parameters: BTreeMap<String, Parameter>,
19    pub implementation_kind: ModelImplementationKind,
20    pub hierarchy: Vec<Identifier>,
21    pub sources: BTreeMap<String, Vec<Identifier>>,
22    pub attributes: Attributes,
23}
24
25impl Model {
26    pub fn make_descriptor(&self, collection: &Collection) -> SharingResult<Arc<DesignedModel>> {
27        let identifier = if let Ok(identifier) = (&self.identifier).try_into() {
28            identifier
29        } else {
30            return SharingResult::new_failure(SharingError::invalid_identifier(
31                1,
32                self.identifier.clone(),
33            ));
34        };
35
36        if self.implementation_kind.is_compiled() {
37            return SharingResult::new_failure(SharingError::compiled_model(2, identifier));
38        }
39
40        let base_identifier: CommonIdentifier = if let Some(base_identifier) = self.hierarchy.last()
41        {
42            if let Ok(base_identifier) = base_identifier.try_into() {
43                base_identifier
44            } else {
45                return SharingResult::new_failure(SharingError::invalid_identifier(
46                    4,
47                    self.identifier.clone(),
48                ));
49            }
50        } else {
51            return SharingResult::new_failure(SharingError::missing_base_identifier(
52                3, identifier,
53            ));
54        };
55
56        let base_model = if let Some(melodium_common::descriptor::Entry::Model(base_model)) =
57            collection.get(&(&base_identifier).into())
58        {
59            base_model
60        } else {
61            return SharingResult::new_failure(
62                LogicError::unexisting_model(230, identifier.clone(), base_identifier.into(), None)
63                    .into(),
64            );
65        };
66
67        let mut result = SharingResult::new_success(());
68        let mut descriptor = DesignedModel::new(identifier.clone(), base_model);
69
70        descriptor.set_documentation(&self.documentation);
71
72        for (name, attribute) in &self.attributes.0 {
73            descriptor.add_attribute(name.clone(), attribute.clone());
74        }
75
76        for (_, param) in &self.parameters {
77            if let Some(parameter) =
78                result.merge_degrade_failure(param.to_parameter(collection, &identifier))
79            {
80                descriptor.add_parameter(parameter);
81            }
82        }
83
84        result.and(SharingResult::new_success(descriptor.commit()))
85    }
86
87    pub fn make_design(&self, collection: &Arc<Collection>) -> SharingResult<()> {
88        let identifier = if let Ok(identifier) = (&self.identifier).try_into() {
89            identifier
90        } else {
91            return SharingResult::new_failure(SharingError::invalid_identifier(
92                6,
93                self.identifier.clone(),
94            ));
95        };
96
97        let design =
98            if let ModelImplementationKind::Designed(Some(design)) = &self.implementation_kind {
99                design
100            } else {
101                return SharingResult::new_failure(SharingError::no_model_design_available(
102                    7, identifier,
103                ));
104            };
105
106        let descriptor = if let Some(CommonEntry::Model(model)) =
107            collection.get(&(&identifier).into())
108        {
109            if let Ok(model) = model.clone().downcast_arc::<DesignedModel>() {
110                model
111            } else {
112                return SharingResult::new_failure(
113                    LogicError::unexisting_model(235, identifier.clone(), identifier.into(), None)
114                        .into(),
115                );
116            }
117        } else {
118            return SharingResult::new_failure(
119                LogicError::unexisting_model(234, identifier.clone(), identifier.into(), None)
120                    .into(),
121            );
122        };
123
124        let designer = match descriptor.designer(Arc::clone(collection), None) {
125            melodium_common::descriptor::Status::Success { success, errors: _ } => success,
126            melodium_common::descriptor::Status::Failure { failure, errors: _ } => {
127                return SharingResult::new_failure(failure.into())
128            }
129        };
130
131        design
132            .make_design(collection, &descriptor)
133            .and_then(|design| {
134                SharingResult::from(designer.write().unwrap().import_design(
135                    &design,
136                    &HashMap::new(),
137                    None,
138                ))
139            })
140            .and_then(|()| SharingResult::from(descriptor.commit_design()))
141    }
142}
143
144impl From<&Arc<dyn CommonModel>> for Model {
145    fn from(value: &Arc<dyn CommonModel>) -> Self {
146        let mut hierarchy = Vec::new();
147        let mut base = value.base_model();
148        while let Some(parent) = base {
149            hierarchy.push(parent.identifier().into());
150            base = parent.base_model();
151        }
152        Self {
153            identifier: Identifier::from(value.identifier()),
154            documentation: value.documentation().to_string(),
155            parameters: value
156                .parameters()
157                .iter()
158                .map(|(name, param)| (name.clone(), Parameter::from(param)))
159                .collect(),
160            implementation_kind: match value.build_mode() {
161                melodium_common::descriptor::ModelBuildMode::Compiled(_) => {
162                    ModelImplementationKind::Compiled
163                }
164                melodium_common::descriptor::ModelBuildMode::Designed() => {
165                    ModelImplementationKind::Designed(
166                        value
167                            .clone()
168                            .downcast_arc::<DesignedModel>()
169                            .unwrap()
170                            .design()
171                            .success()
172                            .map(|design| design.as_ref().into()),
173                    )
174                }
175            },
176            hierarchy,
177            sources: value
178                .sources()
179                .iter()
180                .map(|(name, contexts)| {
181                    (
182                        name.clone(),
183                        contexts
184                            .iter()
185                            .map(|context| Identifier::from(context.identifier()))
186                            .collect(),
187                    )
188                })
189                .collect(),
190            attributes: value.attributes().into(),
191        }
192    }
193}
194
195#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
196#[serde(rename_all = "snake_case")]
197#[cfg_attr(feature = "webassembly", derive(tsify::Tsify))]
198#[cfg_attr(feature = "webassembly", tsify(into_wasm_abi, from_wasm_abi))]
199pub enum ModelImplementationKind {
200    Compiled,
201    Designed(Option<ModelDesign>),
202}
203
204impl ModelImplementationKind {
205    pub fn is_compiled(&self) -> bool {
206        match self {
207            ModelImplementationKind::Compiled => true,
208            _ => false,
209        }
210    }
211}