use crate::prelude::*;

/// Determine target formats for a source.
#[injectable]
pub struct TargetFormatProvider {
    /// Target format options.
    pub options: Ref<TargetOptions>,
}

impl TargetFormatProvider {
    /// Get the target formats for a [Source].
    #[must_use]
    pub fn get(
        &self,
        source: SourceFormat,
        existing: &BTreeSet<ExistingFormat>,
    ) -> BTreeSet<TargetFormat> {
        if self.options.allow_existing {
            self.get_with_existing(source)
        } else {
            self.get_without_existing(existing)
        }
    }

    /// Filter the target formats to exclude the source format.
    fn get_with_existing(&self, source: SourceFormat) -> BTreeSet<TargetFormat> {
        let set = BTreeSet::from([source.to_existing()]);
        self.get_targets_except_excluded(&set)
    }

    /// Filter the target formats to exclude existing formats (which will include the source format).
    fn get_without_existing(&self, existing: &BTreeSet<ExistingFormat>) -> BTreeSet<TargetFormat> {
        self.get_targets_except_excluded(existing)
    }

    fn get_targets_except_excluded(
        &self,
        exclude: &BTreeSet<ExistingFormat>,
    ) -> BTreeSet<TargetFormat> {
        self.options
            .target
            .iter()
            .filter(|target| !exclude.contains(&target.to_existing()))
            .copied()
            .collect()
    }
}
