use crate::inspect::smp::bundle::{
    SmpContainerProfile, SmpRuntimeAnchorCycleBlock, SmpSaveAnchorRunBlock, SmpSaveBootstrapBlock,
    SmpSecondaryVariantProbe,
};
use crate::inspect::smp::common::{read_u32_at, read_u32_window};

pub(in crate::inspect::smp) fn parse_save_bootstrap_block(
    container_profile: Option<&SmpContainerProfile>,
    secondary_variant_probe: Option<&SmpSecondaryVariantProbe>,
) -> Option<SmpSaveBootstrapBlock> {
    let profile = container_profile?;
    let secondary = secondary_variant_probe?;
    let words = &secondary.words;
    if words.len() < 8 {
        return None;
    }

    let supported = matches!(
        profile.profile_family.as_str(),
        "rt3-classic-save-container-v1"
            | "rt3-105-save-container-v1"
            | "rt3-105-scenario-save-container-v1"
            | "rt3-105-alt-save-container-v1"
    );
    if !supported {
        return None;
    }

    Some(SmpSaveBootstrapBlock {
        profile_family: profile.profile_family.clone(),
        aligned_window_offset: secondary.aligned_window_offset,
        leading_word: words[0],
        leading_word_hex: format!("0x{:08x}", words[0]),
        anchor_word: words[1],
        anchor_word_hex: format!("0x{:08x}", words[1]),
        descriptor_word_2: words[2],
        descriptor_word_2_hex: format!("0x{:08x}", words[2]),
        descriptor_word_3: words[3],
        descriptor_word_3_hex: format!("0x{:08x}", words[3]),
        descriptor_word_4: words[4],
        descriptor_word_4_hex: format!("0x{:08x}", words[4]),
        descriptor_word_5: words[5],
        descriptor_word_5_hex: format!("0x{:08x}", words[5]),
        descriptor_word_6: words[6],
        descriptor_word_6_hex: format!("0x{:08x}", words[6]),
        descriptor_word_7: words[7],
        descriptor_word_7_hex: format!("0x{:08x}", words[7]),
    })
}

pub(in crate::inspect::smp) fn parse_runtime_anchor_cycle_block(
    bytes: &[u8],
    container_profile: Option<&SmpContainerProfile>,
    secondary_variant_probe: Option<&SmpSecondaryVariantProbe>,
) -> Option<SmpRuntimeAnchorCycleBlock> {
    let profile = container_profile?;
    let secondary = secondary_variant_probe?;
    let supported = matches!(
        profile.profile_family.as_str(),
        "rt3-classic-save-container-v1"
            | "rt3-classic-sandbox-container-v1"
            | "rt3-105-save-container-v1"
            | "rt3-105-scenario-save-container-v1"
            | "rt3-105-alt-save-container-v1"
            | "rt3-105-sandbox-container-v1"
    );
    if !supported {
        return None;
    }

    let cycle_start_offset = secondary.aligned_window_offset + 0x1c;
    let cycle_words = read_u32_window(bytes, cycle_start_offset, 9);
    if cycle_words.len() < 9 {
        return None;
    }

    let mut full_cycle_count = 0usize;
    let mut cursor = cycle_start_offset;
    while read_u32_window(bytes, cursor, cycle_words.len()) == cycle_words {
        full_cycle_count += 1;
        cursor += cycle_words.len() * 4;
    }

    if full_cycle_count == 0 {
        return None;
    }

    let mut partial_cycle_word_count = 0usize;
    while partial_cycle_word_count < cycle_words.len() {
        let offset = cursor + partial_cycle_word_count * 4;
        if read_u32_at(bytes, offset) == Some(cycle_words[partial_cycle_word_count]) {
            partial_cycle_word_count += 1;
        } else {
            break;
        }
    }

    let trailer_offset = cursor + partial_cycle_word_count * 4;
    let trailer_words = read_u32_window(bytes, trailer_offset, 16);

    Some(SmpRuntimeAnchorCycleBlock {
        profile_family: profile.profile_family.clone(),
        cycle_start_offset,
        cycle_hex_words: cycle_words
            .iter()
            .map(|word| format!("0x{word:08x}"))
            .collect(),
        cycle_words,
        full_cycle_count,
        partial_cycle_word_count,
        trailer_offset,
        trailer_hex_words: trailer_words
            .iter()
            .map(|word| format!("0x{word:08x}"))
            .collect(),
        trailer_words,
    })
}

pub(in crate::inspect::smp) fn parse_save_anchor_run_block(
    bytes: &[u8],
    container_profile: Option<&SmpContainerProfile>,
    save_bootstrap_block: Option<&SmpSaveBootstrapBlock>,
) -> Option<SmpSaveAnchorRunBlock> {
    let profile = container_profile?;
    let bootstrap = save_bootstrap_block?;
    let supported = matches!(
        profile.profile_family.as_str(),
        "rt3-classic-save-container-v1"
            | "rt3-105-save-container-v1"
            | "rt3-105-scenario-save-container-v1"
            | "rt3-105-alt-save-container-v1"
    );
    if !supported {
        return None;
    }

    let cycle_start_offset = bootstrap.aligned_window_offset + 0x1c;
    let cycle_words = read_u32_window(bytes, cycle_start_offset, 9);
    if cycle_words.len() < 9 {
        return None;
    }

    let mut full_cycle_count = 0usize;
    let mut cursor = cycle_start_offset;
    while read_u32_window(bytes, cursor, cycle_words.len()) == cycle_words {
        full_cycle_count += 1;
        cursor += cycle_words.len() * 4;
    }

    if full_cycle_count == 0 {
        return None;
    }

    let mut partial_cycle_word_count = 0usize;
    while partial_cycle_word_count < cycle_words.len() {
        let offset = cursor + partial_cycle_word_count * 4;
        if read_u32_at(bytes, offset) == Some(cycle_words[partial_cycle_word_count]) {
            partial_cycle_word_count += 1;
        } else {
            break;
        }
    }

    let trailer_offset = cursor + partial_cycle_word_count * 4;
    let trailer_words = read_u32_window(bytes, trailer_offset, 12);

    Some(SmpSaveAnchorRunBlock {
        profile_family: profile.profile_family.clone(),
        cycle_start_offset,
        cycle_hex_words: cycle_words
            .iter()
            .map(|word| format!("0x{word:08x}"))
            .collect(),
        cycle_words,
        full_cycle_count,
        partial_cycle_word_count,
        trailer_offset,
        trailer_hex_words: trailer_words
            .iter()
            .map(|word| format!("0x{word:08x}"))
            .collect(),
        trailer_words,
    })
}
