use crate::core::error::ChartError;
use crate::core::model::calendar::BirthTime;
use crate::core::model::chart::{Chart, Palace, StarPlacement};
use crate::core::model::ganzhi::{EarthlyBranch, HeavenlyStem};
use crate::core::model::star::mutagen::{Scope, birth_year_star_mutagen};
use crate::core::model::star::{Brightness, StarMetadata, StarName};
use crate::core::placement::location::{kui_yue_branches, lu_yang_tuo_ma_branches};
use crate::core::placement::natal::life_body::LunarMonth;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct MinorStarPlacementInput {
lunar_month: LunarMonth,
birth_time: BirthTime,
birth_year_stem: HeavenlyStem,
birth_year_branch: EarthlyBranch,
}
impl MinorStarPlacementInput {
pub const fn new(
lunar_month: LunarMonth,
birth_time: EarthlyBranch,
birth_year_stem: HeavenlyStem,
birth_year_branch: EarthlyBranch,
) -> Self {
Self::new_with_birth_time_variant(
lunar_month,
BirthTime::from_branch(birth_time),
birth_year_stem,
birth_year_branch,
)
}
pub const fn new_with_birth_time_variant(
lunar_month: LunarMonth,
birth_time: BirthTime,
birth_year_stem: HeavenlyStem,
birth_year_branch: EarthlyBranch,
) -> Self {
Self {
lunar_month,
birth_time,
birth_year_stem,
birth_year_branch,
}
}
pub const fn lunar_month(self) -> LunarMonth {
self.lunar_month
}
pub const fn birth_time(self) -> EarthlyBranch {
self.birth_time.branch()
}
pub const fn birth_time_variant(self) -> BirthTime {
self.birth_time
}
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 MinorStarPlacer {
fn place_minor_stars(
&self,
chart: Chart,
input: MinorStarPlacementInput,
) -> Result<Chart, ChartError>;
}
pub const fn minor_star_metadata_table() -> &'static [StarMetadata; 14] {
crate::core::model::star::minor_star_metadata_table()
}
pub fn minor_star_metadata(star: StarName) -> &'static StarMetadata {
crate::core::model::star::minor_star_metadata(star)
}
pub fn try_minor_star_metadata(star: StarName) -> Option<&'static StarMetadata> {
crate::core::model::star::try_minor_star_metadata(star)
}
pub fn minor_star_brightness(star: StarName, branch: EarthlyBranch) -> Brightness {
const MIAO: Brightness = Brightness::Temple;
const WANG: Brightness = Brightness::Prosperous;
const DE: Brightness = Brightness::Advantage;
const LI: Brightness = Brightness::Favourable;
const PING: Brightness = Brightness::Flat;
const XIAN: Brightness = Brightness::Trapped;
const UNKNOWN: Brightness = Brightness::Unknown;
let brightness_by_yin_order = match star {
StarName::WenChang => [XIAN, LI, DE, MIAO, XIAN, LI, DE, MIAO, XIAN, LI, DE, MIAO],
StarName::WenQu => [
PING, WANG, DE, MIAO, XIAN, WANG, DE, MIAO, XIAN, WANG, DE, MIAO,
],
StarName::HuoXing | StarName::LingXing => {
[MIAO, LI, XIAN, DE, MIAO, LI, XIAN, DE, MIAO, LI, XIAN, DE]
}
StarName::QingYang => [
UNKNOWN, XIAN, MIAO, UNKNOWN, XIAN, MIAO, UNKNOWN, XIAN, MIAO, UNKNOWN, XIAN, MIAO,
],
StarName::TuoLuo => [
XIAN, UNKNOWN, MIAO, XIAN, UNKNOWN, MIAO, XIAN, UNKNOWN, MIAO, XIAN, UNKNOWN, MIAO,
],
_ => return Brightness::Unknown,
};
let yin_order_index = (branch.index() + 12 - EarthlyBranch::Yin.index()) % 12;
brightness_by_yin_order[yin_order_index]
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)]
pub struct DeterministicMinorStarPlacer;
impl MinorStarPlacer for DeterministicMinorStarPlacer {
fn place_minor_stars(
&self,
chart: Chart,
input: MinorStarPlacementInput,
) -> Result<Chart, ChartError> {
let placements = minor_star_placements(input);
let palaces = chart
.palaces()
.iter()
.map(|palace| {
let mut stars = palace.stars().to_vec();
for &(branch, name) in &placements {
if branch == palace.branch() {
let metadata = minor_star_metadata(name);
stars.push(StarPlacement::new(
name,
metadata.kind(),
minor_star_brightness(name, branch),
birth_year_star_mutagen(input.birth_year_stem(), name),
Scope::Natal,
));
}
}
Palace::new(palace.name(), palace.branch(), palace.stem(), stars)
})
.collect();
Chart::try_new(
chart.birth_context().clone(),
chart.birth_year(),
chart.method_profile().clone(),
palaces,
chart.body_palace_branch(),
chart.five_element_bureau(),
)
}
}
fn minor_star_placements(input: MinorStarPlacementInput) -> [(EarthlyBranch, StarName); 14] {
let (zuo, you) = zuo_you_branches(input.lunar_month());
let (chang, qu) = chang_qu_branches(input.birth_time_variant());
let (kui, yue) = kui_yue_branches(input.birth_year_stem());
let (lu, yang, tuo, ma) =
lu_yang_tuo_ma_branches(input.birth_year_stem(), input.birth_year_branch());
let (kong, jie) = kong_jie_branches(input.birth_time_variant());
let (huo, ling) = huo_ling_branches(input.birth_year_branch(), input.birth_time_variant());
[
(zuo, StarName::ZuoFu),
(you, StarName::YouBi),
(chang, StarName::WenChang),
(qu, StarName::WenQu),
(kui, StarName::TianKui),
(yue, StarName::TianYue),
(lu, StarName::LuCun),
(ma, StarName::TianMa),
(kong, StarName::DiKong),
(jie, StarName::DiJie),
(huo, StarName::HuoXing),
(ling, StarName::LingXing),
(yang, StarName::QingYang),
(tuo, StarName::TuoLuo),
]
}
fn zuo_you_branches(lunar_month: LunarMonth) -> (EarthlyBranch, EarthlyBranch) {
let month_offset = isize::from(lunar_month.value()) - 1;
(
EarthlyBranch::Chen.offset(month_offset),
EarthlyBranch::Xu.offset(-month_offset),
)
}
fn chang_qu_branches(birth_time: BirthTime) -> (EarthlyBranch, EarthlyBranch) {
let time_index = birth_time_index(birth_time);
(
EarthlyBranch::Xu.offset(-time_index),
EarthlyBranch::Chen.offset(time_index),
)
}
fn kong_jie_branches(birth_time: BirthTime) -> (EarthlyBranch, EarthlyBranch) {
let time_index = birth_time_index(birth_time);
(
EarthlyBranch::Hai.offset(-time_index),
EarthlyBranch::Hai.offset(time_index),
)
}
fn huo_ling_branches(
year_branch: EarthlyBranch,
birth_time: BirthTime,
) -> (EarthlyBranch, EarthlyBranch) {
let time_index = birth_time_index(birth_time);
let (huo_start, ling_start) = match year_branch {
EarthlyBranch::Yin | EarthlyBranch::Wu | EarthlyBranch::Xu => {
(EarthlyBranch::Chou, EarthlyBranch::Mao)
}
EarthlyBranch::Shen | EarthlyBranch::Zi | EarthlyBranch::Chen => {
(EarthlyBranch::Yin, EarthlyBranch::Xu)
}
EarthlyBranch::Si | EarthlyBranch::You | EarthlyBranch::Chou => {
(EarthlyBranch::Mao, EarthlyBranch::Xu)
}
EarthlyBranch::Hai | EarthlyBranch::Wei | EarthlyBranch::Mao => {
(EarthlyBranch::You, EarthlyBranch::Xu)
}
};
(huo_start.offset(time_index), ling_start.offset(time_index))
}
fn birth_time_index(birth_time: BirthTime) -> isize {
isize::from(birth_time.iztro_time_index() % 12)
}