1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
use super::condition::{
    get_condition_value, match_condition, parse_condition, Condition, LogicalCondition, Op,
};
use crate::calc_engine::connector::Connector;
use logisheets_parser::ast;

use super::{CalcValue, CalcVertex, Value};

pub fn calc<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
    C: Connector,
{
    if args.len() != 2 {
        return CalcVertex::from_error(ast::Error::Unspecified);
    }
    let mut iter = args.into_iter();
    let first = fetcher.get_calc_value(iter.next().unwrap());
    let second = fetcher.get_calc_value(iter.next().unwrap());
    let condition = match second {
        CalcValue::Scalar(v) => match v {
            Value::Text(t) => parse_condition(&t),
            _ => {
                let value = get_condition_value(&v);
                let condition = Condition::Logical(LogicalCondition { op: Op::Eq, value });
                Some(condition)
            }
        },
        _ => None,
    };
    match condition {
        Some(cond) => CalcVertex::from_number(count_if_calc_value(&cond, first) as f64),
        None => CalcVertex::from_error(ast::Error::Unspecified),
    }
}

fn count_if_calc_value(cond: &Condition, value: CalcValue) -> u32 {
    match value {
        CalcValue::Scalar(v) => count_if_value(cond, &v),
        CalcValue::Range(r) => r
            .into_iter()
            .fold(0, |prev, this| prev + count_if_value(&cond, &this)),
        CalcValue::Cube(c) => c
            .into_iter()
            .fold(0, |prev, this| prev + count_if_value(&cond, &this)),
        CalcValue::Union(values) => values
            .into_iter()
            .map(|v| count_if_calc_value(cond, *v))
            .fold(0_u32, |prev, this| prev + this),
    }
}

fn count_if_value(condition: &Condition, v: &Value) -> u32 {
    if match_condition(condition, v) {
        1
    } else {
        0
    }
}