#[cfg(feature = "serde")]
use super::{StateDump, TimerDump};
use crate::{
HailInstant, Run,
types::{SplitStatus, TimeType, TimingMethod},
};
#[derive(Debug, Clone)]
pub struct HailTimer {
timer: HailInstant,
pub active_time: i128,
time_adj: i128,
current_seg: usize,
pub run_split_times: Vec<TimeType>,
pub run_seg_times: Vec<TimeType>,
pub run_pb_statuses: Vec<SplitStatus>,
pub active_pb_status: SplitStatus,
pub run_gold_statuses: Vec<SplitStatus>,
pub active_gold_status: SplitStatus,
pub run_avg_statuses: Vec<SplitStatus>,
pub active_avg_status: SplitStatus,
pub run_pb_diffs: Vec<TimeType>,
pub run_pb_seg_diffs: Vec<TimeType>,
pub active_pb_diff: i128,
pub run_gold_diffs: Vec<TimeType>,
pub run_gold_seg_diffs: Vec<TimeType>,
pub active_gold_diff: i128,
pub run_avg_diffs: Vec<TimeType>,
pub run_avg_seg_diffs: Vec<TimeType>,
pub active_avg_diff: i128,
pub pb_split_times: Vec<TimeType>,
pb_seg_times: Vec<TimeType>,
pub gold_split_times: Vec<TimeType>,
gold_seg_times: Vec<TimeType>,
pub avg_split_times: Vec<TimeType>,
avg_seg_times: Vec<TimeType>,
}
impl HailTimer {
pub fn new(run: &Run, method: TimingMethod) -> Self {
let len = run.segment_names.len();
let (pb_split_times, gold_split_times, avg_split_times, gold_segments) =
if method == TimingMethod::Rta {
(
run.rta_pb_splits.clone(),
run.rta_gold_segments
.iter()
.scan((TimeType::None, false), seg_to_split)
.collect(),
run.avg_segments(method)
.iter()
.scan((TimeType::None, false), seg_to_split)
.collect(),
run.rta_gold_segments.clone(),
)
} else if run.igt_pb_splits.len() == len {
(
run.igt_pb_splits.clone(),
run.igt_gold_segments
.iter()
.scan((TimeType::None, false), seg_to_split)
.collect(),
run.avg_segments(method)
.iter()
.scan((TimeType::None, false), seg_to_split)
.collect(),
run.igt_gold_segments.clone(),
)
} else {
(
vec![TimeType::None; len],
vec![TimeType::None; len],
vec![TimeType::None; len],
vec![TimeType::None; len],
)
};
let time_adj = run.offset.val();
Self {
timer: HailInstant::now(),
active_time: 0,
time_adj,
current_seg: 0,
run_split_times: Vec::with_capacity(len),
run_seg_times: Vec::with_capacity(len),
run_pb_statuses: Vec::with_capacity(len),
active_pb_status: SplitStatus::None,
run_gold_statuses: Vec::with_capacity(len),
active_gold_status: SplitStatus::None,
run_avg_statuses: Vec::with_capacity(len),
active_avg_status: SplitStatus::None,
run_pb_diffs: Vec::with_capacity(len),
run_pb_seg_diffs: Vec::with_capacity(len),
active_pb_diff: 0,
run_gold_diffs: Vec::with_capacity(len),
run_gold_seg_diffs: Vec::with_capacity(len),
active_gold_diff: 0,
run_avg_diffs: Vec::with_capacity(len),
run_avg_seg_diffs: Vec::with_capacity(len),
active_avg_diff: 0,
pb_split_times,
pb_seg_times: run.pb_segments(method).clone(),
gold_split_times,
gold_seg_times: gold_segments,
avg_split_times,
avg_seg_times: run.avg_segments(method).clone(),
}
}
#[cfg(feature = "serde")]
pub fn create_dump(&self) -> TimerDump {
TimerDump {
active_time: self.active_time,
run_split_times: self.run_split_times.clone(),
run_seg_times: self.run_seg_times.clone(),
run_pb_statuses: self.run_pb_statuses.clone(),
run_gold_statuses: self.run_gold_statuses.clone(),
run_avg_statuses: self.run_avg_statuses.clone(),
run_pb_diffs: self.run_pb_diffs.clone(),
run_pb_seg_diffs: self.run_pb_seg_diffs.clone(),
run_gold_diffs: self.run_gold_diffs.clone(),
run_gold_seg_diffs: self.run_gold_seg_diffs.clone(),
run_avg_diffs: self.run_avg_diffs.clone(),
run_avg_seg_diffs: self.run_avg_seg_diffs.clone(),
avg_split_times: self.avg_split_times.clone(),
}
}
#[cfg(feature = "serde")]
pub fn from_dump(d: &StateDump, run: &Run, method: TimingMethod) -> Self {
let td = if method == TimingMethod::Rta {
&d.rta
} else {
&d.igt
};
let mut s = Self::new(run, method);
s.time_adj = td.active_time;
s.active_time = td.active_time;
s.current_seg = d.current_seg;
td.run_split_times.clone_into(&mut s.run_split_times);
td.run_seg_times.clone_into(&mut s.run_seg_times);
td.run_pb_statuses.clone_into(&mut s.run_pb_statuses);
td.run_gold_statuses.clone_into(&mut s.run_gold_statuses);
td.run_avg_statuses.clone_into(&mut s.run_avg_statuses);
td.run_pb_diffs.clone_into(&mut s.run_pb_diffs);
td.run_pb_seg_diffs.clone_into(&mut s.run_pb_seg_diffs);
td.run_gold_diffs.clone_into(&mut s.run_gold_diffs);
td.run_gold_seg_diffs.clone_into(&mut s.run_gold_seg_diffs);
td.run_avg_diffs.clone_into(&mut s.run_avg_diffs);
td.run_avg_seg_diffs.clone_into(&mut s.run_avg_seg_diffs);
td.avg_split_times.clone_into(&mut s.avg_split_times);
if s.current_seg != 0 || s.active_time != 0 {
s.update_status();
}
s
}
pub fn save_adj(&mut self) {
self.time_adj = self.active_time;
}
pub fn tare(&mut self) {
self.timer = HailInstant::now();
}
pub fn update_timing(&mut self) {
let elapsed = self.timer.elapsed().as_millis() as i128;
self.active_time = elapsed + self.time_adj;
self.update_status();
}
pub fn update_status(&mut self) {
if self.pb_split_times[self.current_seg].is_time() {
self.active_pb_diff = self.active_time - self.pb_split_times[self.current_seg].val();
self.active_pb_status = calc_status(
self.run_pb_diffs
.iter()
.rfind(|t| t.is_time())
.unwrap_or(&TimeType::None)
.val(),
self.active_pb_diff,
);
} else {
self.active_pb_diff = 0;
self.active_pb_status = SplitStatus::None;
}
if self.gold_split_times[self.current_seg].is_time() {
self.active_gold_diff =
self.active_time - self.gold_split_times[self.current_seg].val();
self.active_gold_status = calc_status(
self.run_gold_diffs
.iter()
.rfind(|t| t.is_time())
.unwrap_or(&TimeType::None)
.val(),
self.active_gold_diff,
);
} else {
self.active_gold_diff = 0;
self.active_gold_status = SplitStatus::None;
}
if self.avg_split_times[self.current_seg].is_time() {
self.active_avg_diff = self.active_time - self.avg_split_times[self.current_seg].val();
self.active_avg_status = calc_status(
self.run_avg_diffs
.iter()
.rfind(|t| t.is_time())
.unwrap_or(&TimeType::None)
.val(),
self.active_avg_diff,
);
} else {
self.active_avg_diff = 0;
self.active_avg_status = SplitStatus::None;
}
}
pub fn set_time(&mut self, t: i128) {
self.time_adj = t;
self.active_time = t;
self.tare();
}
pub fn skip(&mut self) {
self.run_split_times.push(TimeType::None);
self.run_seg_times.push(TimeType::None);
self.run_pb_statuses.push(SplitStatus::None);
self.run_gold_statuses.push(SplitStatus::None);
self.run_avg_statuses.push(SplitStatus::None);
self.run_pb_diffs.push(TimeType::None);
self.run_pb_seg_diffs.push(TimeType::None);
self.run_gold_diffs.push(TimeType::None);
self.run_gold_seg_diffs.push(TimeType::None);
self.run_avg_diffs.push(TimeType::None);
self.run_avg_seg_diffs.push(TimeType::None);
self.current_seg += 1;
}
pub fn unsplit(&mut self) -> TimeType {
self.run_split_times.pop();
let seg_time = self.run_seg_times.pop().unwrap_or(TimeType::None);
self.run_pb_statuses.pop();
self.run_gold_statuses.pop();
self.run_avg_statuses.pop();
self.run_pb_diffs.pop();
self.run_pb_seg_diffs.pop();
self.active_pb_diff = self.active_time - self.pb_split_times[self.current_seg - 1].val();
self.run_gold_diffs.pop();
self.run_gold_seg_diffs.pop();
self.active_gold_diff =
self.active_time - self.gold_split_times[self.current_seg - 1].val();
self.run_avg_diffs.pop();
self.run_avg_seg_diffs.pop();
self.active_avg_diff = self.active_time - self.avg_split_times[self.current_seg - 1].val();
self.current_seg -= 1;
seg_time
}
pub fn split(&mut self) -> TimeType {
let split_time = self.active_time;
let seg_time = TimeType::from(if self.current_seg == 0 {
split_time
} else if self.run_split_times[self.current_seg - 1].is_time() {
split_time - self.run_split_times[self.current_seg - 1].val()
} else {
0
});
self.run_split_times.push(split_time.into());
self.run_seg_times.push(seg_time);
if self.pb_split_times[self.current_seg].is_time() {
self.run_pb_diffs.push(self.active_pb_diff.into());
} else {
self.run_pb_diffs.push(TimeType::None);
}
if self.gold_split_times[self.current_seg].is_time() {
self.run_gold_diffs.push(self.active_gold_diff.into());
} else {
self.run_gold_diffs.push(TimeType::None);
}
if self.avg_split_times[self.current_seg].is_time() {
self.run_avg_diffs.push(self.active_avg_diff.into());
} else {
self.run_avg_diffs.push(TimeType::None);
}
if seg_time.is_time() {
if self.pb_seg_times[self.current_seg].is_time() {
self.run_pb_seg_diffs
.push(seg_time - self.pb_seg_times[self.current_seg]);
} else {
self.run_pb_seg_diffs.push(TimeType::None);
}
if self.gold_seg_times[self.current_seg].is_time() {
self.run_gold_seg_diffs
.push(seg_time - self.gold_seg_times[self.current_seg]);
} else {
self.run_gold_seg_diffs.push(TimeType::None);
}
if self.avg_seg_times[self.current_seg].is_time() {
self.run_avg_seg_diffs
.push(seg_time - self.avg_seg_times[self.current_seg]);
} else {
self.run_avg_seg_diffs.push(TimeType::None);
}
} else {
self.run_pb_seg_diffs.push(TimeType::None);
self.run_gold_seg_diffs.push(TimeType::None);
self.run_avg_seg_diffs.push(TimeType::None);
}
if !self.gold_seg_times[self.current_seg].is_time()
|| (seg_time.is_time() && seg_time < self.gold_seg_times[self.current_seg])
{
self.run_pb_statuses.push(SplitStatus::Gold);
self.run_gold_statuses.push(SplitStatus::Gold);
self.run_avg_statuses.push(SplitStatus::Gold);
} else {
if self.pb_split_times[self.current_seg].is_time() {
self.run_pb_statuses.push(self.active_pb_status);
} else {
self.run_pb_statuses.push(SplitStatus::None);
}
if self.gold_split_times[self.current_seg].is_time() {
self.run_gold_statuses.push(self.active_gold_status);
} else {
self.run_gold_statuses.push(SplitStatus::None);
}
if self.avg_split_times[self.current_seg].is_time() {
self.run_avg_statuses.push(self.active_avg_status);
} else {
self.run_avg_statuses.push(SplitStatus::None);
}
}
self.current_seg += 1;
seg_time
}
}
fn seg_to_split((sum, has_gap): &mut (TimeType, bool), &time: &TimeType) -> Option<TimeType> {
*sum += time;
if !time.is_time() {
*has_gap = true;
}
if *has_gap {
Some(TimeType::None)
} else {
Some(*sum)
}
}
fn calc_status(last: i128, cur: i128) -> SplitStatus {
if cur < 0 {
if cur <= last {
SplitStatus::Ahead
} else {
SplitStatus::Losing
}
} else if cur >= last {
SplitStatus::Behind
} else {
SplitStatus::Gaining
}
}