use crate::inspect::smp::special_conditions::*;

pub(in crate::inspect::smp) fn parse_post_special_conditions_scalar_probe(
    bytes: &[u8],
    file_extension_hint: Option<&str>,
    container_profile: Option<&SmpContainerProfile>,
    special_conditions_probe: Option<&SmpSpecialConditionsProbe>,
) -> Option<SmpPostSpecialConditionsScalarProbe> {
    special_conditions_probe?;
    if POST_SPECIAL_CONDITIONS_GROUNDED_TEXT_FIELD_FILE_END_OFFSET > bytes.len() {
        return None;
    }

    let dword_count =
        (POST_SPECIAL_CONDITIONS_SCALAR_END_OFFSET - POST_SPECIAL_CONDITIONS_SCALAR_OFFSET) / 4;
    let mut nonzero_lanes = Vec::new();
    for index in 0..dword_count {
        let absolute_offset = POST_SPECIAL_CONDITIONS_SCALAR_OFFSET + index * 4;
        let value = read_u32_at(bytes, absolute_offset)?;
        if value == 0 {
            continue;
        }
        nonzero_lanes.push(SmpPostSpecialConditionsScalarLane {
            absolute_offset,
            relative_offset: absolute_offset - POST_SPECIAL_CONDITIONS_SCALAR_OFFSET,
            absolute_offset_hex: format!("0x{absolute_offset:04x}"),
            relative_offset_hex: format!(
                "0x{:x}",
                absolute_offset - POST_SPECIAL_CONDITIONS_SCALAR_OFFSET
            ),
            value,
            value_hex: format!("0x{value:08x}"),
            probable_f32_le: probable_normal_f32_string(value),
        });
    }

    let source_kind = match file_extension_hint.unwrap_or("") {
        "gmp" => "map-post-special-conditions-window",
        "gms" => "save-post-special-conditions-window",
        "gmx" => "sandbox-post-special-conditions-window",
        _ => "post-special-conditions-window",
    }
    .to_string();
    let profile_family = container_profile
        .map(|profile| profile.profile_family.clone())
        .unwrap_or_else(|| "unknown".to_string());
    let first_nonzero_offset = nonzero_lanes.first().map(|lane| lane.absolute_offset);
    let last_nonzero_offset = nonzero_lanes.last().map(|lane| lane.absolute_offset);
    let overlap_nonzero_relative_offset_hexes = nonzero_lanes
        .iter()
        .filter(|lane| lane.absolute_offset < POST_SPECIAL_CONDITIONS_SCALAR_OVERLAP_END_OFFSET)
        .map(|lane| lane.relative_offset_hex.clone())
        .collect::<Vec<_>>();
    let tail_nonzero_lanes = nonzero_lanes
        .iter()
        .filter(|lane| lane.absolute_offset >= POST_SPECIAL_CONDITIONS_SCALAR_TAIL_OFFSET)
        .cloned()
        .collect::<Vec<_>>();
    let tail_first_nonzero_offset = tail_nonzero_lanes.first().map(|lane| lane.absolute_offset);
    let tail_last_nonzero_offset = tail_nonzero_lanes.last().map(|lane| lane.absolute_offset);
    let tail_nonzero_relative_offset_hexes = tail_nonzero_lanes
        .iter()
        .map(|lane| lane.relative_offset_hex.clone())
        .collect::<Vec<_>>();
    let grounded_text_field_remaining_file_window = &bytes[POST_SPECIAL_CONDITIONS_SCALAR_END_OFFSET
        ..POST_SPECIAL_CONDITIONS_GROUNDED_TEXT_FIELD_FILE_END_OFFSET];
    let mut grounded_text_field_remaining_nonzero_offsets = Vec::new();
    for (index, byte) in grounded_text_field_remaining_file_window.iter().enumerate() {
        if *byte != 0 {
            grounded_text_field_remaining_nonzero_offsets
                .push(POST_SPECIAL_CONDITIONS_SCALAR_END_OFFSET + index);
        }
    }
    let grounded_text_field_remaining_first_nonzero_offset =
        grounded_text_field_remaining_nonzero_offsets
            .first()
            .copied();
    let grounded_text_field_remaining_last_nonzero_offset =
        grounded_text_field_remaining_nonzero_offsets
            .last()
            .copied();
    let mut evidence = vec![
        format!(
            "fixed post-sentinel dword window at 0x{POST_SPECIAL_CONDITIONS_SCALAR_OFFSET:04x}..0x{POST_SPECIAL_CONDITIONS_SCALAR_END_OFFSET:04x}"
        ),
        "window starts immediately after the hidden special-conditions sentinel slot at 0x0df0"
            .to_string(),
        format!(
            "leading overlap prefix 0x{POST_SPECIAL_CONDITIONS_SCALAR_OFFSET:04x}..0x{POST_SPECIAL_CONDITIONS_SCALAR_OVERLAP_END_OFFSET:04x} aliases aligned runtime-rule band indices {}..{}",
            SMP_ALIGNED_RUNTIME_RULE_POST_WINDOW_OVERLAP_START_INDEX,
            SMP_ALIGNED_RUNTIME_RULE_POST_WINDOW_OVERLAP_START_INDEX
                + SMP_ALIGNED_RUNTIME_RULE_POST_WINDOW_OVERLAP_DWORD_COUNT
                - 1
        ),
        format!("save-only tail begins at 0x{POST_SPECIAL_CONDITIONS_SCALAR_TAIL_OFFSET:04x}"),
        format!(
            "that tail is offset-aligned with live runtime object bytes [world+0x{POST_SPECIAL_CONDITIONS_SCALAR_TAIL_RUNTIME_OBJECT_OFFSET:04x}..+0x{POST_SPECIAL_CONDITIONS_SCALAR_TAIL_RUNTIME_OBJECT_END_OFFSET:04x}]"
        ),
        format!(
            "the tail start lands on the grounded live field [world+0x{POST_SPECIAL_CONDITIONS_SCALAR_TAIL_GROUNDED_TEXT_FIELD_OFFSET:04x}], a 0x{POST_SPECIAL_CONDITIONS_SCALAR_TAIL_GROUNDED_TEXT_FIELD_COPY_LEN:x}-byte status-text buffer written by win/lose and winner-announcement helpers"
        ),
        format!(
            "current dword scan stops at 0x{POST_SPECIAL_CONDITIONS_SCALAR_END_OFFSET:04x}, leaving one byte-aligned continuation window 0x{POST_SPECIAL_CONDITIONS_SCALAR_END_OFFSET:04x}..0x{POST_SPECIAL_CONDITIONS_GROUNDED_TEXT_FIELD_FILE_END_OFFSET:04x} before the next clean live-field edge at [world+0x{POST_SPECIAL_CONDITIONS_SCALAR_TAIL_GROUNDED_TEXT_FIELD_COPY_END_OFFSET:04x}]"
        ),
        format!(
            "the next exact grounded fields after that edge begin at [world+0x{POST_TEXT_FIELD_0_RUNTIME_OBJECT_OFFSET:04x}], [world+0x{POST_TEXT_FIELD_1_RUNTIME_OBJECT_OFFSET:04x}], [world+0x{POST_TEXT_FIELD_2_RUNTIME_OBJECT_OFFSET:04x}], [world+0x{POST_SPECIAL_CONDITIONS_NEXT_GROUNDED_DWORD_FIELD_0_OFFSET:04x}], and [world+0x{POST_TEXT_FIELD_4_RUNTIME_OBJECT_OFFSET:04x}], which map to file offsets 0x{POST_TEXT_FIELD_NEIGHBORHOOD_OFFSET:04x}, 0x0f5d, 0x0f61, 0x{POST_SPECIAL_CONDITIONS_NEXT_GROUNDED_DWORD_FIELD_0_FILE_OFFSET:04x}, and 0x0f6d"
        ),
        format!(
            "the first grounded dword-sized fields after that edge are [world+0x{POST_SPECIAL_CONDITIONS_NEXT_GROUNDED_DWORD_FIELD_0_OFFSET:04x}] and [world+0x{POST_SPECIAL_CONDITIONS_NEXT_GROUNDED_DWORD_FIELD_1_OFFSET:04x}], which would land at file offsets 0x{POST_SPECIAL_CONDITIONS_NEXT_GROUNDED_DWORD_FIELD_0_FILE_OFFSET:04x} and 0x{POST_SPECIAL_CONDITIONS_NEXT_GROUNDED_DWORD_FIELD_1_FILE_OFFSET:04x}"
        ),
    ];
    if nonzero_lanes.is_empty() {
        evidence.push(
            "all observed dwords in this post-sentinel window are zero for this file".to_string(),
        );
    } else {
        evidence.push(format!(
            "observed {} nonzero dword lanes between {} and {}",
            nonzero_lanes.len(),
            nonzero_lanes
                .first()
                .map(|lane| lane.absolute_offset_hex.as_str())
                .unwrap_or("n/a"),
            nonzero_lanes
                .last()
                .map(|lane| lane.absolute_offset_hex.as_str())
                .unwrap_or("n/a")
        ));
        if nonzero_lanes
            .iter()
            .all(|lane| lane.probable_f32_le.is_some())
        {
            evidence.push(
                "every nonzero lane in this window also decodes as a normal finite little-endian f32"
                    .to_string(),
            );
        }
        evidence.push(format!(
            "{} nonzero lanes fall inside the aligned-band overlap prefix and {} fall inside the later tail",
            overlap_nonzero_relative_offset_hexes.len(),
            tail_nonzero_lanes.len()
        ));
    }
    evidence.push(
        "checked file bytes in the later tail are not yet validated as a byte-for-byte mirror of the live object, because the region aligned to [world+0x4b47] does not currently decode as preserved text in the checked saves"
            .to_string(),
    );
    if grounded_text_field_remaining_nonzero_offsets.is_empty() {
        evidence.push(
            "the remaining file window through the grounded text-field edge is all zero in this file"
                .to_string(),
        );
    } else {
        evidence.push(format!(
            "the remaining file window through the grounded text-field edge still has {} nonzero bytes between 0x{:04x} and 0x{:04x}",
            grounded_text_field_remaining_nonzero_offsets.len(),
            grounded_text_field_remaining_first_nonzero_offset.unwrap_or(0),
            grounded_text_field_remaining_last_nonzero_offset.unwrap_or(0)
        ));
    }

    Some(SmpPostSpecialConditionsScalarProbe {
        profile_family,
        source_kind,
        window_offset: POST_SPECIAL_CONDITIONS_SCALAR_OFFSET,
        window_end_offset: POST_SPECIAL_CONDITIONS_SCALAR_END_OFFSET,
        window_len: POST_SPECIAL_CONDITIONS_SCALAR_END_OFFSET
            - POST_SPECIAL_CONDITIONS_SCALAR_OFFSET,
        window_len_hex: format!(
            "0x{:x}",
            POST_SPECIAL_CONDITIONS_SCALAR_END_OFFSET - POST_SPECIAL_CONDITIONS_SCALAR_OFFSET
        ),
        dword_count,
        overlap_end_offset: POST_SPECIAL_CONDITIONS_SCALAR_OVERLAP_END_OFFSET,
        overlap_end_offset_hex: format!(
            "0x{POST_SPECIAL_CONDITIONS_SCALAR_OVERLAP_END_OFFSET:04x}"
        ),
        overlap_dword_count: (POST_SPECIAL_CONDITIONS_SCALAR_OVERLAP_END_OFFSET
            - POST_SPECIAL_CONDITIONS_SCALAR_OFFSET)
            / 4,
        overlap_nonzero_dword_count: overlap_nonzero_relative_offset_hexes.len(),
        overlap_nonzero_relative_offset_hexes,
        tail_offset: POST_SPECIAL_CONDITIONS_SCALAR_TAIL_OFFSET,
        tail_offset_hex: format!("0x{POST_SPECIAL_CONDITIONS_SCALAR_TAIL_OFFSET:04x}"),
        tail_len: POST_SPECIAL_CONDITIONS_SCALAR_END_OFFSET
            - POST_SPECIAL_CONDITIONS_SCALAR_TAIL_OFFSET,
        tail_len_hex: format!(
            "0x{:x}",
            POST_SPECIAL_CONDITIONS_SCALAR_END_OFFSET - POST_SPECIAL_CONDITIONS_SCALAR_TAIL_OFFSET
        ),
        tail_dword_count: (POST_SPECIAL_CONDITIONS_SCALAR_END_OFFSET
            - POST_SPECIAL_CONDITIONS_SCALAR_TAIL_OFFSET)
            / 4,
        tail_runtime_object_offset: POST_SPECIAL_CONDITIONS_SCALAR_TAIL_RUNTIME_OBJECT_OFFSET,
        tail_runtime_object_offset_hex: format!(
            "0x{POST_SPECIAL_CONDITIONS_SCALAR_TAIL_RUNTIME_OBJECT_OFFSET:04x}"
        ),
        tail_runtime_object_end_offset:
            POST_SPECIAL_CONDITIONS_SCALAR_TAIL_RUNTIME_OBJECT_END_OFFSET,
        tail_runtime_object_end_offset_hex: format!(
            "0x{POST_SPECIAL_CONDITIONS_SCALAR_TAIL_RUNTIME_OBJECT_END_OFFSET:04x}"
        ),
        tail_runtime_object_validated_byte_mirror: false,
        tail_grounded_live_field_offset:
            POST_SPECIAL_CONDITIONS_SCALAR_TAIL_GROUNDED_TEXT_FIELD_OFFSET,
        tail_grounded_live_field_offset_hex: format!(
            "0x{POST_SPECIAL_CONDITIONS_SCALAR_TAIL_GROUNDED_TEXT_FIELD_OFFSET:04x}"
        ),
        tail_grounded_live_field_name: "victory-or-outcome status text buffer".to_string(),
        tail_grounded_live_field_copy_len:
            POST_SPECIAL_CONDITIONS_SCALAR_TAIL_GROUNDED_TEXT_FIELD_COPY_LEN,
        tail_grounded_live_field_copy_len_hex: format!(
            "0x{:x}",
            POST_SPECIAL_CONDITIONS_SCALAR_TAIL_GROUNDED_TEXT_FIELD_COPY_LEN
        ),
        tail_grounded_live_field_copy_end_offset:
            POST_SPECIAL_CONDITIONS_SCALAR_TAIL_GROUNDED_TEXT_FIELD_COPY_END_OFFSET,
        tail_grounded_live_field_copy_end_offset_hex: format!(
            "0x{POST_SPECIAL_CONDITIONS_SCALAR_TAIL_GROUNDED_TEXT_FIELD_COPY_END_OFFSET:04x}"
        ),
        tail_window_cuts_through_grounded_live_field:
            POST_SPECIAL_CONDITIONS_SCALAR_TAIL_RUNTIME_OBJECT_END_OFFSET
                < POST_SPECIAL_CONDITIONS_SCALAR_TAIL_GROUNDED_TEXT_FIELD_COPY_END_OFFSET,
        tail_grounded_live_field_remaining_file_window_offset:
            POST_SPECIAL_CONDITIONS_SCALAR_END_OFFSET,
        tail_grounded_live_field_remaining_file_window_offset_hex: format!(
            "0x{POST_SPECIAL_CONDITIONS_SCALAR_END_OFFSET:04x}"
        ),
        tail_grounded_live_field_remaining_file_window_len:
            POST_SPECIAL_CONDITIONS_GROUNDED_TEXT_FIELD_FILE_END_OFFSET
                - POST_SPECIAL_CONDITIONS_SCALAR_END_OFFSET,
        tail_grounded_live_field_remaining_file_window_len_hex: format!(
            "0x{:x}",
            POST_SPECIAL_CONDITIONS_GROUNDED_TEXT_FIELD_FILE_END_OFFSET
                - POST_SPECIAL_CONDITIONS_SCALAR_END_OFFSET
        ),
        tail_grounded_live_field_remaining_file_window_nonzero_byte_count:
            grounded_text_field_remaining_nonzero_offsets.len(),
        tail_grounded_live_field_remaining_file_window_first_nonzero_offset:
            grounded_text_field_remaining_first_nonzero_offset,
        tail_grounded_live_field_remaining_file_window_first_nonzero_offset_hex:
            grounded_text_field_remaining_first_nonzero_offset
                .map(|offset| format!("0x{offset:04x}")),
        tail_grounded_live_field_remaining_file_window_last_nonzero_offset:
            grounded_text_field_remaining_last_nonzero_offset,
        tail_grounded_live_field_remaining_file_window_last_nonzero_offset_hex:
            grounded_text_field_remaining_last_nonzero_offset
                .map(|offset| format!("0x{offset:04x}")),
        tail_next_grounded_dword_field_offset:
            POST_SPECIAL_CONDITIONS_NEXT_GROUNDED_DWORD_FIELD_0_OFFSET,
        tail_next_grounded_dword_field_offset_hex: format!(
            "0x{POST_SPECIAL_CONDITIONS_NEXT_GROUNDED_DWORD_FIELD_0_OFFSET:04x}"
        ),
        tail_next_grounded_dword_field_file_offset:
            POST_SPECIAL_CONDITIONS_NEXT_GROUNDED_DWORD_FIELD_0_FILE_OFFSET,
        tail_next_grounded_dword_field_file_offset_hex: format!(
            "0x{POST_SPECIAL_CONDITIONS_NEXT_GROUNDED_DWORD_FIELD_0_FILE_OFFSET:04x}"
        ),
        tail_second_grounded_dword_field_offset:
            POST_SPECIAL_CONDITIONS_NEXT_GROUNDED_DWORD_FIELD_1_OFFSET,
        tail_second_grounded_dword_field_offset_hex: format!(
            "0x{POST_SPECIAL_CONDITIONS_NEXT_GROUNDED_DWORD_FIELD_1_OFFSET:04x}"
        ),
        tail_second_grounded_dword_field_file_offset:
            POST_SPECIAL_CONDITIONS_NEXT_GROUNDED_DWORD_FIELD_1_FILE_OFFSET,
        tail_second_grounded_dword_field_file_offset_hex: format!(
            "0x{POST_SPECIAL_CONDITIONS_NEXT_GROUNDED_DWORD_FIELD_1_FILE_OFFSET:04x}"
        ),
        post_text_field_file_alignment_matches_grounded_dword_fields: false,
        tail_nonzero_dword_count: tail_nonzero_lanes.len(),
        tail_first_nonzero_offset,
        tail_first_nonzero_offset_hex: tail_first_nonzero_offset
            .map(|offset| format!("0x{offset:04x}")),
        tail_last_nonzero_offset,
        tail_last_nonzero_offset_hex: tail_last_nonzero_offset
            .map(|offset| format!("0x{offset:04x}")),
        tail_nonzero_relative_offset_hexes,
        nonzero_dword_count: nonzero_lanes.len(),
        first_nonzero_offset,
        first_nonzero_offset_hex: first_nonzero_offset.map(|offset| format!("0x{offset:04x}")),
        last_nonzero_offset,
        last_nonzero_offset_hex: last_nonzero_offset.map(|offset| format!("0x{offset:04x}")),
        nonzero_lanes,
        evidence,
    })
}

pub(in crate::inspect::smp) fn parse_post_text_field_neighborhood_probe(
    bytes: &[u8],
    file_extension_hint: Option<&str>,
    container_profile: Option<&SmpContainerProfile>,
    special_conditions_probe: Option<&SmpSpecialConditionsProbe>,
) -> Option<SmpPostTextFieldNeighborhoodProbe> {
    special_conditions_probe?;
    if POST_TEXT_FIELD_NEIGHBORHOOD_END_OFFSET > bytes.len() {
        return None;
    }

    let profile_family = container_profile
        .map(|profile| profile.profile_family.clone())
        .unwrap_or_else(|| "unknown".to_string());
    let source_kind = match file_extension_hint.unwrap_or("") {
        "gmp" => "post-text-grounded-field-neighborhood",
        "gms" => "post-text-grounded-field-neighborhood",
        "gmx" => "post-text-grounded-field-neighborhood",
        _ => "post-text-grounded-field-neighborhood",
    }
    .to_string();

    let exact_fields = [
        (
            "Auto-Show Grade During Track Lay",
            POST_TEXT_FIELD_0_RUNTIME_OBJECT_OFFSET,
            POST_TEXT_FIELD_NEIGHBORHOOD_OFFSET,
            1usize,
        ),
        (
            "Starting Building Density Level",
            POST_TEXT_FIELD_1_RUNTIME_OBJECT_OFFSET,
            0x0f5dusize,
            1usize,
        ),
        (
            "Building Density Growth",
            POST_TEXT_FIELD_2_RUNTIME_OBJECT_OFFSET,
            0x0f61usize,
            1usize,
        ),
        (
            "leftover simulation time accumulator",
            POST_TEXT_FIELD_3_RUNTIME_OBJECT_OFFSET,
            0x0f65usize,
            4usize,
        ),
        (
            "selected-year lane snapshot",
            POST_TEXT_FIELD_4_RUNTIME_OBJECT_OFFSET,
            0x0f6dusize,
            1usize,
        ),
        (
            "late locomotive policy gate dword",
            POST_TEXT_FIELD_5_RUNTIME_OBJECT_OFFSET,
            0x0f71usize,
            4usize,
        ),
    ];

    let grounded_field_observations = exact_fields
        .iter()
        .map(
            |(field_name, runtime_object_offset, file_offset, field_width_bytes)| {
                let raw = &bytes[*file_offset..*file_offset + *field_width_bytes];
                let raw_hex = hex_encode(raw);
                let (value_u8, value_u8_hex, value_u32, value_u32_hex, probable_f32_le) =
                    if *field_width_bytes == 1 {
                        let value = raw[0];
                        (
                            Some(value),
                            Some(format!("0x{value:02x}")),
                            None,
                            None,
                            None,
                        )
                    } else {
                        let value = u32::from_le_bytes([raw[0], raw[1], raw[2], raw[3]]);
                        (
                            None,
                            None,
                            Some(value),
                            Some(format!("0x{value:08x}")),
                            probable_normal_f32_string(value),
                        )
                    };
                SmpPostTextGroundedFieldObservation {
                    field_name: (*field_name).to_string(),
                    runtime_object_offset: *runtime_object_offset,
                    runtime_object_offset_hex: format!("0x{runtime_object_offset:04x}"),
                    file_offset: *file_offset,
                    file_offset_hex: format!("0x{file_offset:04x}"),
                    field_width_bytes: *field_width_bytes,
                    field_width_bytes_hex: format!("0x{field_width_bytes:x}"),
                    raw_hex,
                    value_u8,
                    value_u8_hex,
                    value_u32,
                    value_u32_hex,
                    probable_f32_le,
                }
            },
        )
        .collect::<Vec<_>>();

    let one_byte_early_float_candidates = exact_fields
        .iter()
        .filter(|(_, _, file_offset, _)| *file_offset > 0)
        .filter_map(|(field_name, runtime_object_offset, file_offset, _)| {
            let candidate_offset = file_offset - 1;
            let value = read_u32_at(bytes, candidate_offset)?;
            let probable_f32_le = probable_normal_f32_string(value)?;
            Some(SmpPostTextFloatAlignmentCandidate {
                grounded_field_name: (*field_name).to_string(),
                grounded_field_runtime_object_offset: *runtime_object_offset,
                grounded_field_runtime_object_offset_hex: format!("0x{runtime_object_offset:04x}"),
                grounded_field_file_offset: *file_offset,
                grounded_field_file_offset_hex: format!("0x{file_offset:04x}"),
                candidate_offset,
                candidate_offset_hex: format!("0x{candidate_offset:04x}"),
                candidate_value: value,
                candidate_value_hex: format!("0x{value:08x}"),
                probable_f32_le,
            })
        })
        .collect::<Vec<_>>();

    let mut evidence = vec![
        format!(
            "post-text grounded-field neighborhood spans file offsets 0x{POST_TEXT_FIELD_NEIGHBORHOOD_OFFSET:04x}..0x{POST_TEXT_FIELD_NEIGHBORHOOD_END_OFFSET:04x}"
        ),
        "this neighborhood starts at the first grounded post-text field [world+0x4c74] and extends through the later dword at [world+0x4c8c]".to_string(),
        "the exact grounded field offsets here are byte-oriented at 0x0f59, 0x0f5d, 0x0f61, and 0x0f6d, with dword-sized fields only at 0x0f65 and 0x0f71".to_string(),
    ];
    if one_byte_early_float_candidates.is_empty() {
        evidence.push(
            "no one-byte-early little-endian float-looking starts were observed ahead of the grounded fields in this file".to_string(),
        );
    } else {
        evidence.push(format!(
            "observed {} float-looking 4-byte starts exactly one byte before grounded field offsets in this file",
            one_byte_early_float_candidates.len()
        ));
    }

    Some(SmpPostTextFieldNeighborhoodProbe {
        profile_family,
        source_kind,
        window_offset: POST_TEXT_FIELD_NEIGHBORHOOD_OFFSET,
        window_end_offset: POST_TEXT_FIELD_NEIGHBORHOOD_END_OFFSET,
        window_len: POST_TEXT_FIELD_NEIGHBORHOOD_END_OFFSET - POST_TEXT_FIELD_NEIGHBORHOOD_OFFSET,
        window_len_hex: format!(
            "0x{:x}",
            POST_TEXT_FIELD_NEIGHBORHOOD_END_OFFSET - POST_TEXT_FIELD_NEIGHBORHOOD_OFFSET
        ),
        grounded_field_observations,
        one_byte_early_float_candidates,
        evidence,
    })
}
