pub mod reader;
pub mod sans_io;
#[cfg(feature = "tokio")]
pub mod tokio;
pub mod writer;
pub use self::reader::Reader;
#[cfg(feature = "tokio")]
pub use self::tokio::TokioReader;
#[cfg(feature = "tokio")]
pub use self::tokio::TokioWriter;
pub use self::writer::Writer;
const MAGIC_LEN: usize = 7;
const MAGIC: [u8; MAGIC_LEN] = *b"RGSSAD\0";
const VERSION: u8 = 1;
const HEADER_LEN: usize = MAGIC_LEN + 1;
const DEFAULT_KEY: u32 = 0xDEADCAFE;
const MAX_FILE_NAME_LEN: u32 = 4096;
const U32_LEN: usize = 4;
#[derive(Debug)]
pub enum Error {
    Io(std::io::Error),
    InvalidState,
    SansIo(self::sans_io::Error),
}
impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Io(_error) => write!(f, "an I/O error occured"),
            Self::InvalidState => write!(f, "user error, invalid internal state"),
            Self::SansIo(error) => error.fmt(f),
        }
    }
}
impl std::error::Error for Error {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            Self::Io(error) => Some(error),
            Self::SansIo(error) => error.source(),
            _ => None,
        }
    }
}
impl From<std::io::Error> for Error {
    fn from(error: std::io::Error) -> Self {
        Self::Io(error)
    }
}
impl From<self::sans_io::Error> for Error {
    fn from(error: self::sans_io::Error) -> Self {
        Self::SansIo(error)
    }
}
fn crypt_u32(key: &mut u32, mut n: u32) -> u32 {
    n ^= *key;
    *key = key.overflowing_mul(7).0.overflowing_add(3).0;
    n
}
fn crypt_name_bytes(key: &mut u32, bytes: &mut [u8]) {
    for byte in bytes.iter_mut() {
        *byte ^= u8::try_from(*key & 0xFF).unwrap();
        *key = key.overflowing_mul(7).0.overflowing_add(3).0;
    }
}
fn crypt_file_data(key: &mut u32, counter: &mut u8, buffer: &mut [u8]) {
    for byte in buffer.iter_mut() {
        *byte ^= key.to_le_bytes()[usize::from(*counter)];
        if *counter == 3 {
            *key = key.overflowing_mul(7).0.overflowing_add(3).0;
        }
        *counter = (*counter + 1) % 4;
    }
}
#[cfg(test)]
mod test {
    use super::*;
    use std::cell::RefCell;
    use std::io::Read;
    use std::io::Seek;
    use std::io::SeekFrom;
    use std::io::Write;
    use std::rc::Rc;
    pub const VX_TEST_GAME: &str =
        "test_data/RPGMakerVXTestGame-Export/RPGMakerVXTestGame/Game.rgss2a";
    #[derive(Debug, Clone)]
    struct SlowReader<R> {
        inner: Rc<RefCell<(R, usize, Option<SeekFrom>)>>,
    }
    impl<R> SlowReader<R> {
        pub fn new(reader: R) -> Self {
            Self {
                inner: Rc::new(RefCell::new((reader, 0, None))),
            }
        }
        fn add_fuel(&self, fuel: usize) {
            let mut inner = self.inner.borrow_mut();
            inner.1 += fuel;
        }
    }
    impl<R> Read for SlowReader<R>
    where
        R: Read,
    {
        fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
            let mut inner = self.inner.borrow_mut();
            let (reader, fuel, _) = &mut *inner;
            assert!(!buf.is_empty());
            let limit = std::cmp::min(*fuel, buf.len());
            let buf = &mut buf[..limit];
            if buf.is_empty() {
                return Err(std::io::Error::from(std::io::ErrorKind::WouldBlock));
            }
            let n = reader.read(buf)?;
            *fuel -= n;
            Ok(n)
        }
    }
    impl<R> Seek for SlowReader<R>
    where
        R: Seek,
    {
        fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
            let mut inner = self.inner.borrow_mut();
            let (reader, _, seek_request) = &mut *inner;
            match seek_request {
                Some(seek_request) => {
                    assert!(pos == *seek_request, "{pos:?} != {seek_request:?}");
                }
                None => {
                    *seek_request = Some(pos);
                    return Err(std::io::Error::from(std::io::ErrorKind::WouldBlock));
                }
            }
            let result = reader.seek(pos);
            *seek_request = None;
            result
        }
    }
    #[derive(Debug, Clone)]
    struct SlowWriter<W> {
        inner: Rc<RefCell<(W, usize, bool)>>,
    }
    impl<W> SlowWriter<W> {
        pub fn new(writer: W) -> Self {
            Self {
                inner: Rc::new(RefCell::new((writer, 0, false))),
            }
        }
        fn add_fuel(&self, fuel: usize) {
            let mut inner = self.inner.borrow_mut();
            inner.1 += fuel;
        }
    }
    impl<W> Write for SlowWriter<W>
    where
        W: Write,
    {
        fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
            let mut inner = self.inner.borrow_mut();
            let (writer, fuel, _) = &mut *inner;
            if *fuel == 0 {
                return Err(std::io::Error::from(std::io::ErrorKind::WouldBlock));
            }
            let len = std::cmp::min(*fuel, buf.len());
            let n = writer.write(&buf[..len])?;
            *fuel -= n;
            Ok(n)
        }
        fn flush(&mut self) -> std::io::Result<()> {
            let mut inner = self.inner.borrow_mut();
            let (writer, _, should_flush) = &mut *inner;
            if *should_flush {
                writer.flush()?;
                *should_flush = false;
                Ok(())
            } else {
                *should_flush = true;
                Err(std::io::Error::from(std::io::ErrorKind::WouldBlock))
            }
        }
    }
    #[test]
    fn reader_writer_smoke() {
        let file = std::fs::read(VX_TEST_GAME).expect("failed to open archive");
        let file = std::io::Cursor::new(file);
        let mut reader = Reader::new(file);
        reader.read_header().expect("failed to read header");
        let mut files = Vec::new();
        while let Some(mut file) = reader.read_file().expect("failed to read file") {
            let mut buffer = Vec::new();
            file.read_to_end(&mut buffer).expect("failed to read file");
            files.push((file.name().to_string(), buffer));
        }
        let mut new_file = Vec::new();
        let mut writer = Writer::new(&mut new_file);
        writer.write_header().expect("failed to write header");
        for (file_name, file_data) in files.iter() {
            writer
                .write_file(
                    file_name,
                    u32::try_from(file_data.len()).expect("file data too large"),
                    &**file_data,
                )
                .expect("failed to write file");
        }
        writer.finish().expect("failed to flush");
        let file = reader.into_inner();
        assert!(&new_file == file.get_ref());
    }
    #[test]
    fn slow_reader() {
        let file = std::fs::read(VX_TEST_GAME).expect("failed to open archive");
        let file = std::io::Cursor::new(file);
        let file = SlowReader::new(file);
        let mut reader = Reader::new(file.clone());
        loop {
            match reader.read_header() {
                Ok(()) => break,
                Err(Error::Io(error)) if error.kind() == std::io::ErrorKind::WouldBlock => {}
                Err(error) => {
                    panic!("Error: {error:?}");
                }
            }
            file.add_fuel(1);
        }
        loop {
            match reader.read_file() {
                Ok(Some(_file)) => {}
                Ok(None) => break,
                Err(Error::Io(error)) if error.kind() == std::io::ErrorKind::WouldBlock => {}
                Err(error) => {
                    panic!("Error: {error:?}");
                }
            }
            file.add_fuel(1);
        }
    }
    #[test]
    fn reader_slow_writer_smoke() {
        let file = std::fs::read(VX_TEST_GAME).expect("failed to open archive");
        let file = std::io::Cursor::new(file);
        let mut reader = Reader::new(file);
        reader.read_header().expect("failed to read header");
        let mut files = Vec::new();
        while let Some(mut file) = reader.read_file().expect("failed to read file") {
            let mut buffer = Vec::new();
            file.read_to_end(&mut buffer).expect("failed to read file");
            files.push((file.name().to_string(), buffer));
        }
        let new_file = SlowWriter::new(Vec::<u8>::new());
        let mut writer = Writer::new(new_file.clone());
        loop {
            match writer.write_header() {
                Ok(()) => break,
                Err(Error::Io(error)) if error.kind() == std::io::ErrorKind::WouldBlock => {
                    new_file.add_fuel(1);
                }
                Err(error) => {
                    panic!("failed to write header: {error}");
                }
            }
        }
        for (file_name, file_data) in files.iter() {
            let len = u32::try_from(file_data.len()).expect("file data too large");
            let mut reader = &**file_data;
            loop {
                match writer.write_file(file_name, len, &mut reader) {
                    Ok(()) => break,
                    Err(Error::Io(error)) if error.kind() == std::io::ErrorKind::WouldBlock => {
                        new_file.add_fuel(1);
                    }
                    Err(error) => {
                        panic!("failed to write file: {error}");
                    }
                }
            }
        }
        loop {
            match writer.finish() {
                Ok(()) => break,
                Err(Error::Io(error)) if error.kind() == std::io::ErrorKind::WouldBlock => {}
                Err(error) => {
                    panic!("failed to flush: {error}");
                }
            }
        }
        let file = reader.into_inner();
        let new_file = new_file.inner.borrow();
        let new_file = &new_file.0;
        let file = file.get_ref();
        dbg!(new_file.len(), file.len());
        assert!(new_file == file);
    }
}