mod image_export;
mod litematica;
mod mcfunction;

use lib::{BlockPalette, StructureExporter};

pub use image_export::PngExporter;
pub use litematica::LitematicaExporter;
pub use mcfunction::McFunctionExporter;

type ExporterFactory = fn(&BlockPalette) -> Box<dyn StructureExporter>;

const REGISTRY: &[(&str, ExporterFactory)] = &[
    ("mcfunction", |p| Box::new(McFunctionExporter::new(p))),
    ("litematica", |p| Box::new(LitematicaExporter::new(p))),
    // cell_size=16 default; CLI uses build_png() to honour --cell-size
    ("png", |p| Box::new(PngExporter::new(p, 16))),
];

pub fn available_names() -> Vec<&'static str> {
    REGISTRY.iter().map(|(name, _)| *name).collect()
}

pub fn build_png(palette: &BlockPalette, cell_size: u32) -> Box<dyn StructureExporter> {
    Box::new(PngExporter::new(palette, cell_size))
}

pub fn build(name: &str, palette: &BlockPalette) -> Option<Box<dyn StructureExporter>> {
    REGISTRY.iter()
        .find(|(n, _)| *n == name)
        .map(|(_, factory)| factory(palette))
}

#[cfg(test)]
mod tests {
    use super::*;

    fn dummy_palette() -> BlockPalette {
        serde_json::from_str(r#"{
            "name": "test",
            "blocks": {
                "body": "minecraft:stone",
                "outline": "minecraft:cobblestone",
                "shadow": "minecraft:gravel"
            }
        }"#).unwrap()
    }

    #[test]
    fn build_mcfunction_returns_some() {
        let p = dummy_palette();
        assert!(build("mcfunction", &p).is_some());
    }

    #[test]
    fn build_litematica_returns_some() {
        let p = dummy_palette();
        assert!(build("litematica", &p).is_some());
    }

    #[test]
    fn build_unknown_returns_none() {
        let p = dummy_palette();
        assert!(build("foo", &p).is_none());
    }

    #[test]
    fn available_names_contains_both() {
        let names = available_names();
        assert!(names.contains(&"mcfunction"));
        assert!(names.contains(&"litematica"));
    }
}
