use std::cmp::Ordering;
use std::fmt;
use std::str::FromStr;
use crate::errors::ParseError;
use crate::hitsounds::SampleSet;
use crate::timing::Millis;
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct UninheritedTimingInfo {
pub mpb: f64,
pub meter: u32,
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct InheritedTimingInfo {
pub slider_velocity: f64,
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum TimingPointKind {
Uninherited(UninheritedTimingInfo),
Inherited(InheritedTimingInfo),
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TimingPoint {
pub time: Millis,
pub kiai: bool,
pub sample_set: SampleSet,
pub sample_index: u32,
pub volume: u16,
pub kind: TimingPointKind,
}
impl Eq for TimingPoint {}
impl PartialEq for TimingPoint {
fn eq(&self, other: &TimingPoint) -> bool {
self.time.eq(&other.time)
}
}
impl Ord for TimingPoint {
fn cmp(&self, other: &TimingPoint) -> Ordering {
self.time.cmp(&other.time)
}
}
impl PartialOrd for TimingPoint {
fn partial_cmp(&self, other: &TimingPoint) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl FromStr for TimingPoint {
type Err = ParseError;
fn from_str(input: &str) -> Result<TimingPoint, Self::Err> {
let input = input.trim_end_matches(',');
let parts = input.split(',').collect::<Vec<_>>();
if parts.len() < 2 {
return Err(ParseError::InvalidTimingPoint(
"timing point must have more than 2 components",
));
}
let timestamp = parts[0].parse::<i32>()?;
let time = Millis(timestamp);
let mpb = parts[1].parse::<f64>()?;
if parts.len() == 2 {
let timing_point = TimingPoint {
kind: TimingPointKind::Uninherited(UninheritedTimingInfo {
mpb,
meter: 4,
}),
kiai: false,
sample_set: SampleSet::Default,
sample_index: 0,
volume: 100,
time,
};
return Ok(timing_point);
}
let meter = parts[2].parse::<u32>()?;
let kiai = if parts.len() > 7 {
parts[7].parse::<i32>()? > 0
} else {
false
};
let sample_set = if parts.len() > 3 {
match parts[3].parse::<u32>()? {
0 => SampleSet::Default,
1 => SampleSet::Normal,
2 => SampleSet::Soft,
3 => SampleSet::Drum,
invalid => return Err(ParseError::InvalidSampleSet(invalid)),
}
} else {
SampleSet::Default
};
let sample_index = if parts.len() > 4 {
parts[4].parse::<u32>()?
} else {
0
};
let volume = if parts.len() > 5 {
parts[5].parse::<u16>()?
} else {
100
};
let inherited = if parts.len() > 6 {
parts[6].parse::<i32>()? == 0
} else {
false
};
let _ = 60_000.0 / mpb;
let timing_point = TimingPoint {
kind: if inherited {
TimingPointKind::Inherited(InheritedTimingInfo {
slider_velocity: -100.0 / mpb,
})
} else {
TimingPointKind::Uninherited(UninheritedTimingInfo { mpb, meter })
},
kiai,
sample_set,
sample_index,
volume,
time,
};
Ok(timing_point)
}
}
impl fmt::Display for TimingPoint {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let inherited = match self.kind {
TimingPointKind::Inherited { .. } => 0,
TimingPointKind::Uninherited { .. } => 1,
};
let (beat_length, meter) = match self.kind {
TimingPointKind::Inherited(InheritedTimingInfo {
slider_velocity,
..
}) => (-100.0 / slider_velocity, 0),
TimingPointKind::Uninherited(UninheritedTimingInfo {
mpb,
meter,
..
}) => (mpb, meter),
};
write!(
f,
"{},{},{},{},{},{},{},{}",
self.time.0,
beat_length,
meter,
self.sample_set as i32,
self.sample_index,
self.volume,
inherited,
if self.kiai { 1 } else { 0 },
)
}
}