use crate::error::{ChartError, Result};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::any::Any;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct TimeRange {
pub start: DateTime<Utc>,
pub end: DateTime<Utc>,
}
impl TimeRange {
pub fn new(start: DateTime<Utc>, end: DateTime<Utc>) -> Result<Self> {
if start >= end {
return Err(ChartError::data_range("Start time must be before end time"));
}
Ok(Self { start, end })
}
pub fn duration(&self) -> chrono::Duration {
self.end - self.start
}
pub fn contains(&self, time: DateTime<Utc>) -> bool {
time >= self.start && time <= self.end
}
pub fn overlaps(&self, other: &TimeRange) -> bool {
self.start < other.end && other.start < self.end
}
}
pub trait Series: std::fmt::Debug + Send + Sync {
fn name(&self) -> &str;
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
fn get_x(&self, index: usize) -> f64;
fn get_y(&self, index: usize) -> f64;
fn get_x_range(&self) -> (f64, f64);
fn get_y_range(&self, x_min: f64, x_max: f64) -> (f64, f64);
fn find_index(&self, x: f64) -> Option<usize>;
fn as_any(&self) -> &dyn Any;
}
pub trait RangeSeries: Series {
fn get_range(&self, index: usize) -> (f64, f64, f64, f64);
fn get_auxiliary(&self, index: usize) -> Option<f64> {
let _ = index;
None
}
}
#[derive(Debug)]
pub struct ChartData {
pub main_series: Box<dyn Series>,
pub overlays: Vec<Box<dyn Series>>,
visible_range: Option<TimeRange>,
y_bounds: Option<(f64, f64)>,
}
impl ChartData {
pub fn new(series: Box<dyn Series>) -> Self {
Self {
main_series: series,
overlays: Vec::new(),
visible_range: None,
y_bounds: None,
}
}
pub fn symbol(&self) -> &str {
self.main_series.name()
}
pub fn len(&self) -> usize {
self.main_series.len()
}
pub fn is_empty(&self) -> bool {
self.main_series.is_empty()
}
pub fn time_range(&self) -> Option<TimeRange> {
if self.is_empty() {
return None;
}
let (min_x, max_x) = self.main_series.get_x_range();
let start = DateTime::from_timestamp(min_x as i64, 0)?;
let end = DateTime::from_timestamp(max_x as i64, 0)?;
TimeRange::new(start, end).ok()
}
pub fn y_bounds(&self) -> Option<(f64, f64)> {
if self.is_empty() {
return None;
}
if let Some(bounds) = self.y_bounds {
return Some(bounds);
}
let (min_x, max_x) = if let Some(range) = self.visible_range {
(range.start.timestamp() as f64, range.end.timestamp() as f64)
} else {
self.main_series.get_x_range()
};
Some(self.main_series.get_y_range(min_x, max_x))
}
pub fn set_visible_range(&mut self, range: TimeRange) {
self.visible_range = Some(range);
self.y_bounds = None; }
pub fn clear_visible_range(&mut self) {
self.visible_range = None;
self.y_bounds = None;
}
pub fn visible_indices(&self) -> Option<(usize, usize)> {
let range = self.visible_range?;
let start_ts = range.start.timestamp() as f64;
let end_ts = range.end.timestamp() as f64;
let start_idx = self.main_series.find_index(start_ts)?;
let end_idx = self.main_series.find_index(end_ts)?.min(self.len());
Some((start_idx, end_idx))
}
}