use std::path::Path;
use yog_data::Text;
use yog_data::kinds::{Block, Item};
use yog_data::with::{Opt, RefTo};
use super::super::assets::Resources;
use super::{Args, Node};
#[derive(Debug, Clone, yog_data::Schema)]
pub struct NodeArgs {
pub id: String,
pub parent: Option<yog_data::Id<yog_data::kinds::Advancement>>,
#[data(with = "Opt<RefTo<(Item, Block)>>")]
pub icon: Option<String>,
pub frame: Option<String>,
pub title: Option<Text>,
pub description: Option<Text>,
pub background: Option<String>,
}
#[derive(Debug, Clone, yog_data::Schema)]
pub struct AdvancementArgs {
pub path: String,
pub selected: String,
pub nodes: Vec<NodeArgs>,
#[data(default)]
pub drag: [f32; 2],
pub hover: Option<String>,
}
const FRAMES: [&str; 3] = ["task", "goal", "challenge"];
impl AdvancementArgs {
pub fn resolved(&self) -> Args {
let root = super::super::mod_root(Path::new(&self.path));
let res = Resources::for_mod(root.as_deref());
let texts = super::super::book::texts(root.as_deref(), &res, None);
let shown = |t: &Option<Text>, or: &str| {
t.as_ref()
.map_or_else(|| or.to_string(), |t| shown(t, &texts))
};
let all = self.with_outer_parents();
let parent_of = |id: &str| {
all.iter()
.find(|n| n.id == id)
.and_then(|n| n.parent.as_ref())
.map(|p| yog_ids::qualify(&p.id))
};
let tab_root = root_of(&self.selected, &parent_of);
let tab: Vec<&NodeArgs> = all
.iter()
.filter(|n| root_of(&n.id, &parent_of) == tab_root)
.collect();
let nodes = tab
.iter()
.map(|n| Node {
id: n.id.clone(),
parent: n.parent.as_ref().map(|p| yog_ids::qualify(&p.id)),
icon: n.icon.clone(),
frame: n
.frame
.clone()
.filter(|f| FRAMES.contains(&f.as_str()))
.unwrap_or_else(|| FRAMES[0].to_string()),
title: shown(&n.title, &n.id),
description: shown(&n.description, ""),
obtained: n.id == self.selected,
})
.collect();
let root_node = tab.iter().find(|n| n.id == tab_root);
Args {
path: self.path.clone(),
title: shown(&root_node.and_then(|n| n.title.clone()), &tab_root),
background: root_node.and_then(|n| n.background.clone()),
nodes,
drag: self.drag,
hover: self.hover.clone(),
}
}
fn with_outer_parents(&self) -> Vec<NodeArgs> {
let mut all = self.nodes.clone();
for n in &self.nodes {
let Some(p) = &n.parent else { continue };
let id = yog_ids::qualify(&p.id);
if !all.iter().any(|o| o.id == id) {
all.push(NodeArgs {
id,
parent: None,
icon: None,
frame: None,
title: None,
description: None,
background: None,
});
}
}
all
}
}
fn root_of(id: &str, parent_of: &dyn Fn(&str) -> Option<String>) -> String {
let mut at = id.to_string();
for _ in 0..MAX_DEPTH {
match parent_of(&at) {
Some(p) if p != at => at = p,
_ => break,
}
}
at
}
const MAX_DEPTH: usize = 256;
fn shown(t: &Text, texts: &super::super::book::Texts) -> String {
match t {
Text::Literal(s) => s.clone(),
Text::Res(r) => texts
.refs
.get(&format!("{}.{}", r.set, r.key))
.cloned()
.unwrap_or_else(|| r.to_string()),
Text::Lang(key) => texts.lang.get(key).cloned().unwrap_or_else(|| key.clone()),
Text::NameOf(r) => {
super::super::book::item_name(&texts.lang, &r.id).unwrap_or_else(|| r.id.clone())
}
}
}