use std::fmt;
use dom_struct::dom_struct;
use crate::dom::bindings::codegen::Bindings::TimeRangesBinding::TimeRangesMethods;
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::num::Finite;
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
use crate::dom::bindings::root::DomRoot;
use crate::dom::window::Window;
#[derive(Clone, JSTraceable, MallocSizeOf)]
struct TimeRange {
start: f64,
end: f64,
}
impl TimeRange {
pub fn union(&mut self, other: &TimeRange) {
self.start = f64::min(self.start, other.start);
self.end = f64::max(self.end, other.end);
}
fn contains(&self, time: f64) -> bool {
self.start <= time && time < self.end
}
fn is_overlapping(&self, other: &TimeRange) -> bool {
self.contains(other.start) || self.contains(other.end) || other.contains(self.start)
}
fn is_contiguous(&self, other: &TimeRange) -> bool {
other.start == self.end || other.end == self.start
}
pub fn is_before(&self, other: &TimeRange) -> bool {
other.start >= self.end
}
}
impl fmt::Debug for TimeRange {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(fmt, "[{},{})", self.start, self.end)
}
}
#[derive(Debug)]
pub enum TimeRangesError {
EndOlderThanStart,
OutOfRange,
}
#[derive(Clone, Debug, Default, JSTraceable, MallocSizeOf)]
pub struct TimeRangesContainer {
ranges: Vec<TimeRange>,
}
impl TimeRangesContainer {
pub fn len(&self) -> u32 {
self.ranges.len() as u32
}
pub fn is_empty(&self) -> bool {
self.ranges.is_empty()
}
pub fn start(&self, index: u32) -> Result<f64, TimeRangesError> {
self.ranges
.get(index as usize)
.map(|r| r.start)
.ok_or(TimeRangesError::OutOfRange)
}
pub fn end(&self, index: u32) -> Result<f64, TimeRangesError> {
self.ranges
.get(index as usize)
.map(|r| r.end)
.ok_or(TimeRangesError::OutOfRange)
}
pub fn add(&mut self, start: f64, end: f64) -> Result<(), TimeRangesError> {
if start > end {
return Err(TimeRangesError::EndOlderThanStart);
}
let mut new_range = TimeRange { start, end };
let mut idx = 0;
while idx < self.ranges.len() {
if new_range.is_overlapping(&self.ranges[idx]) ||
new_range.is_contiguous(&self.ranges[idx])
{
new_range.union(&self.ranges[idx]);
self.ranges.remove(idx);
} else if new_range.is_before(&self.ranges[idx]) &&
(idx == 0 || self.ranges[idx - 1].is_before(&new_range))
{
self.ranges.insert(idx, new_range);
return Ok(());
} else {
idx += 1;
}
}
self.ranges.insert(idx, new_range);
Ok(())
}
}
#[dom_struct]
pub struct TimeRanges {
reflector_: Reflector,
ranges: TimeRangesContainer,
}
impl TimeRanges {
fn new_inherited(ranges: TimeRangesContainer) -> TimeRanges {
Self {
reflector_: Reflector::new(),
ranges,
}
}
pub fn new(window: &Window, ranges: TimeRangesContainer) -> DomRoot<TimeRanges> {
reflect_dom_object(Box::new(TimeRanges::new_inherited(ranges)), window)
}
}
impl TimeRangesMethods<crate::DomTypeHolder> for TimeRanges {
fn Length(&self) -> u32 {
self.ranges.len()
}
fn Start(&self, index: u32) -> Fallible<Finite<f64>> {
self.ranges
.start(index)
.map(Finite::wrap)
.map_err(|_| Error::IndexSize)
}
fn End(&self, index: u32) -> Fallible<Finite<f64>> {
self.ranges
.end(index)
.map(Finite::wrap)
.map_err(|_| Error::IndexSize)
}
}