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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
//! Provides functions for calculating how much time save there is for either
//! single segments or the remainder of an active attempt. This information is
//! based on the best segments. Considering the best segments don't represent
//! theoretically perfect segment times, this information is only an
//! approximation of how much time can actually be saved.

use crate::{analysis, timing::Snapshot, TimeSpan};

/// Calculates how much time could be saved on the given segment with the given
/// comparison. This information is based on the best segments. Considering the
/// best segments don't represent theoretically perfect segment times, this
/// information is only an approximation of how much time can actually be saved.
/// If the parameter `live` is set to `true`, then the segment time of the
/// current attempt is used if it gets longer than the segment time of the
/// segment the possible time save is calculated for. So the possible time save
/// shrinks towards zero as time goes on. The time returned by this function can
/// never be below zero. Additionally a boolean is returned that indicates if
/// the value is currently actively changing as time is being lost.
pub fn calculate(
    timer: &Snapshot<'_>,
    segment_index: usize,
    comparison: &str,
    live: bool,
) -> (Option<TimeSpan>, bool) {
    let segments = timer.run().segments();
    let method = timer.current_timing_method();
    let mut prev_time = TimeSpan::zero();
    let segment = timer.run().segment(segment_index);

    catch! {
        let mut best_segments = segment.best_segment_time()[method]?;

        for segment in segments[..segment_index].iter().rev() {
            if let Some(split_time) = segment.comparison(comparison)[method] {
                prev_time = split_time;
                break;
            } else if let Some(best_segment) = segment.best_segment_time()[method] {
                best_segments += best_segment;
            }
        }

        let mut time = segment.comparison(comparison)[method]? - best_segments - prev_time;
        let mut updates_frequently = false;

        if live && timer.current_split_index() == Some(segment_index) {
            if let Some(segment_delta) = analysis::live_segment_delta(
                timer,
                segment_index,
                comparison,
                method,
            ) {
                let segment_delta = TimeSpan::zero() - segment_delta;
                if segment_delta < time {
                    time = segment_delta;
                    updates_frequently = timer.current_phase().is_running();
                }
            }
        }

        if time < TimeSpan::zero() {
            (Some(TimeSpan::zero()), false)
        } else {
            (Some(time), updates_frequently)
        }
    }
    .unwrap_or_default()
}

/// Calculates how much time could be saved on the remainder of the run with the
/// given comparison. This information is based on the best segments.
/// Considering the best segments don't represent theoretically perfect segment
/// times, this information is only an approximation of how much time can
/// actually be saved. This information is always live, so the total possible
/// time save will shrink towards zero throughout the run and when time is lost
/// on a segment. The time returned by this function can never be below zero.
/// Additionally a boolean is returned that indicates if the value is currently
/// actively changing as time is being lost.
pub fn calculate_total(
    timer: &Snapshot<'_>,
    segment_index: usize,
    comparison: &str,
) -> (TimeSpan, bool) {
    let mut total = TimeSpan::zero();
    let mut updates_frequently = false;

    for index in segment_index..timer.run().len() {
        let (time_save, changing) = calculate(timer, index, comparison, true);
        updates_frequently |= changing;
        if let Some(time_save) = time_save {
            total += time_save;
        }
    }

    (total, updates_frequently)
}