use std::ffi::OsStr;
use std::path::Path;
use brodo::cache::{CacheEntry, CacheFile};
use brodo::field::AnyField;
use brodo::vdb::{VdbEntry, VdbFile};
use brodo::vtk::write;
use crate::args::{Format, Input};
use crate::error::MestoloError;
use crate::load::{Grid, Loaded, Opened, load, open};
pub fn convert(
input: &Input,
grid: Option<&str>,
format: Option<Format>,
output: &Path,
) -> Result<(), MestoloError> {
let by_name = || match output.extension().and_then(OsStr::to_str) {
Some("vdb") => Ok(Format::Vdb),
Some("vtu") => Ok(Format::Vtu),
_ => Err(MestoloError::OutputKind(output.to_path_buf())),
};
match format.map_or_else(by_name, Ok)? {
Format::Vdb => vdb(&load(input)?, grid, output),
Format::Vtu => vtu(load(input)?.grid(grid)?, output),
Format::Cache => Ok(brodo::cache::write(output, &entries(&open(input)?, grid)?)?),
Format::Archive => Ok(brodo::archive::write(output, &entries(&open(input)?, grid)?)?),
}
}
fn vdb(loaded: &Loaded, grid: Option<&str>, output: &Path) -> Result<(), MestoloError> {
let grids = match grid {
Some(_) => vec![loaded.grid(grid)?],
None => loaded.grids.iter().collect(),
};
let not_written: Vec<_> = grids
.iter()
.filter(|g| !matches!(g.field, AnyField::F32(_) | AnyField::F64(_)))
.map(|g| (g.meta.name.clone(), g.field.value_type()))
.collect();
if !not_written.is_empty() {
return Err(MestoloError::NotWritten(not_written));
}
let entries = grids.into_iter().map(entry).collect::<Result<Vec<_>, _>>()?;
Ok(brodo::vdb::write(output, &entries)?)
}
fn entry(g: &Grid) -> Result<VdbEntry, MestoloError> {
let (m, t) = (&g.meta, &g.topology);
match &g.field {
AnyField::F32(f) => Ok(VdbEntry::new(m, t, f)?),
AnyField::F64(f) => Ok(VdbEntry::new(m, t, f)?),
AnyField::U8(_) | AnyField::U32(_) | AnyField::F32x3(_) | AnyField::F64x3(_) | _ => {
Err(MestoloError::ValueType(g.field.value_type()))
}
}
}
fn entries(opened: &Opened, grid: Option<&str>) -> Result<Vec<CacheEntry>, MestoloError> {
match opened {
Opened::Cache { file, .. } => copied(file, grid),
Opened::Vdb(file) => from_vdb(file, grid),
}
}
fn copied(file: &CacheFile, grid: Option<&str>) -> Result<Vec<CacheEntry>, MestoloError> {
let grids = file.grids();
let Some(name) = grid else {
let bytes = file.bytes_entries().into_iter().map(CacheEntry::copy_bytes);
return Ok(grids.into_iter().map(CacheEntry::copy).chain(bytes).collect());
};
let g = grids.into_iter().find(|g| g.meta().name == name);
Ok(vec![CacheEntry::copy(g.ok_or_else(|| MestoloError::NoGrid(name.to_owned()))?)])
}
fn from_vdb(file: &VdbFile, grid: Option<&str>) -> Result<Vec<CacheEntry>, MestoloError> {
let chosen: Vec<_> = match grid {
None => file.grids.iter().collect(),
Some(name) => vec![
file.grids
.iter()
.find(|g| g.meta.name == name)
.ok_or_else(|| MestoloError::NoGrid(name.to_owned()))?,
],
};
chosen
.into_iter()
.map(|g| match &g.field {
AnyField::F32(f) => Ok(CacheEntry::new(&g.meta, &g.topology, f)?),
AnyField::F64(f) => Ok(CacheEntry::new(&g.meta, &g.topology, f)?),
AnyField::U8(_) | AnyField::U32(_) | AnyField::F32x3(_) | AnyField::F64x3(_) | _ => {
Err(MestoloError::ValueType(g.field.value_type()))
}
})
.collect()
}
fn vtu(g: &Grid, output: &Path) -> Result<(), MestoloError> {
let (t, name) = (&g.topology, g.meta.name.as_str());
match &g.field {
AnyField::F32(f) => write(output, t, f, name)?,
AnyField::F64(f) => write(output, t, f, name)?,
AnyField::U8(f) => write(output, t, f, name)?,
AnyField::U32(f) => write(output, t, f, name)?,
AnyField::F32x3(f) => write(output, t, f, name)?,
AnyField::F64x3(f) => write(output, t, f, name)?,
_ => return Err(MestoloError::ValueType(g.field.value_type())),
}
Ok(())
}