use std::collections::{HashMap, HashSet};

#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

use super::Dialect;

/// MAVLink protocol.
///
/// [`Protocol`] is a collection of MAVLink dialects. Each [`Dialect`] contains MAVLink [`enums`](Dialect::enums) and
/// [`messages`](Dialect::messages) as well as additional information (like version or dependencies).
#[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Protocol {
    dialects: HashMap<String, Dialect>,
}

impl Protocol {
    /// Default constructor.
    pub fn new(dialects: HashMap<String, Dialect>) -> Self {
        Self { dialects }
    }

    /// Dialects within protocol.
    ///
    /// Canonical version of dialect name ([`Dialect::canonical_name`]) is used as a key.
    pub fn dialects(&self) -> &HashMap<String, Dialect> {
        &self.dialects
    }

    /// Create a new instance of [`Protocol`] with dialects containing only specified `messages`.
    ///
    /// If `keep_enums` is set to `false` then only enums related to existing messages will be kept.
    ///
    /// This is an immutable cloning version [`Self::retain_messages`]. The latter filters out messages and enums
    /// in-place.
    ///
    /// See also [`Dialect::with_filtered_messages`] which performs the same operation on the [`Dialect`] level.
    pub fn with_filtered_messages(&self, messages: &HashSet<String>, keep_enums: bool) -> Self {
        let mut protocol = Protocol::default();

        for (id, dialect) in &self.dialects {
            protocol.dialects.insert(
                id.clone(),
                dialect.with_filtered_messages(messages, keep_enums),
            );
        }

        protocol
    }

    /// Retain only specified `messages` for each [`Dialect`].
    ///
    /// If `keep_enums` is set to `false` then only enums related to existing messages will be kept.
    ///
    /// This is an in-place version of [`Self::with_filtered_messages`]. The latter creates a new instance of
    /// [`Protocol`].
    ///
    /// See also [`Dialect::retain_messages`] which performs the same operation on the [`Dialect`] level.
    pub fn retain_messages(&mut self, messages: &HashSet<String>, keep_enums: bool) {
        for dialect in &mut self.dialects.values_mut() {
            dialect.retain_messages(messages, keep_enums);
        }
    }
}
