use std::collections::HashMap;
use std::fs::File;
use std::io::{Read, Seek, SeekFrom};
use std::path::{Path, PathBuf};
use crate::byte_order_chirho::{ZBlockIndexChirho, ZVerseIndexChirho, ReadSwordChirho};
use crate::compression_chirho::CompressorChirho;
use crate::error_chirho::{ErrorChirho, ResultChirho};
use crate::{BlockTypeChirho, CompressionTypeChirho};
pub struct ZVerseChirho {
path_chirho: PathBuf,
block_type_chirho: BlockTypeChirho,
ot_verse_idx_chirho: Option<File>,
nt_verse_idx_chirho: Option<File>,
ot_block_idx_chirho: Option<File>,
nt_block_idx_chirho: Option<File>,
ot_data_chirho: Option<File>,
nt_data_chirho: Option<File>,
compressor_chirho: Box<dyn CompressorChirho>,
block_cache_chirho: HashMap<(u8, u32), Vec<u8>>,
}
impl ZVerseChirho {
fn extension_prefix_chirho(comp_type_chirho: CompressionTypeChirho) -> &'static str {
match comp_type_chirho {
CompressionTypeChirho::ZipChirho => "c",
CompressionTypeChirho::Bzip2Chirho => "b",
CompressionTypeChirho::XzChirho => "x",
CompressionTypeChirho::LzssChirho => "c", CompressionTypeChirho::NoneChirho => "c", }
}
pub fn open_chirho<P: AsRef<Path>>(
path_chirho: P,
compressor_chirho: Box<dyn CompressorChirho>,
) -> ResultChirho<Self> {
Self::open_with_comp_type_chirho(path_chirho, compressor_chirho, CompressionTypeChirho::ZipChirho)
}
pub fn open_with_comp_type_chirho<P: AsRef<Path>>(
path_chirho: P,
compressor_chirho: Box<dyn CompressorChirho>,
comp_type_chirho: CompressionTypeChirho,
) -> ResultChirho<Self> {
let path_chirho = path_chirho.as_ref().to_path_buf();
let ext_prefix_chirho = Self::extension_prefix_chirho(comp_type_chirho);
let extensions_chirho = [
ext_prefix_chirho,
"c", "b", "x", ];
let mut ot_verse_idx_chirho = None;
let mut nt_verse_idx_chirho = None;
let mut ot_block_idx_chirho = None;
let mut nt_block_idx_chirho = None;
let mut ot_data_chirho = None;
let mut nt_data_chirho = None;
for ext_chirho in &extensions_chirho {
if ot_verse_idx_chirho.is_none() {
ot_verse_idx_chirho = File::open(path_chirho.join(format!("ot.{}zv", ext_chirho))).ok();
}
if nt_verse_idx_chirho.is_none() {
nt_verse_idx_chirho = File::open(path_chirho.join(format!("nt.{}zv", ext_chirho))).ok();
}
if ot_block_idx_chirho.is_none() {
ot_block_idx_chirho = File::open(path_chirho.join(format!("ot.{}zs", ext_chirho))).ok();
}
if nt_block_idx_chirho.is_none() {
nt_block_idx_chirho = File::open(path_chirho.join(format!("nt.{}zs", ext_chirho))).ok();
}
if ot_data_chirho.is_none() {
ot_data_chirho = File::open(path_chirho.join(format!("ot.{}zz", ext_chirho))).ok();
}
if nt_data_chirho.is_none() {
nt_data_chirho = File::open(path_chirho.join(format!("nt.{}zz", ext_chirho))).ok();
}
if (ot_verse_idx_chirho.is_some() || nt_verse_idx_chirho.is_some())
&& (ot_block_idx_chirho.is_some() || nt_block_idx_chirho.is_some())
&& (ot_data_chirho.is_some() || nt_data_chirho.is_some())
{
break;
}
}
if ot_verse_idx_chirho.is_none() && nt_verse_idx_chirho.is_none() {
return Err(ErrorChirho::InvalidModulePathChirho { path_chirho });
}
Ok(Self {
path_chirho,
block_type_chirho: BlockTypeChirho::ChapterBlocksChirho,
ot_verse_idx_chirho,
nt_verse_idx_chirho,
ot_block_idx_chirho,
nt_block_idx_chirho,
ot_data_chirho,
nt_data_chirho,
compressor_chirho,
block_cache_chirho: HashMap::new(),
})
}
pub fn set_block_type_chirho(&mut self, block_type_chirho: BlockTypeChirho) {
self.block_type_chirho = block_type_chirho;
}
pub fn find_verse_index_chirho(
&mut self,
testament_chirho: u8,
idx_off_chirho: u32,
) -> ResultChirho<ZVerseIndexChirho> {
let byte_offset_chirho = idx_off_chirho as u64 * ZVerseIndexChirho::SIZE_CHIRHO as u64;
let idx_file_chirho = match testament_chirho {
1 => self.ot_verse_idx_chirho.as_mut(),
2 => self.nt_verse_idx_chirho.as_mut(),
_ => return Err(ErrorChirho::generic_chirho("Invalid testament")),
};
let idx_file_chirho = idx_file_chirho
.ok_or_else(|| ErrorChirho::generic_chirho("Verse index not available"))?;
idx_file_chirho.seek(SeekFrom::Start(byte_offset_chirho))?;
let block_num_chirho = idx_file_chirho.read_u32_sword_chirho()?;
let offset_in_block_chirho = idx_file_chirho.read_u32_sword_chirho()?;
let size_chirho = idx_file_chirho.read_u16_sword_chirho()?;
Ok(ZVerseIndexChirho {
block_num_chirho,
offset_in_block_chirho,
size_chirho,
})
}
pub fn find_block_index_chirho(
&mut self,
testament_chirho: u8,
block_num_chirho: u32,
) -> ResultChirho<ZBlockIndexChirho> {
let byte_offset_chirho = block_num_chirho as u64 * ZBlockIndexChirho::SIZE_CHIRHO as u64;
let idx_file_chirho = match testament_chirho {
1 => self.ot_block_idx_chirho.as_mut(),
2 => self.nt_block_idx_chirho.as_mut(),
_ => return Err(ErrorChirho::generic_chirho("Invalid testament")),
};
let idx_file_chirho = idx_file_chirho
.ok_or_else(|| ErrorChirho::generic_chirho("Block index not available"))?;
idx_file_chirho.seek(SeekFrom::Start(byte_offset_chirho))?;
let comp_offset_chirho = idx_file_chirho.read_u32_sword_chirho()?;
let comp_size_chirho = idx_file_chirho.read_u32_sword_chirho()?;
let uncomp_size_chirho = idx_file_chirho.read_u32_sword_chirho()?;
Ok(ZBlockIndexChirho {
comp_offset_chirho,
comp_size_chirho,
uncomp_size_chirho,
})
}
pub fn read_block_chirho(
&mut self,
testament_chirho: u8,
block_num_chirho: u32,
) -> ResultChirho<Vec<u8>> {
if let Some(data_chirho) = self.block_cache_chirho.get(&(testament_chirho, block_num_chirho)) {
return Ok(data_chirho.clone());
}
let block_idx_chirho = self.find_block_index_chirho(testament_chirho, block_num_chirho)?;
if block_idx_chirho.is_empty_chirho() {
return Ok(Vec::new());
}
let data_file_chirho = match testament_chirho {
1 => self.ot_data_chirho.as_mut(),
2 => self.nt_data_chirho.as_mut(),
_ => return Err(ErrorChirho::generic_chirho("Invalid testament")),
};
let data_file_chirho = data_file_chirho
.ok_or_else(|| ErrorChirho::generic_chirho("Data file not available"))?;
data_file_chirho.seek(SeekFrom::Start(block_idx_chirho.comp_offset_chirho as u64))?;
let mut compressed_chirho = vec![0u8; block_idx_chirho.comp_size_chirho as usize];
data_file_chirho.read_exact(&mut compressed_chirho)?;
let decompressed_chirho = self.compressor_chirho.decompress_chirho(&compressed_chirho)?;
self.block_cache_chirho.insert(
(testament_chirho, block_num_chirho),
decompressed_chirho.clone(),
);
Ok(decompressed_chirho)
}
pub fn read_verse_chirho(
&mut self,
testament_chirho: u8,
idx_off_chirho: u32,
) -> ResultChirho<String> {
let verse_idx_chirho = self.find_verse_index_chirho(testament_chirho, idx_off_chirho)?;
if verse_idx_chirho.is_empty_chirho() {
return Ok(String::new());
}
let block_data_chirho = self.read_block_chirho(testament_chirho, verse_idx_chirho.block_num_chirho)?;
let start_chirho = verse_idx_chirho.offset_in_block_chirho as usize;
let end_chirho = start_chirho + verse_idx_chirho.size_chirho as usize;
if end_chirho > block_data_chirho.len() {
return Err(ErrorChirho::generic_chirho("Verse extends beyond block"));
}
let text_bytes_chirho = &block_data_chirho[start_chirho..end_chirho];
match String::from_utf8(text_bytes_chirho.to_vec()) {
Ok(s) => Ok(s),
Err(_) => Ok(text_bytes_chirho.iter().map(|&b| b as char).collect()),
}
}
pub fn clear_cache_chirho(&mut self) {
self.block_cache_chirho.clear();
}
pub fn path_chirho(&self) -> &Path {
&self.path_chirho
}
}