autosar_data_abstraction/software_component/interface/
clientserver.rsuse crate::{
    abstraction_element,
    datatype::{self, AbstractAutosarDataType},
    software_component::AbstractPortInterface,
    AbstractionElement, ArPackage, AutosarAbstractionError, Element, EnumItem,
};
use autosar_data::ElementName;
use datatype::AutosarDataType;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ClientServerInterface(pub(crate) Element);
abstraction_element!(ClientServerInterface, ClientServerInterface);
impl AbstractPortInterface for ClientServerInterface {}
impl ClientServerInterface {
    pub(crate) fn new(name: &str, package: &ArPackage) -> Result<Self, AutosarAbstractionError> {
        let elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
        let client_server_interface = elements.create_named_sub_element(ElementName::ClientServerInterface, name)?;
        Ok(Self(client_server_interface))
    }
    pub fn create_possible_error(
        &self,
        name: &str,
        error_code: u64,
    ) -> Result<ApplicationError, AutosarAbstractionError> {
        let possible_errors = self.element().get_or_create_sub_element(ElementName::PossibleErrors)?;
        ApplicationError::new(name, error_code, &possible_errors)
    }
    pub fn create_operation(&self, name: &str) -> Result<ClientServerOperation, AutosarAbstractionError> {
        let operations = self.element().get_or_create_sub_element(ElementName::Operations)?;
        ClientServerOperation::new(name, &operations)
    }
    pub fn operations(&self) -> impl Iterator<Item = ClientServerOperation> {
        self.element()
            .get_sub_element(ElementName::Operations)
            .into_iter()
            .flat_map(|operations| operations.sub_elements())
            .filter_map(|elem| ClientServerOperation::try_from(elem).ok())
    }
    pub fn possible_errors(&self) -> impl Iterator<Item = ApplicationError> {
        self.element()
            .get_sub_element(ElementName::PossibleErrors)
            .into_iter()
            .flat_map(|errors| errors.sub_elements())
            .filter_map(|elem| ApplicationError::try_from(elem).ok())
    }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ApplicationError(Element);
abstraction_element!(ApplicationError, ApplicationError);
impl ApplicationError {
    fn new(name: &str, error_code: u64, parent_element: &Element) -> Result<Self, AutosarAbstractionError> {
        let application_error = parent_element.create_named_sub_element(ElementName::ApplicationError, name)?;
        application_error
            .create_sub_element(ElementName::ErrorCode)?
            .set_character_data(error_code)?;
        Ok(Self(application_error))
    }
    pub fn error_code(&self) -> Option<u64> {
        self.element()
            .get_sub_element(ElementName::ErrorCode)?
            .character_data()?
            .parse_integer()
    }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ClientServerOperation(Element);
abstraction_element!(ClientServerOperation, ClientServerOperation);
impl ClientServerOperation {
    fn new(name: &str, parent_element: &Element) -> Result<Self, AutosarAbstractionError> {
        let operation = parent_element.create_named_sub_element(ElementName::ClientServerOperation, name)?;
        Ok(Self(operation))
    }
    pub fn create_argument<T: AbstractAutosarDataType>(
        &self,
        name: &str,
        data_type: &T,
        direction: ArgumentDirection,
    ) -> Result<ArgumentDataPrototype, AutosarAbstractionError> {
        let arguments = self.element().get_or_create_sub_element(ElementName::Arguments)?;
        ArgumentDataPrototype::new(name, &arguments, data_type.element(), direction)
    }
    pub fn arguments(&self) -> impl Iterator<Item = ArgumentDataPrototype> {
        self.element()
            .get_sub_element(ElementName::Arguments)
            .into_iter()
            .flat_map(|arguments| arguments.sub_elements())
            .filter_map(|elem| ArgumentDataPrototype::try_from(elem).ok())
    }
    pub fn add_possible_error(&self, error: &ApplicationError) -> Result<(), AutosarAbstractionError> {
        if self.element().named_parent()? != error.element().named_parent()? {
            return Err(AutosarAbstractionError::InvalidParameter(
                "Error and operation must be in the same ClientServerInterface".to_string(),
            ));
        }
        let possible_errors = self
            .element()
            .get_or_create_sub_element(ElementName::PossibleErrorRefs)?;
        possible_errors
            .create_sub_element(ElementName::PossibleErrorRef)?
            .set_reference_target(error.element())?;
        Ok(())
    }
    pub fn possible_errors(&self) -> impl Iterator<Item = ApplicationError> {
        self.element()
            .get_sub_element(ElementName::PossibleErrorRefs)
            .into_iter()
            .flat_map(|errors| errors.sub_elements())
            .filter_map(|refelem| {
                refelem
                    .get_reference_target()
                    .ok()
                    .and_then(|elem| ApplicationError::try_from(elem).ok())
            })
    }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ArgumentDirection {
    In,
    Out,
    InOut,
}
impl TryFrom<EnumItem> for ArgumentDirection {
    type Error = AutosarAbstractionError;
    fn try_from(item: EnumItem) -> Result<Self, Self::Error> {
        match item {
            EnumItem::In => Ok(ArgumentDirection::In),
            EnumItem::Out => Ok(ArgumentDirection::Out),
            EnumItem::Inout => Ok(ArgumentDirection::InOut),
            _ => Err(AutosarAbstractionError::ValueConversionError {
                value: item.to_string(),
                dest: "ArgumentDirection".to_string(),
            }),
        }
    }
}
impl From<ArgumentDirection> for EnumItem {
    fn from(direction: ArgumentDirection) -> Self {
        match direction {
            ArgumentDirection::In => EnumItem::In,
            ArgumentDirection::Out => EnumItem::Out,
            ArgumentDirection::InOut => EnumItem::Inout,
        }
    }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ArgumentDataPrototype(Element);
abstraction_element!(ArgumentDataPrototype, ArgumentDataPrototype);
impl ArgumentDataPrototype {
    fn new(
        name: &str,
        parent_element: &Element,
        data_type: &Element,
        direction: ArgumentDirection,
    ) -> Result<Self, AutosarAbstractionError> {
        let argument = parent_element.create_named_sub_element(ElementName::ArgumentDataPrototype, name)?;
        argument
            .create_sub_element(ElementName::TypeTref)?
            .set_reference_target(data_type)?;
        argument
            .create_sub_element(ElementName::Direction)?
            .set_character_data::<EnumItem>(direction.into())?;
        Ok(Self(argument))
    }
    pub fn data_type(&self) -> Option<AutosarDataType> {
        let data_type_elem = self
            .element()
            .get_sub_element(ElementName::TypeTref)?
            .get_reference_target()
            .ok()?;
        AutosarDataType::try_from(data_type_elem).ok()
    }
    pub fn direction(&self) -> Option<ArgumentDirection> {
        let value = self
            .element()
            .get_sub_element(ElementName::Direction)?
            .character_data()?
            .enum_value()?;
        ArgumentDirection::try_from(value).ok()
    }
}
#[cfg(test)]
mod test {
    use super::*;
    use autosar_data::{AutosarModel, AutosarVersion};
    use datatype::{BaseTypeEncoding, ImplementationDataTypeSettings};
    #[test]
    fn test_client_server_interface() {
        let model = AutosarModel::new();
        let _file = model.create_file("filename", AutosarVersion::LATEST).unwrap();
        let package = ArPackage::get_or_create(&model, "/package").unwrap();
        let client_server_interface = ClientServerInterface::new("TestInterface", &package).unwrap();
        assert_eq!(client_server_interface.name().unwrap(), "TestInterface");
        assert_eq!(client_server_interface.operations().count(), 0);
        assert_eq!(client_server_interface.possible_errors().count(), 0);
        let error = client_server_interface.create_possible_error("TestError", 42).unwrap();
        assert_eq!(client_server_interface.possible_errors().count(), 1);
        assert_eq!(error.name().unwrap(), "TestError");
        assert_eq!(error.error_code().unwrap(), 42);
        let operation = client_server_interface.create_operation("TestOperation").unwrap();
        assert_eq!(client_server_interface.operations().count(), 1);
        assert_eq!(operation.name().unwrap(), "TestOperation");
        assert_eq!(operation.arguments().count(), 0);
        operation.add_possible_error(&error).unwrap();
        assert_eq!(operation.possible_errors().count(), 1);
        let base_type = package
            .create_sw_base_type("base", 32, BaseTypeEncoding::None, None, None, None)
            .unwrap();
        let impl_settings = ImplementationDataTypeSettings::Value {
            name: "ImplementationValue".to_string(),
            base_type,
            compu_method: None,
            data_constraint: None,
        };
        let datatype = package.create_implementation_data_type(impl_settings).unwrap();
        let argument = operation
            .create_argument("TestArgument", &datatype, ArgumentDirection::In)
            .unwrap();
        assert_eq!(argument.name().unwrap(), "TestArgument");
        assert_eq!(argument.data_type().unwrap().name().unwrap(), "ImplementationValue");
        assert_eq!(argument.direction().unwrap(), ArgumentDirection::In);
        assert_eq!(operation.arguments().count(), 1);
    }
}