use crate::Manifest;
use anyhow::Result;
use std::io::{Seek, Write};
use zip::write::SimpleFileOptions;
use zip::ZipWriter;

pub struct ZcdWriter<W: Write + Seek> {
    zip: ZipWriter<W>,
    manifest: Manifest,
}

impl<W: Write + Seek> ZcdWriter<W> {
    pub fn new(writer: W) -> Self {
        Self {
            zip: ZipWriter::new(writer),
            manifest: Manifest::default(),
        }
    }

    pub fn set_name(&mut self, name: &str) {
        self.manifest.name = Some(name.to_string());
    }

    /// Write cordial source (.crd).
    pub fn write_source_crd(&mut self, source: &str) -> Result<()> {
        let options = SimpleFileOptions::default();
        self.zip.start_file("source/model.crd", options)?;
        self.zip.write_all(source.as_bytes())?;
        self.manifest.layers.source = true;
        Ok(())
    }

    /// Write SCAD source (legacy).
    pub fn write_source_scad(&mut self, source: &str) -> Result<()> {
        let options = SimpleFileOptions::default();
        self.zip.start_file("source/model.scad", options)?;
        self.zip.write_all(source.as_bytes())?;
        self.manifest.layers.source = true;
        Ok(())
    }

    /// Write serialized TrigGraph IR.
    pub fn write_trig(&mut self, trig_bytes: &[u8]) -> Result<()> {
        let options = SimpleFileOptions::default();
        self.zip.start_file("trig/scene.trig", options)?;
        self.zip.write_all(trig_bytes)?;
        self.manifest.layers.trig = true;
        Ok(())
    }

    pub fn write_shader(&mut self, wgsl_source: &str) -> Result<()> {
        let options = SimpleFileOptions::default();
        self.zip.start_file("shader/scene.wgsl", options)?;
        self.zip.write_all(wgsl_source.as_bytes())?;
        self.manifest.layers.shader = true;
        Ok(())
    }

    /// Write compiled CORDIC binary (.cord).
    pub fn write_cordic(&mut self, cordic_binary: &[u8], word_bits: u8) -> Result<()> {
        let options = SimpleFileOptions::default();
        self.zip.start_file("cordic/scene.cord", options)?;
        self.zip.write_all(cordic_binary)?;
        self.manifest.layers.cordic = true;
        self.manifest.cordic_word_bits = Some(word_bits);
        Ok(())
    }

    /// Write CORDIC configuration (TOML).
    pub fn write_config(&mut self, config_toml: &str) -> Result<()> {
        let options = SimpleFileOptions::default();
        self.zip.start_file("config/cordic.toml", options)?;
        self.zip.write_all(config_toml.as_bytes())?;
        self.manifest.layers.config = true;
        Ok(())
    }

    pub fn finish(mut self) -> Result<W> {
        let manifest_json = serde_json::to_string_pretty(&self.manifest)?;
        let options = SimpleFileOptions::default();
        self.zip.start_file("manifest.json", options)?;
        self.zip.write_all(manifest_json.as_bytes())?;
        Ok(self.zip.finish()?)
    }
}
