use crate::std;
use crate::{
    error::{Error, Result},
    impl_default, impl_extended_ops, impl_message_ops, impl_omnibus_extended_command,
    len::SET_EXTENDED_NOTE_INHIBITS_BASE,
    std::fmt,
    ExtendedCommand, ExtendedCommandOps, ExtendedNoteReporting, MessageOps, MessageType,
    OmnibusCommandOps,
};
pub const CFSC_ENABLE_LEN: usize = 8;
pub const SC_ENABLE_LEN: usize = 19;
mod bitmask {
    pub const ENABLE_NOTE: u8 = 0b111_1111;
}
pub mod index {
    pub const ENABLE_NOTE: usize = 7;
}
bitfield! {
    #[derive(Clone, Copy, Debug, Default, PartialEq)]
    pub struct EnableNote(u16);
    u16;
    pub note1, set_note1: 0;
    pub note2, set_note2: 1;
    pub note3, set_note3: 2;
    pub note4, set_note4: 3;
    pub note5, set_note5: 4;
    pub note6, set_note6: 5;
    pub note7, set_note7: 6;
    pub note_index, set_note_index: 15, 7;
}
impl EnableNote {
    pub const LEN: usize = 7;
    pub const fn none() -> Self {
        Self(0)
    }
    pub const fn all() -> Self {
        Self(bitmask::ENABLE_NOTE as u16)
    }
    pub const fn len() -> usize {
        Self::LEN
    }
    pub fn set_index(&mut self, index: usize) -> Result<()> {
        match index {
            1 => self.set_note1(true),
            2 => self.set_note2(true),
            3 => self.set_note3(true),
            4 => self.set_note4(true),
            5 => self.set_note5(true),
            6 => self.set_note6(true),
            7 => self.set_note7(true),
            _ => return Err(Error::failure("invalid enable index")),
        }
        Ok(())
    }
}
impl From<&[bool]> for EnableNote {
    fn from(b: &[bool]) -> Self {
        let mut inner = 0u16;
        let end = std::cmp::min(b.len(), Self::len());
        for (i, &set) in b[..end].iter().enumerate() {
            let bit = if set { 1 } else { 0 };
            inner |= bit << i;
        }
        Self(inner)
    }
}
impl<const N: usize> From<&[bool; N]> for EnableNote {
    fn from(b: &[bool; N]) -> Self {
        b.as_ref().into()
    }
}
impl<const N: usize> From<[bool; N]> for EnableNote {
    fn from(b: [bool; N]) -> Self {
        (&b).into()
    }
}
impl From<u8> for EnableNote {
    fn from(b: u8) -> Self {
        Self((b & bitmask::ENABLE_NOTE) as u16)
    }
}
impl From<&EnableNote> for u8 {
    fn from(e: &EnableNote) -> u8 {
        (e.0 & 0xff) as u8
    }
}
impl From<EnableNote> for u8 {
    fn from(e: EnableNote) -> u8 {
        (&e).into()
    }
}
impl fmt::Display for EnableNote {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let idx = self.note_index();
        write!(f, "{{")?;
        write!(f, r#""note_{idx}": {}, "#, self.note1())?;
        write!(f, r#""note_{}": {}, "#, idx.saturating_add(1), self.note2())?;
        write!(f, r#""note_{}": {}, "#, idx.saturating_add(2), self.note3())?;
        write!(f, r#""note_{}": {}, "#, idx.saturating_add(3), self.note4())?;
        write!(f, r#""note_{}": {}, "#, idx.saturating_add(4), self.note5())?;
        write!(f, r#""note_{}": {}, "#, idx.saturating_add(5), self.note6())?;
        write!(f, r#""note_{}": {}"#, idx.saturating_add(6), self.note7())?;
        write!(f, "}}")
    }
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct SetExtendedNoteInhibits<const M: usize, const N: usize> {
    buf: [u8; M],
}
impl<const M: usize, const N: usize> SetExtendedNoteInhibits<M, N> {
    pub const ENABLE_NOTE_LEN: usize = N;
    pub fn new() -> Self {
        assert!(
            M == SET_EXTENDED_NOTE_INHIBITS_BASE + CFSC_ENABLE_LEN
                || M == SET_EXTENDED_NOTE_INHIBITS_BASE + SC_ENABLE_LEN
        );
        let mut message = Self { buf: [0u8; M] };
        message.init();
        message.set_message_type(MessageType::Extended);
        message.set_extended_note(ExtendedNoteReporting::Set);
        message.set_extended_command(ExtendedCommand::SetExtendedNoteInhibits);
        message
    }
    pub fn enabled_notes(&self) -> [EnableNote; N] {
        let mut ret = [EnableNote::none(); N];
        for (¬e, set_note) in self.buf
            [index::ENABLE_NOTE..index::ENABLE_NOTE + Self::ENABLE_NOTE_LEN]
            .iter()
            .zip(ret.iter_mut())
        {
            *set_note = EnableNote::from(note);
        }
        ret
    }
    pub fn set_enabled_notes(&mut self, notes: &[EnableNote]) {
        let max_len = std::cmp::min(notes.len(), Self::ENABLE_NOTE_LEN);
        for (i, note) in notes[..max_len].iter().enumerate() {
            self.buf[index::ENABLE_NOTE + i] = note.into();
        }
    }
}
impl<const M: usize, const N: usize> fmt::Display for SetExtendedNoteInhibits<M, N> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{{")?;
        write!(f, r#""message_type": {}, "#, self.message_type())?;
        write!(f, r#""extended_command": {}, "#, self.extended_command())?;
        write!(f, r#""denomination": {}, "#, self.denomination())?;
        write!(f, r#""operational_mode": {}, "#, self.operational_mode())?;
        write!(f, r#""configuration": {}, "#, self.configuration())?;
        write!(f, r#""enabled_notes": ["#)?;
        let mut notes = self.enabled_notes();
        for (i, note) in notes.iter_mut().enumerate() {
            if i != 0 {
                write!(f, ", ")?;
            }
            note.set_note_index(((i * 7) + 1) as u16);
            write!(f, "{note}")?;
        }
        write!(f, "]}}")
    }
}
pub const CFSC_ENABLE_FULL_LEN: usize = SET_EXTENDED_NOTE_INHIBITS_BASE + CFSC_ENABLE_LEN;
pub const SC_ENABLE_FULL_LEN: usize = SET_EXTENDED_NOTE_INHIBITS_BASE + SC_ENABLE_LEN;
pub type SetExtendedNoteInhibitsCFSC =
    SetExtendedNoteInhibits<CFSC_ENABLE_FULL_LEN, CFSC_ENABLE_LEN>;
pub type SetExtendedNoteInhibitsSC = SetExtendedNoteInhibits<SC_ENABLE_FULL_LEN, SC_ENABLE_LEN>;
impl_default!(SetExtendedNoteInhibits, M, N);
impl_message_ops!(SetExtendedNoteInhibits, M, N);
impl_extended_ops!(SetExtendedNoteInhibits, M, N);
impl_omnibus_extended_command!(SetExtendedNoteInhibits, M, N);
#[cfg(test)]
mod tests {
    use super::*;
    use crate::Result;
    #[test]
    #[rustfmt::skip]
    fn test_query_set_extended_note_inhibits_from_bytes() -> Result<()> {
        let msg_bytes = [
            0x02, 0x11, 0x70, 0x03,
            0x00, 0x00, 0x00,
            0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x03, 0x63,
        ];
        let mut msg = SetExtendedNoteInhibitsCFSC::new();
        msg.from_buf(msg_bytes.as_ref())?;
        assert_eq!(msg.message_type(), MessageType::Extended);
        assert_eq!(msg.extended_command(), ExtendedCommand::SetExtendedNoteInhibits);
        let exp_enabled = [
            EnableNote::from(1), EnableNote::none(), EnableNote::none(), EnableNote::none(),
            EnableNote::none(), EnableNote::none(), EnableNote::none(),  EnableNote::none(),
        ];
        assert_eq!(msg.enabled_notes(), exp_enabled);
        let msg_bytes = [
            0x02, 0x1c, 0x70, 0x03,
            0x00, 0x00, 0x00,
            0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00,
            0x03, 0x6e,
        ];
        let mut msg = SetExtendedNoteInhibitsSC::new();
        msg.from_buf(msg_bytes.as_ref())?;
        assert_eq!(msg.message_type(), MessageType::Extended);
        assert_eq!(msg.extended_command(), ExtendedCommand::SetExtendedNoteInhibits);
        let exp_enabled = [
            EnableNote::from(1), EnableNote::none(), EnableNote::none(), EnableNote::none(),
            EnableNote::none(), EnableNote::none(), EnableNote::none(),  EnableNote::none(),
            EnableNote::none(), EnableNote::none(), EnableNote::none(),  EnableNote::none(),
            EnableNote::none(), EnableNote::none(), EnableNote::none(),  EnableNote::none(),
            EnableNote::none(), EnableNote::none(), EnableNote::none(),
        ];
        assert_eq!(msg.enabled_notes(), exp_enabled);
        Ok(())
    }
    #[test]
    fn test_display() -> Result<()> {
        let enabled = SetExtendedNoteInhibitsCFSC::new();
        let enabled_disp = r#"{"message_type": "Extended", "extended_command": "SetExtendedNoteInhibits", "denomination": "None", "operational_mode": {"orientation_control": "one way", "escrow_mode": "unset", "document_stack": "unset", "document_return": "unset"}, "configuration": {"no_push": "unset", "barcode": "unset", "power_up": "a", "extended_note": "set", "extended_coupon": "unset"}, "enabled_notes": [{"note_1": false, "note_2": false, "note_3": false, "note_4": false, "note_5": false, "note_6": false, "note_7": false}, {"note_8": false, "note_9": false, "note_10": false, "note_11": false, "note_12": false, "note_13": false, "note_14": false}, {"note_15": false, "note_16": false, "note_17": false, "note_18": false, "note_19": false, "note_20": false, "note_21": false}, {"note_22": false, "note_23": false, "note_24": false, "note_25": false, "note_26": false, "note_27": false, "note_28": false}, {"note_29": false, "note_30": false, "note_31": false, "note_32": false, "note_33": false, "note_34": false, "note_35": false}, {"note_36": false, "note_37": false, "note_38": false, "note_39": false, "note_40": false, "note_41": false, "note_42": false}, {"note_43": false, "note_44": false, "note_45": false, "note_46": false, "note_47": false, "note_48": false, "note_49": false}, {"note_50": false, "note_51": false, "note_52": false, "note_53": false, "note_54": false, "note_55": false, "note_56": false}]}"#;
        assert_eq!(format!("{enabled}").as_str(), enabled_disp);
        Ok(())
    }
}