use crate::Manifest;
use anyhow::{Context, Result};
use std::io::{Read, Seek};
use zip::ZipArchive;

pub struct ZcdReader<R: Read + Seek> {
    archive: ZipArchive<R>,
}

impl<R: Read + Seek> ZcdReader<R> {
    pub fn new(reader: R) -> Result<Self> {
        let archive = ZipArchive::new(reader)?;
        Ok(Self { archive })
    }

    pub fn manifest(&mut self) -> Result<Manifest> {
        let mut file = self.archive.by_name("manifest.json")
            .context("missing manifest.json")?;
        let mut buf = String::new();
        file.read_to_string(&mut buf)?;
        Ok(serde_json::from_str(&buf)?)
    }

    /// Read source, trying .crd first then .scad fallback.
    pub fn read_source(&mut self) -> Result<Option<String>> {
        for path in &["source/model.crd", "source/model.scad"] {
            if let Ok(mut file) = self.archive.by_name(path) {
                let mut buf = String::new();
                file.read_to_string(&mut buf)?;
                return Ok(Some(buf));
            }
        }
        Ok(None)
    }

    pub fn read_trig(&mut self) -> Result<Option<Vec<u8>>> {
        match self.archive.by_name("trig/scene.trig") {
            Ok(mut file) => {
                let mut buf = Vec::new();
                file.read_to_end(&mut buf)?;
                Ok(Some(buf))
            }
            Err(_) => Ok(None),
        }
    }

    pub fn read_shader(&mut self) -> Result<Option<String>> {
        match self.archive.by_name("shader/scene.wgsl") {
            Ok(mut file) => {
                let mut buf = String::new();
                file.read_to_string(&mut buf)?;
                Ok(Some(buf))
            }
            Err(_) => Ok(None),
        }
    }

    /// Read CORDIC binary, trying new path then legacy fallback.
    pub fn read_cordic(&mut self) -> Result<Option<Vec<u8>>> {
        for path in &["cordic/scene.cord", "cordic/scene.bin"] {
            if let Ok(mut file) = self.archive.by_name(path) {
                let mut buf = Vec::new();
                file.read_to_end(&mut buf)?;
                return Ok(Some(buf));
            }
        }
        Ok(None)
    }

    /// Read CORDIC configuration (TOML).
    pub fn read_config(&mut self) -> Result<Option<String>> {
        match self.archive.by_name("config/cordic.toml") {
            Ok(mut file) => {
                let mut buf = String::new();
                file.read_to_string(&mut buf)?;
                Ok(Some(buf))
            }
            Err(_) => Ok(None),
        }
    }
}
