use super::read::read_u32_le;
use super::types::{
    WinAnonymousSelectorRecord, WinResourceRecordSample, WinResourceReference,
    WinResourceSelectorRecord,
};

pub(super) fn build_resource_selector_records(
    samples: &[WinResourceRecordSample],
) -> Vec<WinResourceSelectorRecord> {
    samples
        .iter()
        .map(|sample| {
            let post_name_word_1_high_u16 = ((sample.post_name_word_1 >> 16) & 0xffff) as u16;
            let post_name_word_1_middle_u16 = ((sample.post_name_word_1 >> 8) & 0xffff) as u16;
            let post_name_word_1_low_u16 = (sample.post_name_word_1 & 0xffff) as u16;
            WinResourceSelectorRecord {
                offset: sample.offset,
                offset_hex: sample.offset_hex.clone(),
                name: sample.name.clone(),
                post_name_word_0: sample.post_name_word_0,
                post_name_word_0_hex: sample.post_name_word_0_hex.clone(),
                selector_high_u16: sample.post_name_word_0_high_u16,
                selector_high_u16_hex: sample.post_name_word_0_high_u16_hex.clone(),
                selector_low_u16: sample.post_name_word_0_low_u16,
                selector_low_u16_hex: sample.post_name_word_0_low_u16_hex.clone(),
                post_name_word_1: sample.post_name_word_1,
                post_name_word_1_hex: sample.post_name_word_1_hex.clone(),
                post_name_word_1_high_u16,
                post_name_word_1_high_u16_hex: format!("0x{post_name_word_1_high_u16:04x}"),
                post_name_word_1_middle_u16,
                post_name_word_1_middle_u16_hex: format!("0x{post_name_word_1_middle_u16:04x}"),
                post_name_word_1_low_u16,
                post_name_word_1_low_u16_hex: format!("0x{post_name_word_1_low_u16:04x}"),
            }
        })
        .collect()
}

pub(super) fn collect_anonymous_selector_records(
    bytes: &[u8],
    references: &[WinResourceReference],
) -> Vec<WinAnonymousSelectorRecord> {
    const PRELUDE: [u8; 12] = [
        0xb8, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x0b, 0x00, 0x00,
    ];

    let mut records = Vec::new();
    let mut start = 0usize;
    while let Some(relative) = bytes.get(start..).and_then(|slice| {
        slice
            .windows(PRELUDE.len())
            .position(|window| window == PRELUDE)
    }) {
        let record_offset = start + relative;
        let name_len = read_u32_le(bytes, record_offset + PRELUDE.len()).unwrap_or(0);
        if name_len == 0 {
            let selector_word_0 = read_u32_le(bytes, record_offset + 0x10).unwrap_or(0);
            let selector_word_0_low_u16 = (selector_word_0 & 0xffff) as u16;
            if (0xc352..=0xc39b).contains(&selector_word_0_low_u16) {
                let preceding_named_record = references
                    .iter()
                    .rev()
                    .find(|reference| reference.offset < record_offset);
                let following_named_record = references
                    .iter()
                    .find(|reference| reference.offset > record_offset);
                let selector_word_1 = read_u32_le(bytes, record_offset + 0x14).unwrap_or(0);
                let selector_word_0_high_u16 = ((selector_word_0 >> 16) & 0xffff) as u16;
                let selector_word_1_middle_u16 = ((selector_word_1 >> 8) & 0xffff) as u16;
                let body_word_0 = read_u32_le(bytes, record_offset + 0x18).unwrap_or(0);
                let body_word_1 = read_u32_le(bytes, record_offset + 0x1c).unwrap_or(0);
                let body_word_2 = read_u32_le(bytes, record_offset + 0x20).unwrap_or(0);
                let body_word_3 = read_u32_le(bytes, record_offset + 0x24).unwrap_or(0);
                let footer_word_0 = read_u32_le(bytes, record_offset + 0x98).unwrap_or(0);
                let footer_word_1 = read_u32_le(bytes, record_offset + 0x9c).unwrap_or(0);
                records.push(WinAnonymousSelectorRecord {
                    record_offset,
                    record_offset_hex: format!("0x{record_offset:04x}"),
                    preceding_named_record_name: preceding_named_record
                        .map(|record| record.name.clone()),
                    preceding_named_record_offset_hex: preceding_named_record
                        .map(|record| record.offset_hex.clone()),
                    following_named_record_name: following_named_record
                        .map(|record| record.name.clone()),
                    following_named_record_offset_hex: following_named_record
                        .map(|record| record.offset_hex.clone()),
                    selector_word_0,
                    selector_word_0_hex: format!("0x{selector_word_0:08x}"),
                    selector_word_0_high_u16,
                    selector_word_0_high_u16_hex: format!("0x{selector_word_0_high_u16:04x}"),
                    selector_word_0_low_u16,
                    selector_word_0_low_u16_hex: format!("0x{selector_word_0_low_u16:04x}"),
                    selector_word_1,
                    selector_word_1_hex: format!("0x{selector_word_1:08x}"),
                    selector_word_1_middle_u16,
                    selector_word_1_middle_u16_hex: format!("0x{selector_word_1_middle_u16:04x}"),
                    body_word_0,
                    body_word_0_hex: format!("0x{body_word_0:08x}"),
                    body_word_1,
                    body_word_1_hex: format!("0x{body_word_1:08x}"),
                    body_word_2,
                    body_word_2_hex: format!("0x{body_word_2:08x}"),
                    body_word_3,
                    body_word_3_hex: format!("0x{body_word_3:08x}"),
                    footer_word_0,
                    footer_word_0_hex: format!("0x{footer_word_0:08x}"),
                    footer_word_1,
                    footer_word_1_hex: format!("0x{footer_word_1:08x}"),
                });
            }
        }
        start = record_offset + 1;
    }
    records
}
