//! Time Series and Event capabilities

use_prelude!();

use chrono::{DateTime, Utc};

use crate::error::{DittoError, ErrorKind};

/// Timeseries, used by the http SDK.
pub struct TimeSeries {
    pub(super) ditto: Arc<ffi_sdk::BoxedDitto>,
    pub(super) ts_name: char_p::Box,
}

impl TimeSeries {
    /// Insert content at given time in the Timeseries.
    pub fn insert<T: ::serde::Serialize>(
        &self,
        datetime: &DateTime<Utc>,
        content: T,
    ) -> Result<(), DittoError> {
        let cbor = ::serde_cbor::to_vec(content.borrow())?;
        let ditto_time = DittoTimestamp::from(datetime);
        let status = {
            ffi_sdk::ditto_insert_timeseries_event(
                &*self.ditto,
                ditto_time.epoch_bytes(),
                ditto_time.nanoseconds(),
                self.ts_name.as_ref(),
                cbor.as_slice().into(),
                None,
            )
        };
        if status != 0 {
            Err(DittoError::from_ffi(ErrorKind::InvalidInput))
        } else {
            Ok(())
        }
    }
}

#[derive(Debug)]
/// Timestamp key for an Event in a Timeseries.
pub struct DittoTimestamp {
    /// Time reference for the timestamp.
    pub epoch_sec: u64,
    /// Offset of the timestamp in nanoseconds.
    pub nanos: u32,
}

impl DittoTimestamp {
    /// Construct a Timestamp key for an Event in a Timeseries from
    /// epoch seconds and nanosecond offset.
    pub(crate) fn new(epoch_sec: u64, nanos: u32) -> Self {
        DittoTimestamp { epoch_sec, nanos }
    }

    /// Utility to make a timestamp from the current UTC time.
    pub fn utc_now() -> Self {
        let datetime = Utc::now();
        let epoch_sec: u64 = datetime.timestamp() as u64;
        let nanos: u32 = datetime.timestamp_subsec_nanos();
        Self::new(epoch_sec, nanos)
    }

    /// Return the epoch bytes in big endian.
    pub fn epoch_bytes(&self) -> [u8; 8] {
        self.epoch_sec.to_be_bytes()
    }

    /// Return the timestamp offset in nanoseconds.
    pub fn nanoseconds(&self) -> u32 {
        self.nanos
    }
}

impl From<&::chrono::DateTime<Utc>> for DittoTimestamp {
    fn from(datetime: &::chrono::DateTime<Utc>) -> DittoTimestamp {
        let epoch_sec: u64 = datetime.timestamp() as u64;
        let nanos: u32 = datetime.timestamp_subsec_nanos();
        DittoTimestamp::new(epoch_sec, nanos)
    }
}
