use crate::manifest::{Manifest, Record};

use crate::ffi::utils::{ForeignObject, SourmashStr};

pub struct SourmashManifest;

impl ForeignObject for SourmashManifest {
    type RustObject = Manifest;
}

#[no_mangle]
pub unsafe extern "C" fn manifest_free(ptr: *mut SourmashManifest) {
    SourmashManifest::drop(ptr);
}

pub struct ManifestRowIterator {
    iter: Box<dyn Iterator<Item = &'static Record>>,
}

pub struct SourmashManifestRowIter;

impl ForeignObject for SourmashManifestRowIter {
    type RustObject = ManifestRowIterator;
}

#[no_mangle]
pub unsafe extern "C" fn manifest_rows_iter_next(
    ptr: *mut SourmashManifestRowIter,
) -> *const SourmashManifestRow {
    let iterator = SourmashManifestRowIter::as_rust_mut(ptr);

    match iterator.iter.next() {
        Some(row) => SourmashManifestRow::from_rust(row.into()),
        None => std::ptr::null(),
    }
}

#[no_mangle]
pub unsafe extern "C" fn manifest_rows(
    ptr: *const SourmashManifest,
) -> *mut SourmashManifestRowIter {
    let manifest = SourmashManifest::as_rust(ptr);

    let iter = Box::new(manifest.iter());
    SourmashManifestRowIter::from_rust(ManifestRowIterator { iter })
}

#[repr(C)]
pub struct SourmashManifestRow {
    pub ksize: u32,
    pub with_abundance: bool,
    pub md5: SourmashStr,
    pub internal_location: SourmashStr,
    pub name: SourmashStr,
    pub moltype: SourmashStr,
    pub n_hashes: usize,
    pub num: u32,
    pub scaled: u32,
    pub filename: SourmashStr,
}

impl ForeignObject for SourmashManifestRow {
    type RustObject = SourmashManifestRow;
}

#[no_mangle]
pub unsafe extern "C" fn manifestrow_free(ptr: *mut SourmashManifestRow) {
    SourmashManifestRow::drop(ptr);
}

impl From<&Record> for SourmashManifestRow {
    fn from(record: &Record) -> SourmashManifestRow {
        Self {
            ksize: record.ksize(),
            with_abundance: record.with_abundance(),
            md5: record.md5().clone().into(),
            name: record.name().clone().into(),
            moltype: record.moltype().to_string().into(),
            internal_location: record.internal_location().to_string().into(),
            n_hashes: *record.n_hashes(),
            num: *record.num(),
            scaled: *record.scaled(),
            filename: record.filename().to_string().into(),
        }
    }
}
