use std::ffi::{CStr, CString};
use std::os::raw::{c_char, c_int, c_void};
use std::path::Path;
use std::ptr;
use fitsio::FitsFile;
use fitsio::errors::check_status;
use fitsio::sys::{LONGLONG, fitsfile};
use crate::error::{AtfitsError, Result};
use crate::image::is_structural_keyword;
pub struct CubeLayout {
pub header: Vec<u8>,
pub datastart: u64,
}
pub fn create_mem_cube(template: &Path, bitpix: i64, dims: &[usize]) -> Result<FitsFile> {
let mut status = 0;
let memname = CString::new("mem://").expect("static name has no NUL");
let mut raw: *mut fitsfile = ptr::null_mut();
unsafe {
fitsio::sys::ffinit(&mut raw, memname.as_ptr(), &mut status);
}
check_status(status)?;
let mut naxes: Vec<LONGLONG> = dims.iter().map(|&d| d as LONGLONG).collect();
unsafe {
fitsio::sys::ffcrimll(
raw,
bitpix as c_int,
naxes.len() as c_int,
naxes.as_mut_ptr(),
&mut status,
);
}
if let Err(e) = check_status(status) {
let mut close = 0;
unsafe { fitsio::sys::ffclos(raw, &mut close) };
return Err(e.into());
}
let mut in_fptr = FitsFile::open(template.to_string_lossy().as_ref())?;
in_fptr.primary_hdu()?;
unsafe {
let mut nkeys: c_int = 0;
let mut morekeys: c_int = 0;
fitsio::sys::ffghsp(in_fptr.as_raw(), &mut nkeys, &mut morekeys, &mut status);
check_status(status)?;
let mut card = [0 as c_char; 81];
for i in 1..=nkeys {
card.fill(0);
fitsio::sys::ffgrec(in_fptr.as_raw(), i, card.as_mut_ptr(), &mut status);
if check_status(status).is_err() {
break;
}
let card_str = CStr::from_ptr(card.as_ptr()).to_string_lossy();
let name = card_str.split([' ', '=']).next().unwrap_or("").trim();
if is_structural_keyword(name) {
continue;
}
fitsio::sys::ffprec(raw, card.as_ptr(), &mut status);
check_status(status)?;
}
}
let handle = unsafe { FitsFile::from_raw(raw, fitsio::FileOpenMode::READWRITE)? };
Ok(handle)
}
pub fn extract_header_layout(fptr: &mut FitsFile) -> Result<CubeLayout> {
fptr.primary_hdu()?; let raw = unsafe { fptr.as_raw() };
let mut status = 0;
let mut headstart: LONGLONG = 0;
let mut datastart: LONGLONG = 0;
let mut dataend: LONGLONG = 0;
unsafe {
fitsio::sys::ffghadll(
raw,
&mut headstart,
&mut datastart,
&mut dataend,
&mut status,
);
}
check_status(status)?;
let mut header_ptr: *mut c_char = ptr::null_mut();
let mut nkeys: c_int = 0;
unsafe {
fitsio::sys::ffhdr2str(
raw,
0, ptr::null_mut(),
0,
&mut header_ptr,
&mut nkeys,
&mut status,
);
}
check_status(status)?;
let mut header = unsafe {
let bytes = CStr::from_ptr(header_ptr).to_bytes().to_vec();
let mut free_status = 0;
fitsio::sys::fffree(header_ptr as *mut c_void, &mut free_status);
bytes
};
let datastart = datastart as u64;
if header.len() as u64 + 3 > datastart {
return Err(AtfitsError::Other(format!(
"header ({} bytes) does not fit before data unit ({datastart} bytes)",
header.len()
)));
}
header.extend_from_slice(b"END");
header.resize(datastart as usize, b' ');
Ok(CubeLayout { header, datastart })
}