use std::{fmt, cmp};
use std::hash::{Hash, Hasher};
use std::ops::{Deref, DerefMut};
use crate::sys::timeval;
use nix::libc::{time_t, suseconds_t};
#[cfg(feature = "serde")]
use serde::{Serialize, Deserialize};

/// An event timestamp.
#[repr(C)]
#[derive(Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct EventTime(
#[cfg_attr(feature = "serde", serde(with = "TimevalDef"))]
timeval
);

#[cfg(feature = "serde")]
#[derive(Serialize, Deserialize)]
#[serde(remote = "timeval")]
#[allow(dead_code)]
struct TimevalDef {
    tv_sec: time_t,
    tv_usec: suseconds_t,
}

impl EventTime {
    /// Create a new timestamp.
    pub const fn new(secs: i64, usecs: i64) -> Self {
        EventTime(timeval {
            tv_sec: secs as time_t,
            tv_usec: usecs as suseconds_t,
        })
    }

    /// Create a timestamp given a libc [`timeval`].
    pub const fn from_timeval(time: timeval) -> Self {
        EventTime(time)
    }

    /// The timestamp represented as seconds since an epoch.
    pub const fn seconds(&self) -> i64 {
        (self.0).tv_sec as i64
    }

    /// Set the seconds component of the timestamp.
    pub fn set_seconds(&mut self, value: i64) {
        (self.0).tv_sec = value as time_t
    }

    /// The microseconds component of the seconds.
    ///
    /// This is meant to be modulo one second, though any value is technically
    /// valid.
    pub const fn microseconds(&self) -> i64 {
        (self.0).tv_usec as i64
    }

    /// Set the microseconds component of the timestamp.
    pub fn set_microseconds(&mut self, value: i64) {
        (self.0).tv_usec = value as suseconds_t
    }

    /// The inner `libc` [`timeval`].
    pub const fn into_inner(self) -> timeval {
        self.0
    }
}

impl Default for EventTime {
    fn default() -> Self {
        Self::new(0, 0)
    }
}

impl fmt::Debug for EventTime {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("EventTime")
            .field("seconds", &(self.0).tv_sec)
            .field("microseconds", &(self.0).tv_usec)
            .finish()
    }
}

impl From<timeval> for EventTime {
    fn from(time: timeval) -> Self {
        EventTime::from_timeval(time)
    }
}

impl From<EventTime> for timeval {
    fn from(time: EventTime) -> Self {
        time.into_inner()
    }
}

impl Deref for EventTime {
    type Target = timeval;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl DerefMut for EventTime {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

impl AsRef<timeval> for EventTime {
    fn as_ref(&self) -> &timeval {
        &self.0
    }
}

impl<'a> From<&'a timeval> for &'a EventTime {
    fn from(time: &'a timeval) -> Self {
        unsafe {
            let raw = time as *const _ as *const _;
            &*raw
        }
    }
}

impl<'a> From<&'a mut timeval> for &'a mut EventTime {
    fn from(time: &'a mut timeval) -> Self {
        unsafe {
            let raw = time as *mut _ as *mut _;
            &mut *raw
        }
    }
}

impl PartialOrd for EventTime {
    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl PartialEq for EventTime {
    fn eq(&self, other: &Self) -> bool {
        (self.0).tv_sec == (other.0).tv_sec &&
            (self.0).tv_usec == (other.0).tv_usec
    }
}

impl Hash for EventTime {
    fn hash<H: Hasher>(&self, state: &mut H) {
        (self.0).tv_sec.hash(state);
        (self.0).tv_usec.hash(state);
    }
}

impl Ord for EventTime {
    fn cmp(&self, other: &Self) -> cmp::Ordering {
        (self.0).tv_sec.cmp(&(other.0).tv_sec)
            .then((self.0).tv_usec.cmp(&(other.0).tv_usec))
    }
}

impl Eq for EventTime {}
