use crate::io::{error::Error, util};
use crate::model::system::System;
use std::io::Write;

pub fn write<W: Write>(mut writer: W, system: &System) -> Result<(), Error> {
    let atom_count = system.atom_count();
    let bond_count = system.bond_count();

    writeln!(writer, "SDF Export")?;
    writeln!(writer, "dreid-forge")?;
    writeln!(writer)?;
    writeln!(
        writer,
        "{:>3}{:>3}  0  0  0  0  0  0  0  0  0999 V2000",
        atom_count, bond_count
    )?;

    for atom in &system.atoms {
        writeln!(
            writer,
            "{:>10.4}{:>10.4}{:>10.4} {:<3} 0  0  0  0  0  0  0  0  0  0  0  0",
            atom.position[0],
            atom.position[1],
            atom.position[2],
            atom.element.symbol()
        )?;
    }

    for bond in &system.bonds {
        writeln!(
            writer,
            "{:>3}{:>3}{:>3}  0  0  0  0",
            bond.i + 1,
            bond.j + 1,
            util::bond_order_to_ctfile(bond.order)
        )?;
    }

    writeln!(writer, "M  END")?;
    writeln!(writer, "$$$$")?;
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::io::sdf::reader;
    use crate::model::{
        atom::Atom,
        system::Bond,
        types::{BondOrder, Element},
    };
    use std::io::Cursor;

    #[test]
    fn writes_and_reads_roundtrip() {
        let system = System {
            atoms: vec![
                Atom::new(Element::C, [0.0, 0.0, 0.0]),
                Atom::new(Element::O, [1.0, 0.0, 0.0]),
                Atom::new(Element::H, [0.0, 1.0, 0.0]),
            ],
            bonds: vec![
                Bond::new(0, 1, BondOrder::Double),
                Bond::new(0, 2, BondOrder::Single),
            ],
            box_vectors: None,
            bio_metadata: None,
        };

        let mut buf = Vec::new();
        write(&mut buf, &system).expect("write sdf");
        let parsed = reader::read(Cursor::new(buf)).expect("read sdf");

        assert_eq!(parsed.atom_count(), system.atom_count());
        assert_eq!(parsed.bond_count(), system.bond_count());
        for (a, b) in system.atoms.iter().zip(parsed.atoms.iter()) {
            assert_eq!(a.element, b.element);
            for k in 0..3 {
                assert!((a.position[k] - b.position[k]).abs() < 1e-4);
            }
        }
        assert_eq!(parsed.bonds, system.bonds);
    }
}
