use std::os::raw::{c_char, c_float, c_int};
use std::{
ffi::{CString, NulError},
path::Path,
};
use crate::errors::{ReadTrajError, TrajError};
use crate::prelude::TrajFile;
use crate::structures::simbox::SimBox;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct CXdrFile {
_empty: [u8; 0],
}
#[cfg(not(feature = "molly"))]
pub(crate) mod xtc {
use super::*;
extern "C" {
pub fn read_xtc(
xd: *mut CXdrFile,
natoms: c_int,
step: *mut c_int,
time: *mut c_float,
box_vec: *mut [[c_float; 3usize]; 3usize],
x: *mut [c_float; 3usize],
prec: *mut c_float,
) -> c_int;
pub fn read_xtc_natoms(filename: *const c_char, natoms: *mut c_int) -> c_int;
pub fn xtc_jump_to_start(xd: *mut CXdrFile, target_time: c_float) -> c_int;
pub fn xtc_skip_frame(xd: *mut CXdrFile) -> c_int;
pub fn xtc_skip_frame_with_time(xd: *mut CXdrFile, time: *mut c_float) -> c_int;
}
}
extern "C" {
pub fn xdrfile_open(path: *const c_char, mode: *const c_char) -> *mut CXdrFile;
pub fn xdrfile_close(xfp: *mut CXdrFile) -> c_int;
pub fn write_xtc(
xd: *mut CXdrFile,
natoms: c_int,
step: c_int,
time: c_float,
box_vec: *mut [[c_float; 3usize]; 3usize],
x: *mut [c_float; 3usize],
prec: c_float,
) -> c_int;
pub fn trr_jump_to_start(xd: *mut CXdrFile, target_time: c_float) -> c_int;
pub fn trr_skip_frame(xd: *mut CXdrFile) -> c_int;
pub fn trr_skip_frame_with_time(xd: *mut CXdrFile, time: *mut c_float) -> c_int;
pub fn read_trr(
xd: *mut CXdrFile,
natoms: c_int,
step: *mut c_int,
time: *mut c_float,
lambda: *mut c_float,
box_vec: *mut [[c_float; 3usize]; 3usize],
x: *mut [c_float; 3usize],
v: *mut [c_float; 3usize],
f: *mut [c_float; 3usize],
) -> c_int;
pub fn read_trr_natoms(filename: *const c_char, natoms: *mut c_int) -> c_int;
pub fn write_trr(
xd: *mut CXdrFile,
natoms: c_int,
step: c_int,
time: c_float,
lambda: c_float,
box_vec: *mut [[c_float; 3usize]; 3usize],
x: *mut [c_float; 3usize],
v: *mut [c_float; 3usize],
f: *mut [c_float; 3usize],
) -> c_int;
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum OpenMode {
Read,
Write,
}
#[derive(Debug)]
pub struct XdrFile {
pub handle: *mut CXdrFile,
}
impl Drop for XdrFile {
fn drop(&mut self) {
unsafe {
xdrfile_close(self.handle);
}
}
}
impl XdrFile {
pub(super) fn open_xdr(filename: impl AsRef<Path>, mode: OpenMode) -> Result<Self, TrajError> {
unsafe {
let c_path = match path2cstring(filename.as_ref()) {
Ok(x) => x,
Err(_) => return Err(TrajError::InvalidPath(Box::from(filename.as_ref()))),
};
let handle = xdrfile_open(c_path.as_ptr(), mode2cstring(mode).as_ptr());
if !handle.is_null() {
Ok(XdrFile { handle })
} else {
Err(TrajError::FileNotFound(Box::from(filename.as_ref())))
}
}
}
}
impl TrajFile for XdrFile {}
pub fn path2cstring(path: impl AsRef<Path>) -> Result<CString, NulError> {
CString::new(
path.as_ref()
.to_str()
.expect(
"FATAL GROAN ERROR | xdrfile::path2cstring | Could not convert path to CString.",
)
.as_bytes(),
)
}
pub fn mode2cstring(mode: OpenMode) -> CString {
match mode {
OpenMode::Read => c"r"
.to_owned(),
OpenMode::Write => c"w"
.to_owned(),
}
}
pub fn matrix2simbox(matrix: [[c_float; 3usize]; 3usize]) -> Result<SimBox, ReadTrajError> {
if matrix[0][1] != 0.0 || matrix[0][2] != 0.0 || matrix[1][2] != 0.0 {
return Err(ReadTrajError::InvalidSimBox);
}
Ok([
matrix[0][0],
matrix[1][1],
matrix[2][2],
matrix[0][1],
matrix[0][2],
matrix[1][0],
matrix[1][2],
matrix[2][0],
matrix[2][1],
]
.into())
}
pub fn simbox2matrix(simbox: Option<&SimBox>) -> [[c_float; 3usize]; 3usize] {
match simbox {
Some(simbox) => [
[simbox.v1x, simbox.v1y, simbox.v1z],
[simbox.v2x, simbox.v2y, simbox.v2z],
[simbox.v3x, simbox.v3y, simbox.v3z],
],
None => [[0.0; 3]; 3],
}
}