use itertools::Itertools;
use super::worksheet::Worksheet;
use crate::errors::Result;
#[derive(Debug, Clone)]
pub struct CellPositioner<const INTERVAL: usize> {
cache_height: Vec<(usize, f64)>,
cache_width: Vec<(usize, f64)>,
}
impl<const INTERVAL: usize> CellPositioner<INTERVAL> {
pub fn new() -> Self {
CellPositioner {
cache_height: vec![],
cache_width: vec![],
}
}
pub fn clear(&mut self) {
self.cache_height = vec![];
self.cache_width = vec![];
}
pub fn get_row_start_y(&mut self, row: usize, ws: &Worksheet) -> Result<f64> {
let (mut curr, mut result) = self.find_closest_cache_height(row);
if curr == row {
return Ok(result);
}
let reverse = curr > row;
while curr != row {
if ws.is_row_hidden(row) {
curr = advance(reverse, curr);
continue;
}
let h = ws.get_row_height(curr)?;
result += h;
self.add_cache(true, curr, result);
}
Ok(result)
}
pub fn get_col_start_x(&mut self, col: usize, ws: &Worksheet) -> Result<f64> {
let (mut curr, mut result) = self.find_closest_cache_width(col);
if curr == col {
return Ok(result);
}
let reverse = curr > col;
while curr != col {
if ws.is_col_hidden(col) {
curr = advance(reverse, curr);
continue;
}
let h = ws.get_row_height(curr)?;
result += h;
self.add_cache(true, curr, result);
}
Ok(result)
}
pub fn get_nearest_row_before_given_y(
&mut self,
y: f64,
ws: &Worksheet,
) -> Result<(usize, f64)> {
let (mut curr_idx, mut curr_h) = self.find_closest_cache_before_y(y);
let mut h = 0.;
while curr_h < y {
if ws.is_row_hidden(curr_idx) {
curr_idx += 1;
continue;
}
h = ws.get_row_height(curr_idx)?;
curr_idx += 1;
curr_h += h;
self.add_cache(true, curr_idx, curr_h);
}
return Ok((curr_idx - 1, curr_h - h));
}
pub fn get_nearest_col_before_given_x(
&mut self,
x: f64,
ws: &Worksheet,
) -> Result<(usize, f64)> {
let (mut curr_idx, mut curr_w) = self.find_closest_cache_before_x(x);
let mut w = 0.;
while curr_w < x {
if ws.is_row_hidden(curr_idx) {
curr_idx += 1;
continue;
}
w = ws.get_row_height(curr_idx)?;
curr_idx += 1;
curr_w += w;
self.add_cache(false, curr_idx, curr_w);
}
return Ok((curr_idx - 1, curr_w - w));
}
fn find_closest_cache_before_x(&self, x: f64) -> (usize, f64) {
if self.cache_width.is_empty() {
return (0, 0.);
}
let r = self.cache_width.iter().find_position(|(_, v)| *v > x);
if let Some((idx, _)) = r {
self.cache_width.get(idx - 1).unwrap().clone()
} else {
self.cache_width.last().unwrap().clone()
}
}
fn find_closest_cache_before_y(&self, y: f64) -> (usize, f64) {
if self.cache_height.is_empty() {
return (0, 0.);
}
let r = self.cache_height.iter().find_position(|(_, v)| *v > y);
if let Some((idx, _)) = r {
self.cache_height.get(idx - 1).unwrap().clone()
} else {
self.cache_height.last().unwrap().clone()
}
}
fn find_closest_cache_width(&self, col: usize) -> (usize, f64) {
if self.cache_width.is_empty() {
return (0, 0.);
}
for (c, x) in self.cache_width.iter() {
if c.abs_diff(col) <= INTERVAL / 2 {
return (*c, *x);
}
}
let result = self.cache_width.last().unwrap();
*result
}
fn find_closest_cache_height(&self, row: usize) -> (usize, f64) {
if self.cache_height.is_empty() {
return (0, 0.);
}
for (r, y) in self.cache_height.iter() {
if r.abs_diff(row) <= usize::from(INTERVAL / 2) {
return (*r, *y);
}
}
let result = self.cache_height.last().unwrap();
*result
}
fn add_cache(&mut self, height: bool, k: usize, v: f64) -> bool {
let cache = if height {
&mut self.cache_height
} else {
&mut self.cache_width
};
if cache.is_empty() {
cache.push((k, v));
return true;
}
let last = cache.last().unwrap();
if k - last.0 >= INTERVAL {
cache.push((k, v));
return true;
}
return false;
}
}
#[inline]
fn advance(reverse: bool, curr: usize) -> usize {
if reverse {
curr - 1
} else {
curr + 1
}
}