use crate::core::error::ChartError;
use crate::core::model::calendar::{BirthTime, SolarDay, SolarMonth};
use crate::core::model::chart::{
Chart, PALACE_COUNT, TemporalPalaceLayout,
temporal_layout::{
build_life_branch_palace_layout, daily_palace_index, map_target_solar_error,
},
};
use crate::core::model::star::mutagen::Scope;
use lunar_lite::{
EarthlyBranch, MonthDivide, SolarDate, StemBranch, StemBranchOptions, YearDivide,
four_pillars_from_solar_date_with_options, solar_to_lunar,
};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct HourlyPeriod {
index: usize,
time_index: u8,
stem_branch: StemBranch,
palace_branch: EarthlyBranch,
palace_layout: TemporalPalaceLayout,
}
impl HourlyPeriod {
fn new(
index: usize,
time_index: u8,
stem_branch: StemBranch,
palace_branch: EarthlyBranch,
palace_layout: TemporalPalaceLayout,
) -> Self {
Self {
index,
time_index,
stem_branch,
palace_branch,
palace_layout,
}
}
pub const fn index(&self) -> usize {
self.index
}
pub const fn time_index(&self) -> u8 {
self.time_index
}
pub const fn stem_branch(&self) -> StemBranch {
self.stem_branch
}
pub const fn palace_branch(&self) -> EarthlyBranch {
self.palace_branch
}
pub const fn palace_layout(&self) -> &TemporalPalaceLayout {
&self.palace_layout
}
}
pub fn build_hourly_period(
natal: &Chart,
target_solar_year: i32,
target_solar_month: SolarMonth,
target_solar_day: SolarDay,
target_time: BirthTime,
) -> Result<HourlyPeriod, ChartError> {
let solar = SolarDate {
year: target_solar_year,
month: target_solar_month.value(),
day: target_solar_day.value(),
};
let time_index = target_time.iztro_time_index();
let target_lunar = solar_to_lunar(solar).map_err(|err| {
map_target_solar_error(
err,
target_solar_year,
target_solar_month.value(),
target_solar_day.value(),
)
})?;
let pillars = four_pillars_from_solar_date_with_options(
solar,
time_index,
StemBranchOptions {
year: YearDivide::Normal,
month: MonthDivide::Normal,
},
)
.map_err(|err| {
map_target_solar_error(
err,
target_solar_year,
target_solar_month.value(),
target_solar_day.value(),
)
})?;
let daily_index = daily_palace_index(
natal,
target_lunar.year,
target_lunar.month,
target_lunar.day,
target_lunar.is_leap_month,
);
let index =
(daily_index as isize + time_index as isize).rem_euclid(PALACE_COUNT as isize) as usize;
let palace_branch = EarthlyBranch::Yin.offset(index as isize);
let palace_layout = build_life_branch_palace_layout(Scope::Hourly, palace_branch)?;
Ok(HourlyPeriod::new(
index,
time_index,
pillars.hourly,
palace_branch,
palace_layout,
))
}