use super::lines::Line;
use super::state::{charge_in, serial_in};
use crate::fixed;
use molframe_core::column::Presence;
use molframe_core::element::Element;
use molframe_core::execution::ExecutionContext;
use molframe_core::parallel::{BlockPlan, map_blocks_in};
const BLOCK_LINES: usize = 2048;
#[derive(Clone, Copy)]
pub(super) struct AtomNumbers {
pub(super) position: [Option<f64>; 3],
pub(super) occupancy: Option<f64>,
pub(super) b_factor: Option<f64>,
pub(super) serial: u32,
pub(super) charge: (i8, Presence),
pub(super) declared: Option<Element>,
}
impl AtomNumbers {
fn parse(text: &str) -> Self {
Self {
position: [
fixed::real(text, 31, 38),
fixed::real(text, 39, 46),
fixed::real(text, 47, 54),
],
occupancy: fixed::real(text, 55, 60),
b_factor: fixed::real(text, 61, 66),
serial: serial_in(text),
charge: charge_in(text),
declared: Element::from_symbol(fixed::text(text, 77, 78)),
}
}
}
pub(super) fn decode_window(
lines: &[Line<'_>],
context: &ExecutionContext,
) -> Vec<Option<AtomNumbers>> {
let plan = BlockPlan::new(lines.len(), BLOCK_LINES);
let blocks = map_blocks_in(plan, context, |_, range| {
let Some(block) = lines.get(range) else {
return Vec::new();
};
block
.iter()
.map(|line| {
matches!(fixed::record(line.text), "ATOM" | "HETATM")
.then(|| AtomNumbers::parse(line.text))
})
.collect::<Vec<_>>()
});
match blocks {
Ok(blocks) => blocks.into_iter().flatten().collect(),
Err(_) => Vec::new(),
}
}