pub use super::epoch::J2000EPOCH as J2000JD;

pub const UNIXEPOCHJD: f64 = 2440587.5;
pub const SECONDSPERDAY: f64 = 86400.0;
pub const NEPTUNEDAYSECONDS: f64 = 57996.0;
pub const NEPTUNEYEARSECONDS: f64 = 5.2006e9;

#[derive(Debug, Clone, Copy)]
pub struct DateTime {
    pub year: i32,
    pub month: u8,
    pub day: u8,
    pub hour: u8,
    pub minute: u8,
    pub second: f64,
}

impl DateTime {
    pub fn new(year: i32, month: u8, day: u8, hour: u8, minute: u8, second: f64) -> Self {
        Self {
            year,
            month,
            day,
            hour,
            minute,
            second,
        }
    }

    pub fn tojuliandate(&self) -> f64 {
        let y = if self.month <= 2 {
            self.year - 1
        } else {
            self.year
        } as f64;
        let m = if self.month <= 2 {
            self.month as f64 + 12.0
        } else {
            self.month as f64
        };
        let d = self.day as f64
            + self.hour as f64 / 24.0
            + self.minute as f64 / 1440.0
            + self.second / SECONDSPERDAY;
        let a = (y / 100.0).floor();
        let b = 2.0 - a + (a / 4.0).floor();
        (365.25 * (y + 4716.0)).floor() + (30.6001 * (m + 1.0)).floor() + d + b - 1524.5
    }

    pub fn fromjuliandate(jd: f64) -> Self {
        let z = (jd + 0.5).floor();
        let f = jd + 0.5 - z;
        let a = if z < 2299161.0 {
            z
        } else {
            let alpha = ((z - 1867216.25) / 36524.25).floor();
            z + 1.0 + alpha - (alpha / 4.0).floor()
        };
        let b = a + 1524.0;
        let c = ((b - 122.1) / 365.25).floor();
        let d = (365.25 * c).floor();
        let e = ((b - d) / 30.6001).floor();
        let day = (b - d - (30.6001 * e).floor() + f) as u8;
        let month = if e < 14.0 { e - 1.0 } else { e - 13.0 } as u8;
        let year = if month > 2 { c - 4716.0 } else { c - 4715.0 } as i32;
        let dayfrac = f * 24.0;
        let hour = dayfrac as u8;
        let minfrac = (dayfrac - hour as f64) * 60.0;
        let minute = minfrac as u8;
        let second = (minfrac - minute as f64) * 60.0;
        Self {
            year,
            month,
            day,
            hour,
            minute,
            second,
        }
    }

    pub fn tounixtimestamp(&self) -> f64 {
        (self.tojuliandate() - UNIXEPOCHJD) * SECONDSPERDAY
    }

    pub fn fromunixtimestamp(ts: f64) -> Self {
        Self::fromjuliandate(ts / SECONDSPERDAY + UNIXEPOCHJD)
    }

    pub fn toneptuneday(&self) -> f64 {
        self.tounixtimestamp() / NEPTUNEDAYSECONDS
    }

    pub fn toneptuneyear(&self) -> f64 {
        self.tounixtimestamp() / NEPTUNEYEARSECONDS
    }
}
