use crate::core::error::ChartError;
use crate::core::model::chart::{Chart, DecorativeStarFamily, DecorativeStarPlacement, Palace};
use crate::core::model::ganzhi::{EarthlyBranch, HeavenlyStem};
use crate::core::model::profile::ChartAlgorithmKind;
use crate::core::model::star::StarName;
use crate::core::model::star::mutagen::Scope;
use crate::core::placement::location::{
changsheng_start_branch, lu_yang_tuo_ma_branches, twelve_god_direction_forward,
};
const CHANGSHENG12: [StarName; 12] = [
StarName::ChangSheng,
StarName::MuYu,
StarName::GuanDai,
StarName::LinGuan,
StarName::DiWang,
StarName::Shuai,
StarName::Bing,
StarName::Si,
StarName::Mu,
StarName::Jue,
StarName::Tai,
StarName::Yang,
];
const BOSHI12: [StarName; 12] = [
StarName::BoShi,
StarName::LiShi,
StarName::QingLong,
StarName::XiaoHaoBoshi,
StarName::JiangJun,
StarName::ZhouShu,
StarName::FayLianBoshi,
StarName::XiShenBoshi,
StarName::BingFuBoshi,
StarName::DaHaoBoshi,
StarName::FuBing,
StarName::GuanFuBoshi,
];
const JIANGQIAN12: [StarName; 12] = [
StarName::JiangXing,
StarName::PanAn,
StarName::SuiYi,
StarName::XiShenJiangqian,
StarName::HuaGaiJiangqian,
StarName::JieSha,
StarName::ZaiSha,
StarName::TianSha,
StarName::ZhiBei,
StarName::XianChiJiangqian,
StarName::YueSha,
StarName::WangShen,
];
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct DecorativeStarPlacementInput {
birth_year_stem: HeavenlyStem,
birth_year_branch: EarthlyBranch,
}
impl DecorativeStarPlacementInput {
pub const fn new(birth_year_stem: HeavenlyStem, birth_year_branch: EarthlyBranch) -> Self {
Self {
birth_year_stem,
birth_year_branch,
}
}
pub const fn birth_year_stem(self) -> HeavenlyStem {
self.birth_year_stem
}
pub const fn birth_year_branch(self) -> EarthlyBranch {
self.birth_year_branch
}
}
pub trait DecorativeStarPlacer {
fn place_decorative_stars(
&self,
chart: Chart,
input: DecorativeStarPlacementInput,
) -> Result<Chart, ChartError>;
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)]
pub struct DeterministicDecorativeStarPlacer;
impl DecorativeStarPlacer for DeterministicDecorativeStarPlacer {
fn place_decorative_stars(
&self,
chart: Chart,
input: DecorativeStarPlacementInput,
) -> Result<Chart, ChartError> {
let placements = decorative_star_placements(&chart, input)?;
let palaces = chart
.palaces()
.iter()
.map(|palace| {
let mut decorative = Vec::new();
for &(branch, name, family) in &placements {
if branch == palace.branch() {
decorative.push(DecorativeStarPlacement::try_new(
name,
family,
Scope::Natal,
)?);
}
}
Ok(palace.clone().with_decorative_stars(decorative))
})
.collect::<Result<Vec<Palace>, ChartError>>()?;
Chart::try_new(
chart.birth_context().clone(),
chart.birth_year(),
chart.method_profile().clone(),
palaces,
chart.body_palace_branch(),
chart.five_element_bureau(),
)
}
}
fn decorative_star_placements(
chart: &Chart,
input: DecorativeStarPlacementInput,
) -> Result<Vec<(EarthlyBranch, StarName, DecorativeStarFamily)>, ChartError> {
let bureau = chart
.five_element_bureau()
.ok_or(ChartError::RequiredFiveElementBureauMissing)?;
let gender = chart.birth_context().gender();
let year_stem = input.birth_year_stem();
let year_branch = input.birth_year_branch();
let forward = twelve_god_direction_forward(gender, year_branch);
let mut placements = Vec::with_capacity(48);
place_twelve(
&mut placements,
changsheng_start_branch(bureau),
forward,
&CHANGSHENG12,
DecorativeStarFamily::Changsheng12,
);
let (lu, _, _, _) = lu_yang_tuo_ma_branches(year_stem, year_branch);
place_twelve(
&mut placements,
lu,
forward,
&BOSHI12,
DecorativeStarFamily::Boshi12,
);
placements.extend(suiqian_jiangqian12_placements(
year_branch,
chart.method_profile().algorithm_kind(),
));
Ok(placements)
}
pub(crate) fn suiqian_jiangqian12_placements(
year_branch: EarthlyBranch,
algorithm: ChartAlgorithmKind,
) -> Vec<(EarthlyBranch, StarName, DecorativeStarFamily)> {
let mut placements = Vec::with_capacity(24);
place_twelve(
&mut placements,
year_branch,
true,
&suiqian12_names(algorithm),
DecorativeStarFamily::Suiqian12,
);
place_twelve(
&mut placements,
jiangqian12_start_branch(year_branch),
true,
&JIANGQIAN12,
DecorativeStarFamily::Jiangqian12,
);
placements
}
fn place_twelve(
out: &mut Vec<(EarthlyBranch, StarName, DecorativeStarFamily)>,
start: EarthlyBranch,
forward: bool,
names: &[StarName; 12],
family: DecorativeStarFamily,
) {
for (i, &name) in names.iter().enumerate() {
let delta = if forward { i as isize } else { -(i as isize) };
out.push((start.offset(delta), name, family));
}
}
fn suiqian12_names(algorithm: ChartAlgorithmKind) -> [StarName; 12] {
let seventh = match algorithm {
ChartAlgorithmKind::Zhongzhou => StarName::SuiPo,
ChartAlgorithmKind::QuanShu | ChartAlgorithmKind::Placeholder => StarName::DaHaoSuiqian,
};
[
StarName::SuiJian,
StarName::HuiQi,
StarName::SangMen,
StarName::GuanSuo,
StarName::GuanFuSuiqian,
StarName::XiaoHaoSuiqian,
seventh,
StarName::LongDeSuiqian,
StarName::BaiHu,
StarName::TianDeSuiqian,
StarName::DiaoKe,
StarName::BingFuSuiqian,
]
}
const fn jiangqian12_start_branch(year_branch: EarthlyBranch) -> EarthlyBranch {
match year_branch {
EarthlyBranch::Yin | EarthlyBranch::Wu | EarthlyBranch::Xu => EarthlyBranch::Wu,
EarthlyBranch::Shen | EarthlyBranch::Zi | EarthlyBranch::Chen => EarthlyBranch::Zi,
EarthlyBranch::Si | EarthlyBranch::You | EarthlyBranch::Chou => EarthlyBranch::You,
EarthlyBranch::Hai | EarthlyBranch::Mao | EarthlyBranch::Wei => EarthlyBranch::Mao,
}
}