use crate::core::error::ChartError;
use crate::core::model::chart::{
ScopedStarPlacement, StarPlacement, TemporalContext, TemporalLayer,
};
use crate::core::model::ganzhi::{EarthlyBranch, HeavenlyStem};
use crate::core::model::star::mutagen::Scope;
use crate::core::model::star::{
Brightness, FlowStarBase, FlowStarScope, StarName, flow_star_name, known_star_metadata,
};
use crate::core::placement::location::{
chang_qu_branches_by_stem, kui_yue_branches, lu_yang_tuo_ma_branches, luan_xi_branches,
nian_jie_branch,
};
pub fn build_flow_star_layer(context: TemporalContext) -> Result<TemporalLayer, ChartError> {
if context.scope() == Scope::Age {
return Err(ChartError::FlowStarsUnavailableForScope { scope: Scope::Age });
}
let flow_scope = flow_scope_of(&context);
let scope = context.scope();
let stem_branch = context.stem_branch();
let stem = stem_branch.stem();
let branch = stem_branch.branch();
let mut placements = Vec::with_capacity(11);
if flow_scope == FlowStarScope::Yearly {
placements.push(scoped_placement(
StarName::NianJieYearly,
nian_jie_branch(branch),
scope,
));
}
for (matrix_branch, base) in flow_star_branches(stem, branch) {
placements.push(scoped_placement(
flow_star_name(flow_scope, base),
matrix_branch,
scope,
));
}
TemporalLayer::try_new(scope, context, placements, Vec::new())
}
fn flow_star_branches(
stem: HeavenlyStem,
branch: EarthlyBranch,
) -> [(EarthlyBranch, FlowStarBase); 10] {
let (kui, yue) = kui_yue_branches(stem);
let (chang, qu) = chang_qu_branches_by_stem(stem);
let (lu, yang, tuo, ma) = lu_yang_tuo_ma_branches(stem, branch);
let (luan, xi) = luan_xi_branches(branch);
[
(kui, FlowStarBase::Kui),
(yue, FlowStarBase::Yue),
(chang, FlowStarBase::Chang),
(qu, FlowStarBase::Qu),
(lu, FlowStarBase::Lu),
(yang, FlowStarBase::Yang),
(tuo, FlowStarBase::Tuo),
(ma, FlowStarBase::Ma),
(luan, FlowStarBase::Luan),
(xi, FlowStarBase::Xi),
]
}
fn scoped_placement(name: StarName, branch: EarthlyBranch, scope: Scope) -> ScopedStarPlacement {
let kind = known_star_metadata(name)
.kind()
.expect("flow stars carry a concrete StarKind");
ScopedStarPlacement::new(
branch,
StarPlacement::new(name, kind, Brightness::Unknown, None, scope),
)
}
fn flow_scope_of(context: &TemporalContext) -> FlowStarScope {
match context {
TemporalContext::Age { .. } => unreachable!("age scope has no flow-star mapping"),
TemporalContext::Decadal { .. } => FlowStarScope::Decadal,
TemporalContext::Yearly { .. } => FlowStarScope::Yearly,
TemporalContext::Monthly { .. } => FlowStarScope::Monthly,
TemporalContext::Daily { .. } => FlowStarScope::Daily,
TemporalContext::Hourly { .. } => FlowStarScope::Hourly,
}
}