use brk_types::OutputType;

use super::schema::with_transaction_features;

#[derive(Clone, Copy, Default)]
pub(crate) struct TxFeatureFlags(u32);

macro_rules! define_flags {
    ($($(#[$attribute:meta])* $vector:ident: $flag:ident = $bit:literal $(, count: $count:ident)?;)+) => {
        impl TxFeatureFlags {
            $(pub(crate) const $flag: u32 = 1 << $bit;)+
        }
    };
}

with_transaction_features!(define_flags);

impl TxFeatureFlags {
    pub(crate) fn insert_type(&mut self, output_type: OutputType) {
        self.0 |= match output_type {
            OutputType::P2PK65 | OutputType::P2PK33 => Self::P2PK,
            OutputType::P2MS => Self::P2MS,
            OutputType::P2PKH => Self::P2PKH,
            OutputType::P2SH => Self::P2SH,
            OutputType::P2WPKH => Self::P2WPKH,
            OutputType::P2WSH => Self::P2WSH,
            OutputType::P2TR => Self::P2TR,
            OutputType::P2A => Self::P2A,
            OutputType::OpReturn => Self::OP_RETURN,
            OutputType::Empty => Self::EMPTY,
            OutputType::Unknown => Self::UNKNOWN,
        };
    }

    #[inline]
    pub(crate) fn insert(&mut self, flag: u32) {
        self.0 |= flag;
    }

    #[inline]
    pub(crate) fn contains_all(self, flags: u32) -> bool {
        self.0 & flags == flags
    }

    #[inline]
    pub(crate) fn is_set(self, flag: u32) -> bool {
        self.0 & flag != 0
    }
}

#[cfg(test)]
mod tests {
    use brk_types::OutputType;

    use super::TxFeatureFlags;

    #[test]
    fn type_flags_union_inputs_and_outputs() {
        let mut flags = TxFeatureFlags::default();
        flags.insert_type(OutputType::P2PKH);
        flags.insert_type(OutputType::P2TR);

        assert!(flags.is_set(TxFeatureFlags::P2PKH));
        assert!(flags.is_set(TxFeatureFlags::P2TR));
    }
}
