use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use crate::model::{SectionTrace, SurfaceCut, SurfaceRead, WellRead};
use crate::read::surfaces::Prepared;
use crate::read::wells::HeadRead;
use crate::spatial::{Index, Rect};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Work {
pub wells_read: usize,
pub surfaces_read: usize,
pub surfaces_cut: usize,
pub sections_cut: usize,
pub reused: usize,
pub heads_computed: usize,
pub spatial_queries: usize,
}
#[derive(Clone, Debug)]
pub(crate) enum HeadSlot {
Head { at: usize, head: Box<HeadRead> },
Unusable,
}
#[derive(Clone, Debug, PartialEq)]
struct Key {
store: String,
revision: u64,
epsg: Option<u64>,
unit: Option<String>,
time_factor: Option<u64>,
}
type Line = Vec<[u64; 2]>;
fn line_of(section: &SectionTrace) -> Line {
section
.trace
.iter()
.map(|p| [p[0].to_bits(), p[1].to_bits()])
.collect()
}
#[derive(Default)]
pub struct PackageCache {
key: Option<Key>,
wells: HashMap<String, WellRead>,
surfaces: HashMap<String, Arc<Prepared>>,
cuts: HashMap<(String, Line), Option<SurfaceCut>>,
work: Work,
heads: HashMap<String, HeadSlot>,
spatial: Index<usize>,
head_uris: Vec<String>,
}
impl PackageCache {
pub fn new() -> Self {
Self::default()
}
pub fn work(&self) -> Work {
self.work
}
pub fn clear(&mut self) {
self.key = None;
self.wells.clear();
self.surfaces.clear();
self.cuts.clear();
self.heads.clear();
self.spatial = Index::new();
self.head_uris.clear();
}
pub(crate) fn hold_for(&mut self, store: &str, revision: u64, scene: &crate::read::Scene<'_>) {
let key = Key {
store: store.to_string(),
revision,
epsg: scene.system.map(|s| s.epsg),
unit: scene.system.map(|s| s.unit.clone()),
time_factor: scene.time_factor.map(f64::to_bits),
};
if self.key.as_ref() != Some(&key) {
self.clear();
self.key = Some(key);
}
}
pub(crate) fn well<E>(
&mut self,
uri: &str,
read: impl FnOnce() -> Result<WellRead, E>,
) -> Result<WellRead, E> {
if let Some(kept) = self.wells.get(uri) {
self.work.reused += 1;
return Ok(kept.clone());
}
let well = read()?;
self.work.wells_read += 1;
self.wells.insert(uri.to_string(), well.clone());
Ok(well)
}
pub(crate) fn head(&mut self, uri: &str, read: impl FnOnce() -> Option<HeadRead>) -> &HeadSlot {
if !self.heads.contains_key(uri) {
self.work.heads_computed += 1;
let slot = match read() {
Some(head) if head.head.iter().all(|v| v.is_finite()) => {
let at = self.head_uris.len();
self.head_uris.push(uri.to_string());
if let Some(rect) = Rect::point([head.head[0], head.head[1]]) {
self.spatial.insert(rect, at);
}
HeadSlot::Head {
at,
head: Box::new(head),
}
}
_ => HeadSlot::Unusable,
};
self.heads.insert(uri.to_string(), slot);
}
&self.heads[uri]
}
pub(crate) fn heads_in(&mut self, windows: &[Rect]) -> HashSet<usize> {
let mut found = HashSet::new();
for window in windows {
self.work.spatial_queries += 1;
self.spatial.visit(window, &mut |at| {
found.insert(*at);
});
}
found
}
pub(crate) fn surface<E>(
&mut self,
uri: &str,
sections: &[SectionTrace],
prepare: impl FnOnce() -> Result<Prepared, E>,
) -> Result<SurfaceRead, E> {
let lines: Vec<Line> = sections.iter().map(line_of).collect();
let mut seen: HashSet<&Line> = HashSet::new();
let missing: Vec<usize> = lines
.iter()
.enumerate()
.filter(|(_, line)| {
!self.cuts.contains_key(&(uri.to_string(), (*line).clone())) && seen.insert(line)
})
.map(|(at, _)| at)
.collect();
let prepared = match self.surfaces.get(uri) {
Some(kept) => Arc::clone(kept),
None => {
let placed = Arc::new(prepare()?);
self.work.surfaces_read += 1;
self.surfaces.insert(uri.to_string(), Arc::clone(&placed));
placed
}
};
if missing.is_empty() {
self.work.reused += 1;
} else {
let asked: Vec<SectionTrace> = missing.iter().map(|at| sections[*at].clone()).collect();
let made = crate::read::surfaces::cut_prepared(&prepared, &asked);
self.work.surfaces_cut += 1;
self.work.sections_cut += asked.len();
for (at, one) in missing.iter().zip(&asked) {
let found = made.cuts.iter().find(|cut| cut.section == one.key).cloned();
self.cuts.insert((uri.to_string(), lines[*at].clone()), found);
}
}
let cuts = sections
.iter()
.zip(&lines)
.filter_map(|(section, line)| {
let kept = self.cuts.get(&(uri.to_string(), line.clone()))?.as_ref()?;
Some(SurfaceCut {
section: section.key,
..kept.clone()
})
})
.collect();
Ok(SurfaceRead {
object: prepared.object.clone(),
role: prepared.role,
time: prepared.time,
colour: prepared.colour.clone(),
age: prepared.age,
feature: prepared.feature.clone(),
cuts,
})
}
}