use super::{CalcValue, CalcVertex, Value};
use crate::calc_engine::calculator::calc_vertex::{CalcReference, Reference};
use crate::calc_engine::connector::Connector;
use logisheets_base::Addr;
use logisheets_parser::ast;
pub fn calc<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
assert_or_return!(args.len() >= 3 && args.len() <= 5, ast::Error::Unspecified);
let _ = fetcher.set_curr_as_dirty();
let mut it = args.into_iter();
let (from_sheet, sheet, base_row, base_col, base_h, base_w) = match it.next().unwrap() {
CalcVertex::Reference(r) => match r.reference {
Reference::Addr(a) => (r.from_sheet, r.sheet, a.row, a.col, 1usize, 1usize),
Reference::Range(s, e) => (
r.from_sheet,
r.sheet,
s.row,
s.col,
e.row.saturating_sub(s.row) + 1,
e.col.saturating_sub(s.col) + 1,
),
_ => return CalcVertex::from_error(ast::Error::Ref),
},
_ => return CalcVertex::from_error(ast::Error::Value),
};
let rows_v = fetcher.get_calc_value(it.next().unwrap());
assert_f64_from_calc_value!(rows, rows_v);
let cols_v = fetcher.get_calc_value(it.next().unwrap());
assert_f64_from_calc_value!(cols, cols_v);
let height = if let Some(a) = it.next() {
let v = fetcher.get_calc_value(a);
assert_f64_from_calc_value!(h, v);
h.trunc() as i64
} else {
base_h as i64
};
let width = if let Some(a) = it.next() {
let v = fetcher.get_calc_value(a);
assert_f64_from_calc_value!(w, v);
w.trunc() as i64
} else {
base_w as i64
};
let new_row = base_row as i64 + rows.trunc() as i64;
let new_col = base_col as i64 + cols.trunc() as i64;
if new_row < 0 || new_col < 0 || height < 1 || width < 1 {
return CalcVertex::from_error(ast::Error::Ref);
}
let top = Addr {
row: new_row as usize,
col: new_col as usize,
};
let reference = if height == 1 && width == 1 {
Reference::Addr(top)
} else {
let bottom = Addr {
row: (new_row + height - 1) as usize,
col: (new_col + width - 1) as usize,
};
Reference::Range(top, bottom)
};
CalcVertex::Reference(CalcReference {
from_sheet,
sheet,
reference,
})
}