use super::{Editor, SegmentRow, TimingMethod};
use crate::{
comparison::personal_best,
platform::prelude::*,
run::RunMetadata,
settings::{CachedImageId, ImageData},
timing::formatter::{none_wrapper::EmptyWrapper, Accuracy, SegmentTime, TimeFormatter},
};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct State {
pub icon_change: Option<ImageData>,
pub game: String,
pub category: String,
pub offset: String,
pub attempts: u32,
pub timing_method: TimingMethod,
pub segments: Vec<Segment>,
pub comparison_names: Vec<String>,
pub buttons: Buttons,
pub metadata: RunMetadata,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Buttons {
pub can_remove: bool,
pub can_move_up: bool,
pub can_move_down: bool,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Segment {
pub icon_change: Option<ImageData>,
pub name: String,
pub split_time: String,
pub segment_time: String,
pub best_segment_time: String,
pub comparison_times: Vec<String>,
pub selected: SelectionState,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum SelectionState {
NotSelected,
Selected,
Active,
}
impl SelectionState {
pub const fn is_selected_or_active(&self) -> bool {
matches!(self, Self::Selected | Self::Active)
}
}
#[cfg(feature = "std")]
impl State {
pub fn write_json<W>(&self, writer: W) -> serde_json::Result<()>
where
W: std::io::Write,
{
serde_json::to_writer(writer, self)
}
}
impl Editor {
pub fn state(&mut self) -> State {
let formatter = EmptyWrapper::new(SegmentTime::with_accuracy(Accuracy::Hundredths));
let icon_change = self
.game_icon_id
.update_with(self.run.game_icon().into())
.map(Into::into);
let game = self.game_name().to_string();
let category = self.category_name().to_string();
let offset = formatter.format(self.offset()).to_string();
let attempts = self.attempt_count();
let timing_method = self.selected_timing_method();
let comparison_names = self
.custom_comparisons()
.iter()
.filter(|&n| n != personal_best::NAME)
.cloned()
.collect::<Vec<_>>();
let buttons = Buttons {
can_remove: self.can_remove_segments(),
can_move_up: self.can_move_segments_up(),
can_move_down: self.can_move_segments_down(),
};
let mut segments = Vec::with_capacity(self.run.len());
self.segment_icon_ids
.resize(self.run.len(), CachedImageId::default());
for segment_index in 0..self.run.len() {
let (name, split_time, segment_time, best_segment_time, comparison_times);
{
let row = SegmentRow::new(segment_index, self);
name = row.name().to_string();
split_time = formatter.format(row.split_time()).to_string();
segment_time = formatter.format(row.segment_time()).to_string();
best_segment_time = formatter.format(row.best_segment_time()).to_string();
comparison_times = comparison_names
.iter()
.map(|c| formatter.format(row.comparison_time(c)).to_string())
.collect();
}
let icon_change = self.segment_icon_ids[segment_index]
.update_with(self.run.segment(segment_index).icon().into())
.map(Into::into);
let selected = if self.active_segment_index() == segment_index {
SelectionState::Active
} else if self.selected_segments.contains(&segment_index) {
SelectionState::Selected
} else {
SelectionState::NotSelected
};
segments.push(Segment {
icon_change,
name,
split_time,
segment_time,
best_segment_time,
comparison_times,
selected,
});
}
State {
icon_change,
game,
category,
offset,
attempts,
timing_method,
segments,
comparison_names,
buttons,
metadata: self.run.metadata().clone(),
}
}
}