use std::fmt::Display;
use std::fmt::Formatter;
use tyme4rs::tyme::sixtycycle::EarthBranch;
use tyme4rs::tyme::sixtycycle::HeavenStem;
use crate::auspice::Auspice;
use crate::auspice::Auspicious;
use crate::element::Element;
use crate::element::ElementRelation;
use crate::enums::QimenDoor;
use crate::enums::QimenGod;
use crate::palace::QimenPalace;
use crate::plate::are_opposite_palaces;
use crate::plate::is_stem_in_tomb;
const STEM_YI: usize = 1;
const STEM_BING: usize = 2;
const STEM_DING: usize = 3;
const STEM_WU: usize = 4;
const STEM_JI: usize = 5;
const STEM_GENG: usize = 6;
const STEM_REN: usize = 8;
const STEM_GUI: usize = 9;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Pattern {
FanYin {
original_palace: u8,
palace: u8,
},
FuYin {
palace: u8,
},
RuMu {
palace: u8,
stem: HeavenStem,
},
KongWang {
palace: u8,
branch: EarthBranch,
},
MenPo {
palace: u8,
door: QimenDoor,
},
YiQiDeShi {
palace: u8,
},
BingQiDeShi {
palace: u8,
},
DingQiDeShi {
palace: u8,
},
TianDun {
palace: u8,
},
DiDun {
palace: u8,
},
RenDun {
palace: u8,
},
ShenDun {
palace: u8,
},
GuiDun {
palace: u8,
},
FengDun {
palace: u8,
},
YunDun {
palace: u8,
},
LongDun {
palace: u8,
},
HuDun {
palace: u8,
},
QingLongFanShou {
palace: u8,
},
FeiNiaoDieXue {
palace: u8,
},
DaGe {
palace: u8,
},
XiaoGe {
palace: u8,
},
XingGe {
palace: u8,
},
BoGe {
palace: u8,
},
TianWangSiZhang {
palace: u8,
},
}
impl Auspicious for Pattern {
fn name(&self) -> &'static str {
match self {
Self::FanYin { .. } => "反吟",
Self::FuYin { .. } => "伏吟",
Self::RuMu { .. } => "入墓",
Self::KongWang { .. } => "落空亡",
Self::MenPo { .. } => "门迫",
Self::YiQiDeShi { .. } => "乙奇得使",
Self::BingQiDeShi { .. } => "丙奇得使",
Self::DingQiDeShi { .. } => "丁奇得使",
Self::TianDun { .. } => "天遁",
Self::DiDun { .. } => "地遁",
Self::RenDun { .. } => "人遁",
Self::ShenDun { .. } => "神遁",
Self::GuiDun { .. } => "鬼遁",
Self::FengDun { .. } => "风遁",
Self::YunDun { .. } => "云遁",
Self::LongDun { .. } => "龙遁",
Self::HuDun { .. } => "虎遁",
Self::QingLongFanShou { .. } => "青龙返首",
Self::FeiNiaoDieXue { .. } => "飞鸟跌穴",
Self::DaGe { .. } => "大格",
Self::XiaoGe { .. } => "小格",
Self::XingGe { .. } => "刑格",
Self::BoGe { .. } => "悖格",
Self::TianWangSiZhang { .. } => "天网四张",
}
}
fn summary(&self) -> &'static str {
match self {
Self::FanYin { .. } => "奇门凶格。星门反吟,反复无常,事情易变。",
Self::FuYin { .. } => "奇门凶格。星门伏吟,事情停滞,宜守不宜进。",
Self::RuMu { .. } => "奇门凶格。日干或时干入墓,艰难阻塞,难以发展。",
Self::KongWang { .. } => "奇门凶格。落入空亡宫,心愿落空,难以实现。",
Self::MenPo { .. } => "奇门凶格。门克宫位,门被迫害,谋事不成,阻碍重重。",
Self::YiQiDeShi { .. } => "奇门吉格。乙奇临开门,利于谋划,贵人相助,诸事吉利。",
Self::BingQiDeShi { .. } => "奇门吉格。丙奇临休门,光明正大,官司必胜,声名可得。",
Self::DingQiDeShi { .. } => "奇门吉格。丁奇临生门,才思敏捷,求财必得,生意兴隆。",
Self::TianDun { .. } => "奇门吉格。丙丁同临生门,天助之,万事亨通,大吉大利。",
Self::DiDun { .. } => "奇门吉格。乙奇临开门加九地,隐匿藏形,避凶趋吉。",
Self::RenDun { .. } => "奇门吉格。丁奇临休门加太阴,人和之象,贵人暗助。",
Self::ShenDun { .. } => "奇门吉格。丙奇临生门加九天,神助之象,心想事成。",
Self::GuiDun { .. } => "奇门吉格。丁奇临杜门加九地,神秘莫测,暗中成事。",
Self::FengDun { .. } => "奇门吉格。乙奇临杜门在巽宫,运筹帷幄,避开祸端。",
Self::YunDun { .. } => "奇门吉格。乙奇临开门在乾宫,腾云驾雾,步步高升。",
Self::LongDun { .. } => "奇门吉格。乙奇临休门在坎宫,龙入大海,鸿图大展。",
Self::HuDun { .. } => "奇门吉格。乙奇临开门在兑宫,猛虎添翼,势不可挡。",
Self::QingLongFanShou { .. } => "奇门大吉格。天盘戊临地盘丙,大吉大利,名利双收。",
Self::FeiNiaoDieXue { .. } => "奇门大吉格。天盘丙临地盘戊,诸事顺遂,不求自得。",
Self::DaGe { .. } => "奇门凶格。庚临癸上,谋事难成,处处受制,大凶。",
Self::XiaoGe { .. } => "奇门凶格。庚临壬上,小有阻碍,谋事迟缓。",
Self::XingGe { .. } => "奇门凶格。庚临己上,官司牢狱,纷争不断。",
Self::BoGe { .. } => "奇门凶格。庚金克制三奇,悖逆阻碍,诸事不顺,主凶。",
Self::TianWangSiZhang { .. } => "奇门凶格。癸水入火域,身陷天网,行动招祸,主凶。",
}
}
fn auspice(&self) -> Auspice {
match self {
Self::QingLongFanShou { .. } | Self::FeiNiaoDieXue { .. } => Auspice::GreatAuspicious,
Self::YiQiDeShi { .. }
| Self::BingQiDeShi { .. }
| Self::DingQiDeShi { .. }
| Self::TianDun { .. }
| Self::DiDun { .. }
| Self::RenDun { .. }
| Self::ShenDun { .. }
| Self::GuiDun { .. }
| Self::FengDun { .. }
| Self::YunDun { .. }
| Self::LongDun { .. }
| Self::HuDun { .. } => Auspice::Auspicious,
Self::DaGe { .. } | Self::BoGe { .. } | Self::TianWangSiZhang { .. } => Auspice::GreatInauspicious,
Self::FanYin { .. }
| Self::FuYin { .. }
| Self::RuMu { .. }
| Self::KongWang { .. }
| Self::MenPo { .. }
| Self::XiaoGe { .. }
| Self::XingGe { .. } => Auspice::Inauspicious,
}
}
}
impl Pattern {
pub const fn palace(&self) -> u8 {
match self {
Self::FanYin { palace, .. }
| Self::FuYin { palace }
| Self::RuMu { palace, .. }
| Self::KongWang { palace, .. }
| Self::MenPo { palace, .. }
| Self::YiQiDeShi { palace }
| Self::BingQiDeShi { palace }
| Self::DingQiDeShi { palace }
| Self::TianDun { palace }
| Self::DiDun { palace }
| Self::RenDun { palace }
| Self::ShenDun { palace }
| Self::GuiDun { palace }
| Self::FengDun { palace }
| Self::YunDun { palace }
| Self::LongDun { palace }
| Self::HuDun { palace }
| Self::QingLongFanShou { palace }
| Self::FeiNiaoDieXue { palace }
| Self::DaGe { palace }
| Self::XiaoGe { palace }
| Self::XingGe { palace }
| Self::BoGe { palace }
| Self::TianWangSiZhang { palace } => *palace,
}
}
}
impl Display for Pattern {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_str(self.name()) }
}
pub(crate) fn detect_patterns(
zhi_fu_original_palace: u8, zhi_fu_palace: u8, palaces: &[QimenPalace; 9], kong_wang_branches: &[EarthBranch; 2],
) -> Vec<Pattern> {
let mut patterns = Vec::new();
if zhi_fu_original_palace == zhi_fu_palace {
patterns.push(Pattern::FuYin { palace: zhi_fu_palace });
} else if are_opposite_palaces(zhi_fu_original_palace, zhi_fu_palace) {
patterns.push(Pattern::FanYin { original_palace: zhi_fu_original_palace, palace: zhi_fu_palace });
}
for palace in palaces {
let n = palace.number();
if n == 5 {
continue;
}
detect_palace_patterns(palace, kong_wang_branches, &mut patterns);
}
patterns
}
fn detect_palace_patterns(palace: &QimenPalace, kong_wang_branches: &[EarthBranch; 2], out: &mut Vec<Pattern>) {
let n = palace.number();
let heaven = palace.heaven_heaven_stem();
let earth = palace.earth_heaven_stem();
let h = heaven.get_index();
let e = earth.get_index();
let door = palace.door();
let god = palace.god();
if is_stem_in_tomb(heaven, n) {
out.push(Pattern::RuMu { palace: n, stem: heaven.clone() });
}
for branch in kong_wang_branches {
if palace.earth_branches().iter().any(|pb| pb == branch) {
out.push(Pattern::KongWang { palace: n, branch: branch.clone() });
}
}
if let Some(d) = door
&& d.element().relation_to(Element::from_palace(n)) == ElementRelation::Restrained
{
out.push(Pattern::MenPo { palace: n, door: d });
}
if let Some(d) = door {
if h == STEM_YI && d == QimenDoor::Open {
out.push(Pattern::YiQiDeShi { palace: n });
}
if h == STEM_BING && d == QimenDoor::Rest {
out.push(Pattern::BingQiDeShi { palace: n });
}
if h == STEM_DING && d == QimenDoor::Life {
out.push(Pattern::DingQiDeShi { palace: n });
}
}
if let Some(d) = door {
if h == STEM_BING && e == STEM_DING && d == QimenDoor::Life {
out.push(Pattern::TianDun { palace: n });
}
if h == STEM_YI && d == QimenDoor::Open && god == Some(QimenGod::JiuDi) {
out.push(Pattern::DiDun { palace: n });
}
if h == STEM_DING && d == QimenDoor::Rest && god == Some(QimenGod::TaiYin) {
out.push(Pattern::RenDun { palace: n });
}
if h == STEM_BING && d == QimenDoor::Life && god == Some(QimenGod::JiuTian) {
out.push(Pattern::ShenDun { palace: n });
}
if h == STEM_DING && d == QimenDoor::Block && god == Some(QimenGod::JiuDi) {
out.push(Pattern::GuiDun { palace: n });
}
if h == STEM_YI && (d == QimenDoor::Open || d == QimenDoor::Block) && n == 4 {
out.push(Pattern::FengDun { palace: n });
}
if h == STEM_YI && d == QimenDoor::Open && n == 6 {
out.push(Pattern::YunDun { palace: n });
}
if h == STEM_YI && d == QimenDoor::Rest && n == 1 {
out.push(Pattern::LongDun { palace: n });
}
if h == STEM_YI && d == QimenDoor::Open && n == 7 {
out.push(Pattern::HuDun { palace: n });
}
}
if h == STEM_WU && e == STEM_BING {
out.push(Pattern::QingLongFanShou { palace: n });
}
if h == STEM_BING && e == STEM_WU {
out.push(Pattern::FeiNiaoDieXue { palace: n });
}
if h == STEM_GENG && e == STEM_GUI {
out.push(Pattern::DaGe { palace: n });
}
if h == STEM_GENG && e == STEM_REN {
out.push(Pattern::XiaoGe { palace: n });
}
if h == STEM_GENG && e == STEM_JI {
out.push(Pattern::XingGe { palace: n });
}
if (h == STEM_BING && e == STEM_GENG) || (h == STEM_GENG && e == STEM_BING) {
out.push(Pattern::BoGe { palace: n });
}
if h == STEM_GUI && e == STEM_GUI {
out.push(Pattern::TianWangSiZhang { palace: n });
}
}
#[cfg(test)]
mod tests {
use tyme4rs::tyme::solar::SolarTime;
use super::*;
use crate::SolarTimeQimenExt;
#[test]
fn pattern_names_and_summaries() {
let p = Pattern::TianDun { palace: 9 };
assert_eq!(p.name(), "天遁");
assert!(p.summary().contains("丙丁同临生门"));
assert_eq!(p.auspice(), Auspice::Auspicious);
let p = Pattern::FeiNiaoDieXue { palace: 1 };
assert_eq!(p.auspice(), Auspice::GreatAuspicious);
let p = Pattern::DaGe { palace: 1 };
assert_eq!(p.auspice(), Auspice::GreatInauspicious);
let p = Pattern::FuYin { palace: 5 };
assert_eq!(p.auspice(), Auspice::Inauspicious);
}
#[test]
fn pattern_palace_accessor() {
assert_eq!(Pattern::FuYin { palace: 7 }.palace(), 7);
assert_eq!(Pattern::DaGe { palace: 3 }.palace(), 3);
assert_eq!(Pattern::QingLongFanShou { palace: 9 }.palace(), 9);
}
#[test]
fn detect_returns_pattern_list_for_known_chart() {
let qimen = SolarTime::from_ymd_hms(2026, 1, 14, 18, 45, 0).qimen();
let fan = qimen.patterns().filter(|p| matches!(p, Pattern::FanYin { .. })).count();
let fu = qimen.patterns().filter(|p| matches!(p, Pattern::FuYin { .. })).count();
assert!(fan + fu <= 1, "FanYin and FuYin are mutually exclusive");
}
#[test]
fn auspice_distribution() {
let qimen = SolarTime::from_ymd_hms(2027, 1, 2, 3, 40, 0).qimen();
for p in qimen.patterns() {
let _ = p.auspice().name();
let _ = p.auspice().is_auspicious();
let _ = p.auspice().is_inauspicious();
}
}
}