use std::ops::Range;
use crate::file::Index;
pub mod offset_by_kind {
    use std::fmt::{Display, Formatter};
    #[allow(missing_docs)]
    #[derive(Debug)]
    pub struct Error {
        pub kind: crate::Id,
    }
    impl Display for Error {
        fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
            write!(
                f,
                "Chunk named {:?} was not found in chunk file index",
                std::str::from_utf8(&self.kind).unwrap_or("<non-ascii>")
            )
        }
    }
    impl std::error::Error for Error {}
}
pub mod data_by_kind {
    #[derive(Debug, thiserror::Error)]
    #[allow(missing_docs)]
    pub enum Error {
        #[error("The chunk wasn't found in the file index")]
        NotFound(#[from] super::offset_by_kind::Error),
        #[error("The offsets into the file couldn't be represented by usize")]
        FileTooLarge,
    }
}
pub struct Entry {
    pub kind: crate::Id,
    pub offset: Range<crate::file::Offset>,
}
impl Index {
    pub const ENTRY_SIZE: usize = std::mem::size_of::<u32>() + std::mem::size_of::<u64>();
    pub const EMPTY_SIZE: usize = Index::ENTRY_SIZE;
    pub const fn size_for_entries(num_entries: usize) -> usize {
        Self::ENTRY_SIZE * (num_entries + 1)
    }
    pub fn offset_by_id(&self, kind: crate::Id) -> Result<Range<crate::file::Offset>, offset_by_kind::Error> {
        self.chunks
            .iter()
            .find_map(|c| (c.kind == kind).then(|| c.offset.clone()))
            .ok_or(offset_by_kind::Error { kind })
    }
    pub fn usize_offset_by_id(&self, kind: crate::Id) -> Result<Range<usize>, offset_by_kind::Error> {
        self.chunks
            .iter()
            .find_map(|c| (c.kind == kind).then(|| crate::range::into_usize_or_panic(c.offset.clone())))
            .ok_or(offset_by_kind::Error { kind })
    }
    pub fn validated_usize_offset_by_id<T>(
        &self,
        kind: crate::Id,
        validate: impl FnOnce(Range<usize>) -> T,
    ) -> Result<T, offset_by_kind::Error> {
        self.chunks
            .iter()
            .find_map(|c| (c.kind == kind).then(|| crate::range::into_usize_or_panic(c.offset.clone())))
            .map(validate)
            .ok_or(offset_by_kind::Error { kind })
    }
    pub fn data_by_id<'a>(&self, data: &'a [u8], kind: crate::Id) -> Result<&'a [u8], data_by_kind::Error> {
        let offset = self.offset_by_id(kind)?;
        Ok(&data[crate::range::into_usize(offset).ok_or(data_by_kind::Error::FileTooLarge)?])
    }
    pub fn highest_offset(&self) -> crate::file::Offset {
        self.chunks.last().expect("at least one chunk").offset.end
    }
}