use crate::core::error::ChartError;
use crate::core::model::chart::{Chart, Palace, StarPlacement};
use crate::core::model::profile::ChartAlgorithmKind;
use crate::core::model::star::mutagen::Scope;
use crate::core::model::star::{Brightness, StarName};
use lunar_lite::EarthlyBranch;
use super::formulas::{
gu_chen_gua_su_branches, hua_gai_branch, jie_lu_branch, kong_wang_branch, po_sui_branch,
tian_chu_branch, tian_fu_adj_branch, tian_guan_branch, tian_shang_tian_shi_branches,
xian_chi_branch, xun_kong_branch, zhongzhou_jie_kong_branch, zhongzhou_jie_sha_adj_branch,
};
use super::input::AdjectiveStarPlacementInput;
use super::metadata::adjective_star_metadata;
use super::tables::{
DA_HAO_ADJ_BY_YEAR_BRANCH, FEI_LIAN_BY_YEAR_BRANCH, JIE_SHEN_BY_HALF_MONTH,
NIAN_JIE_BY_YEAR_BRANCH, TIAN_WU_BY_MONTH, TIAN_YUE_BY_MONTH, YIN_SHA_BY_MONTH,
};
pub trait AdjectiveStarPlacer {
fn place_adjective_stars(
&self,
chart: Chart,
input: AdjectiveStarPlacementInput,
) -> Result<Chart, ChartError>;
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)]
pub struct DeterministicAdjectiveStarPlacer;
impl AdjectiveStarPlacer for DeterministicAdjectiveStarPlacer {
fn place_adjective_stars(
&self,
chart: Chart,
input: AdjectiveStarPlacementInput,
) -> Result<Chart, ChartError> {
let placements = adjective_star_placements(&chart, 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 = adjective_star_metadata(name);
stars.push(StarPlacement::new(
name,
metadata.kind(),
Brightness::Unknown,
None,
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 adjective_star_placements(
chart: &Chart,
input: AdjectiveStarPlacementInput,
) -> Result<Vec<(EarthlyBranch, StarName)>, ChartError> {
let month_index = usize::from(input.lunar_month().value()) - 1;
let month_offset = month_index as isize;
let day_offset = isize::from(input.daily_star_offset());
let time_index = isize::from(input.birth_time_variant().iztro_time_index() % 12);
let year_stem = input.birth_year_stem();
let year_branch = input.birth_year_branch();
let year_branch_index = year_branch.index();
let year_branch_offset = year_branch_index as isize;
let life_branch = chart
.life_palace()
.map(Palace::branch)
.ok_or(ChartError::RequiredLifeBodyPalaceMissing)?;
let body_branch = chart
.body_palace_branch()
.ok_or(ChartError::RequiredLifeBodyPalaceMissing)?;
let zuo_fu = branch_containing_required_star(chart, StarName::ZuoFu)?;
let you_bi = branch_containing_required_star(chart, StarName::YouBi)?;
let wen_chang = branch_containing_required_star(chart, StarName::WenChang)?;
let wen_qu = branch_containing_required_star(chart, StarName::WenQu)?;
let hong_luan = EarthlyBranch::Mao.offset(-year_branch_offset);
let tian_xi = hong_luan.offset(6);
let tian_yao = EarthlyBranch::Chou.offset(month_offset);
let tian_xing = EarthlyBranch::You.offset(month_offset);
let tai_fu = EarthlyBranch::Wu.offset(time_index);
let feng_gao = EarthlyBranch::Yin.offset(time_index);
let san_tai = zuo_fu.offset(day_offset);
let ba_zuo = you_bi.offset(-day_offset);
let long_chi = EarthlyBranch::Chen.offset(year_branch_offset);
let feng_ge = EarthlyBranch::Xu.offset(-year_branch_offset);
let tian_ku = EarthlyBranch::Wu.offset(-year_branch_offset);
let tian_xu = EarthlyBranch::Wu.offset(year_branch_offset);
let en_guang = wen_chang.offset(day_offset - 1);
let tian_gui = wen_qu.offset(day_offset - 1);
let tian_wu = TIAN_WU_BY_MONTH[month_index % 4];
let tian_yue = TIAN_YUE_BY_MONTH[month_index];
let yin_sha = YIN_SHA_BY_MONTH[month_index % 6];
let jie_shen = JIE_SHEN_BY_HALF_MONTH[month_index / 2];
let hua_gai = hua_gai_branch(year_branch);
let (gu_chen, gua_su) = gu_chen_gua_su_branches(year_branch);
let fei_lian = FEI_LIAN_BY_YEAR_BRANCH[year_branch_index];
let po_sui = po_sui_branch(year_branch);
let tian_de = EarthlyBranch::You.offset(year_branch_offset);
let yue_de = EarthlyBranch::Si.offset(year_branch_offset);
let nian_jie = NIAN_JIE_BY_YEAR_BRANCH[year_branch_index];
let xian_chi = xian_chi_branch(year_branch);
let tian_kong = year_branch.offset(1);
let tian_guan = tian_guan_branch(year_stem);
let tian_chu = tian_chu_branch(year_stem);
let tian_fu_adj = tian_fu_adj_branch(year_stem);
let jie_lu = jie_lu_branch(year_stem);
let kong_wang = kong_wang_branch(year_stem);
let tian_cai = life_branch.offset(year_branch_offset);
let tian_shou = body_branch.offset(year_branch_offset);
let (tian_shang, tian_shi) = tian_shang_tian_shi_branches(
chart.method_profile().algorithm_kind(),
chart.birth_context().gender(),
year_branch,
life_branch,
);
let xun_kong = xun_kong_branch(year_stem, year_branch);
let mut placements = Vec::with_capacity(40);
placements.extend([
(hong_luan, StarName::HongLuan),
(tian_xi, StarName::TianXi),
(tian_yao, StarName::TianYao),
(tian_xing, StarName::TianXing),
(tai_fu, StarName::TaiFu),
(feng_gao, StarName::FengGao),
(san_tai, StarName::SanTai),
(ba_zuo, StarName::BaZuo),
(long_chi, StarName::LongChi),
(feng_ge, StarName::FengGe),
(tian_ku, StarName::TianKu),
(tian_xu, StarName::TianXu),
(en_guang, StarName::EnGuang),
(tian_gui, StarName::TianGui),
(tian_wu, StarName::TianWu),
(tian_yue, StarName::TianYueAdj),
(yin_sha, StarName::YinSha),
(jie_shen, StarName::JieShen),
(hua_gai, StarName::HuaGai),
(gu_chen, StarName::GuChen),
(gua_su, StarName::GuaSu),
(fei_lian, StarName::FeiLian),
(po_sui, StarName::PoSui),
(tian_de, StarName::TianDe),
(yue_de, StarName::YueDe),
(nian_jie, StarName::NianJie),
(xian_chi, StarName::XianChi),
(tian_kong, StarName::TianKong),
(tian_guan, StarName::TianGuan),
(tian_chu, StarName::TianChu),
(tian_fu_adj, StarName::TianFuAdj),
(tian_cai, StarName::TianCai),
(tian_shou, StarName::TianShou),
(tian_shang, StarName::TianShang),
(tian_shi, StarName::TianShi),
(xun_kong, StarName::XunKong),
]);
match chart.method_profile().algorithm_kind() {
ChartAlgorithmKind::Zhongzhou => {
placements.extend([
(year_branch.offset(7), StarName::LongDeAdj),
(
zhongzhou_jie_kong_branch(year_branch, jie_lu, kong_wang),
StarName::JieKong,
),
(
zhongzhou_jie_sha_adj_branch(year_branch),
StarName::JieShaAdj,
),
(
DA_HAO_ADJ_BY_YEAR_BRANCH[year_branch_index],
StarName::DaHaoAdj,
),
]);
}
ChartAlgorithmKind::QuanShu | ChartAlgorithmKind::Placeholder => {
placements.extend([(jie_lu, StarName::JieLu), (kong_wang, StarName::KongWang)]);
}
}
Ok(placements)
}
fn branch_containing_required_star(
chart: &Chart,
star: StarName,
) -> Result<EarthlyBranch, ChartError> {
chart
.palace_containing_star(star)
.map(Palace::branch)
.ok_or(ChartError::RequiredStarMissing { star })
}