use displaydoc::Display;
use strum::AsRefStr;
use crate::{
orbit::{MeanMotion, Perihelion, Season, SemiAxis, SolarLongitude, Type},
planets::EARTH_ROTATIONAL_PERIOD,
};
pub trait Body {
fn epoch(&self) -> f64;
fn orbital_eccentricity(&self) -> f64;
fn orbital_period(&self) -> f64;
fn rotational_period(&self) -> f64;
fn perihelion(&self) -> Perihelion;
fn semimajor(&self) -> f64;
fn semiminor(&self) -> f64 {
SemiAxis(self.semimajor()).minor(self.orbital_eccentricity())
}
fn mean_motion(&mut self, day: f64) -> f64 {
MeanMotion::by(
&mut MeanMotion,
day,
self.perihelion(),
self.orbital_period(),
)
}
fn to_date(&mut self, julian_date: f64) -> Date {
Date::default().compute(
julian_date,
self.epoch(),
self.rotational_period(),
self.perihelion(),
self.semimajor(),
self.orbital_eccentricity(),
self.orbital_period(),
)
}
}
#[derive(Debug, Default, AsRefStr, Clone, Copy)]
pub enum Eras {
#[strum(serialize = "AD")]
AD,
#[strum(serialize = "BD")]
BD,
#[default]
Unknown,
}
#[derive(Display, Debug, Default, Clone)]
pub struct Date {
pub era: Eras,
pub year: i32,
pub month: u8,
pub day: f64,
pub ls: f64,
pub season: String,
}
impl Date {
pub fn compute(
&self,
julian_date: f64,
epoch: f64,
rotational_period: f64,
mut peri: Perihelion,
semimajor: f64,
orbital_eccentricity: f64,
orbital_period: f64,
) -> Self {
let mut tmp_year = 12.0;
let mut tmp_day = (julian_date - epoch) * EARTH_ROTATIONAL_PERIOD / rotational_period;
let shape = Type::default().shape(orbital_eccentricity);
while tmp_day >= orbital_period {
tmp_day -= orbital_period;
tmp_year += 1.0;
}
while tmp_day < 0.0 {
tmp_day += orbital_period;
tmp_year -= 1.0;
}
let ls = SolarLongitude.compute(
shape,
tmp_day,
orbital_eccentricity,
peri,
orbital_period,
semimajor,
);
let year = tmp_year;
let month = 1.0 + (ls / peri.avg_ls()).floor();
let day = 1.0 + tmp_day.floor();
let season = Season::default().from(ls as u32);
let era = match year as i32 > 0 {
true => Eras::AD,
false => Eras::BD,
};
Self {
era,
year: year as i32,
month: month as u8,
day,
ls,
season,
}
}
}
#[derive(Display, Debug, Default, Clone)]
pub struct Time {
pub hour: i32,
pub minute: u8,
pub second: u8,
pub code: String,
pub name: String,
pub offset_name: String,
pub hour_type: String,
}
#[derive(Display, Debug, Default, Clone)]
pub struct DateTime {
pub date: Date,
pub time: Time
}
pub trait TimeZone {
fn millis(&self) -> f64;
fn offset(&self) -> f64;
fn julian_offset(&self) -> f64;
fn julian_date_universal_time(&self) -> f64;
fn body_host_ratio(&self) -> f64;
fn julian_date_terrestial_time(&self) -> f64;
fn julian_date_2000_time(&self) -> f64;
fn day_date(&self) -> f64;
fn fractional_hour(&self) -> f64;
fn fractional_minute(&self) -> f64;
fn coordinated_time(&self) -> f64;
fn now(&self) -> Time;
}
#[derive(Display, Debug, Clone, Copy, Default, AsRefStr)]
pub enum HourType {
#[strum(serialize = "AM")]
AM,
#[strum(serialize = "PM")]
PM,
#[default]
#[strum(serialize = "Unknown")]
Unknown,
}
impl HourType {
pub fn new(&self, hour: u8) -> String {
match hour {
0..=11 => Self::AM,
12..=24 => Self::PM,
_ => Self::Unknown,
}
.as_ref()
.to_string()
}
}