use crate::core::error::ChartError;
use crate::core::model::calendar::Gender;
use crate::core::model::chart::{
Chart, PALACE_COUNT, TemporalPalaceLayout,
temporal_layout::{build_life_branch_palace_layout, yin_first_branch_index},
};
use crate::core::model::star::mutagen::Scope;
use lunar_lite::{EarthlyBranch, StemBranch};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct AgePeriod {
index: usize,
nominal_age: u8,
palace_branch: EarthlyBranch,
stem_branch: StemBranch,
palace_layout: TemporalPalaceLayout,
}
impl AgePeriod {
fn new(
index: usize,
nominal_age: u8,
palace_branch: EarthlyBranch,
stem_branch: StemBranch,
palace_layout: TemporalPalaceLayout,
) -> Self {
Self {
index,
nominal_age,
palace_branch,
stem_branch,
palace_layout,
}
}
pub const fn index(&self) -> usize {
self.index
}
pub const fn nominal_age(&self) -> u8 {
self.nominal_age
}
pub const fn palace_branch(&self) -> EarthlyBranch {
self.palace_branch
}
pub const fn stem_branch(&self) -> StemBranch {
self.stem_branch
}
pub const fn palace_layout(&self) -> &TemporalPalaceLayout {
&self.palace_layout
}
}
pub fn build_age_period(natal: &Chart, nominal_age: u8) -> Result<AgePeriod, ChartError> {
if !(1..=120).contains(&nominal_age) {
return Err(ChartError::InvalidNominalAge { value: nominal_age });
}
let start_branch = age_start_branch(natal.birth_year().branch());
let step = age_direction_step(natal.birth_context().gender());
let offset = ((nominal_age - 1) % PALACE_COUNT as u8) as isize;
let palace_branch = start_branch.offset(offset * step);
let index = age_index(palace_branch);
let palace = natal
.palaces()
.iter()
.find(|palace| palace.branch() == palace_branch)
.ok_or(ChartError::RequiredLifeBodyPalaceMissing)?;
let stem_branch =
StemBranch::try_new(palace.stem(), palace_branch).map_err(|err| match err {
lunar_lite::StemBranchError::InvalidStemBranchPair { stem, branch } => {
ChartError::InvalidStemBranchPair { stem, branch }
}
})?;
let palace_layout = build_age_palace_layout(palace_branch)?;
Ok(AgePeriod::new(
index,
nominal_age,
palace_branch,
stem_branch,
palace_layout,
))
}
fn age_start_branch(birth_year_branch: EarthlyBranch) -> EarthlyBranch {
match birth_year_branch {
EarthlyBranch::Yin | EarthlyBranch::Wu | EarthlyBranch::Xu => EarthlyBranch::Chen,
EarthlyBranch::Shen | EarthlyBranch::Zi | EarthlyBranch::Chen => EarthlyBranch::Xu,
EarthlyBranch::Si | EarthlyBranch::You | EarthlyBranch::Chou => EarthlyBranch::Wei,
EarthlyBranch::Hai | EarthlyBranch::Mao | EarthlyBranch::Wei => EarthlyBranch::Chou,
}
}
const fn age_direction_step(gender: Gender) -> isize {
match gender {
Gender::Male => 1,
Gender::Female => -1,
}
}
fn build_age_palace_layout(age_branch: EarthlyBranch) -> Result<TemporalPalaceLayout, ChartError> {
build_life_branch_palace_layout(Scope::Age, age_branch)
}
fn age_index(branch: EarthlyBranch) -> usize {
yin_first_branch_index(branch)
}