use std::path::{PathBuf, Path};
use crate::error::FsResult;
use dochy_core::structs::{RootObject};
use std::collections::{BTreeMap};
use crate::imp::common::apply_items::{apply_items_st, apply_items_mt};
use crate::history::HistoryOptions;
use crate::common::{ CurrentSrc};
use crate::imp::common::archive::get_archive_path::get_archive_path2;
use crate::imp::common::current_src::current_src_map::get_current_src_info;
use dochy_core::archive_file_to_root;
pub(crate) struct DochyCache{
src_root : RootObject,
hash : u128,
pub(crate) phase_cache: BTreeMap<usize, (PathBuf, RootObject)>,
}
impl DochyCache{
pub fn get_or_create_hash_root<P:AsRef<Path>>(&self, history_dir : P, hash : u128) -> FsResult<RootObject>{
if self.hash() != hash {
Ok(archive_file_to_root(get_archive_path2(history_dir, hash), false)?)
} else{
Ok(self.clone_src_root())
}
}
pub(crate) fn new(current_src : CurrentSrc) -> FsResult<DochyCache>{
let info = get_current_src_info(current_src)?;
Ok(DochyCache{
src_root : info.clone_src_root(),
hash : info.hash(),
phase_cache : BTreeMap::new(),
})
}
pub fn hash(&self) -> u128{ self.hash }
pub fn clone_src_root(&self) -> RootObject{
self.src_root.clone()
}
pub fn apply_items_for_save(&mut self, paths: Vec<PathBuf>, op : &HistoryOptions) -> FsResult<RootObject> {
let first_len = paths.len();
let (root,paths) = get_cached_item(self.clone_src_root(), self, paths, op.max_phase())?;
let num_cached = first_len - paths.len();
remove_upper_phase_cache(&mut self.phase_cache, num_cached);
let mut current_index = num_cached;
let cache_func = |r : &RootObject| {
if current_index < op.max_phase() {
let path = &paths[current_index - num_cached];
self.phase_cache.insert(current_index, (path.to_path_buf(), r.clone()));
current_index += 1;
}
};
if op.mt_save() == false {
apply_items_st(root, &paths, cache_func)
} else{
apply_items_mt(root, paths.to_vec(), cache_func)
}
}
pub fn apply_items_for_load(&mut self, load_root : RootObject, paths: Vec<PathBuf>, op : &HistoryOptions) -> FsResult<RootObject> {
let (root,paths) = get_cached_item(load_root,self, paths, op.max_phase())?;
if op.mt_load() {
apply_items_mt(root, paths, |_| {})
} else{
apply_items_st(root, &paths, |_| {})
}
}
pub fn set_cache(&mut self, path : PathBuf, item: RootObject, phase: usize) {
remove_upper_phase_cache(&mut self.phase_cache, phase);
self.phase_cache.insert(phase, (path, item));
}
}
fn remove_upper_phase_cache(cache : &mut BTreeMap<usize, (PathBuf, RootObject)>, phase : usize){
loop{
let index = if let Some((index, _)) = cache.iter().last(){ *index }
else{
break;
};
if phase <= index{
cache.remove(&index);
} else{
break;
}
}
}
fn get_cached_item(root : RootObject, cache : &mut DochyCache, paths: Vec<PathBuf>, max_phase : usize) -> FsResult<(RootObject, Vec<PathBuf>)> {
if let Some(index) = get_phase_cache(&cache.phase_cache, &paths, max_phase){
if index == max_phase{
let (_,root) = cache.phase_cache.get(&index).unwrap();
return Ok((root.clone(), Vec::new()));
} else{
let root = {
let (_, root) = cache.phase_cache.get(&index).unwrap();
root.clone()
};
let ans = paths.into_iter().skip(index+1).collect();
return Ok((root, ans));
}
} else{
Ok((root, paths))
}
}
fn get_phase_cache(cache : &BTreeMap<usize, (PathBuf, RootObject)>, vec : &Vec<PathBuf>, max_phase : usize) -> Option<usize>{
let (index,(path, _)) = cache.iter().last()?;
let last_path = vec.iter().last()?;
if path == last_path {
return Some(*index);
}
for i in (0..max_phase).rev(){
if let Some((c,_)) = cache.get(&i){
if let Some(p) = vec.get(i){
if c == p{ return Some(i); }
}
}
}
return None;
}