use std::io::{ Read, Write };
use std::path::Path;
use std::fs;
use std::path::PathBuf;
use std::collections::HashMap;
use std::hash::{ self, Hash, Hasher };
use toml_edit as toml;
use crate::structs::rid::RID;
use crate::traits::{ node::Node, instanceable::Instanceable };
use crate::services::node_registry::{ self, FieldMap, SFieldMap };
const SCN_COMMENT: &str = "# This scene file was generated automatically via node_tree.\n# If you wish to modify it, ensure that children are in front of their parents.\n\n";
#[derive(Debug)]
pub struct NodeScene {
this: *mut dyn Node,
children: Vec<NodeScene>,
from_disk: bool,
pub is_owner: bool
}
impl NodeScene {
#[inline]
pub fn new<N: Node>(root: N) -> Self {
Self::new_dyn(root.to_dyn_box())
}
#[inline]
pub fn new_dyn(root: Box<dyn Node>) -> Self {
NodeScene {
this: Box::into_raw(root),
children: Vec::new(),
from_disk: false,
is_owner: true
}
}
pub fn load_from_str(document: &str) -> Result<Self, String> {
let document: toml::DocumentMut = document.parse().map_err(|err| format!("{err}"))?;
let mut node_scene: Option<NodeScene> = None;
let mut traversal: HashMap<RID, Vec<usize>> = HashMap::new();
for (key, node_data) in document.iter() {
let node_data: &toml::Table = node_data.as_table().ok_or(format!("Failed to parse {}'s data", key))?;
let metadata: &toml::InlineTable = node_data.get("metadata").map(|nd| nd.as_inline_table()).flatten().ok_or(format!("Failed to parse {}'s metadata", key))?;
let type_name: String = metadata.get("type_name").map(|tn| tn.as_str().map(|s| s.to_string())).flatten().ok_or(format!("Failed to parse {}'s type name", key))?;
let is_owner: bool = metadata.get("is_owner").map(|tn| tn.as_bool()).flatten().ok_or(format!("Failed to parse {}'s ownership status", key))?;
let parent: Option<RID> = metadata.get("parent").map(|p| p.as_integer().map(|rid| rid as RID)).flatten();
let node_fields: Option<SFieldMap> = node_data.into_iter()
.filter(|(field, _)| *field != "metadata")
.map(|(field, value)| {
match value {
toml::Item::Value(value) => Some((field.into(), value.to_owned())),
_ => None
}
}).collect();
let mut node: Box<dyn Node> = node_registry::deserialize(&type_name, node_fields.ok_or("Could not parse node fields".to_string())?)?;
let (name, local_rid): (&str, RID) = key.split_once('_')
.map(|(name, local_rid)| local_rid.parse().map(|local_rid| (name, local_rid)).map_err(|err| format!("{err}")))
.ok_or("Failed to parse Node key".to_string())??;
unsafe {
node.set_name_unchecked(name);
node.set_rid(local_rid);
}
match node_scene.as_mut() {
Some(node_scene) => {
let parent_rid: RID = parent.ok_or("No parent registered for a non-root node".to_string())?;
if parent_rid == 0 {
let mut new_scene: NodeScene = NodeScene::new_dyn(node);
new_scene.from_disk = true;
if is_owner {
node_scene.append_as_owner(new_scene);
} else {
node_scene.append(new_scene);
}
traversal.insert(local_rid, vec![node_scene.children.len() - 1]);
continue;
}
match traversal.get(&parent_rid) {
Some(cached_path) => {
let mut cursor: Option<*mut NodeScene> = None;
let mut path: Vec<usize> = Vec::new();
for &segment in cached_path {
path.push(segment);
match cursor {
Some(_) => cursor = cursor.map(|scene_ptr| &mut (unsafe { &mut *scene_ptr }.children[segment]) as *mut _),
None => cursor = Some(&mut node_scene.children[segment])
}
}
let found_parent: &mut NodeScene = unsafe { &mut *cursor.expect("Could not find a parent - internal bug") };
let mut new_scene: NodeScene = NodeScene::new_dyn(node);
new_scene.from_disk = true;
if is_owner {
found_parent.append_as_owner(new_scene);
} else {
found_parent.append(new_scene);
}
path.push(found_parent.children.len() - 1);
traversal.insert(local_rid, path);
},
None => return Err("Child was declared ahead of parent in the .scn file".to_string())
}
},
None => node_scene = Some(NodeScene::new_dyn(node))
}
}
node_scene.ok_or("No root node found in scene".to_string())
}
pub fn load(path: &Path) -> Result<Self, String> {
match path.extension().map(|ext| ext.to_str()).flatten() {
Some("scn") => (),
Some(_) => return Err("Attempted to load a file with an extension differing from .scn".to_string()),
None => return Err("Path did not contain a valid file extension".to_string())
}
let mut file: fs::File = fs::File::open(path).map_err(|err| format!("{err}"))?;
let mut buffer: Vec<u8> = Vec::new();
file.read_to_end(&mut buffer).map_err(|err| format!("{err}"))?;
drop(file);
let document: String = String::from_utf8(buffer).map_err(|err| format!("{err}"))?;
Self::load_from_str(&document)
}
pub fn save_to_str(&self) -> Result<String, String> {
let mut document: toml::DocumentMut = toml::DocumentMut::new();
self.update_internal(0);
self.clone().iterate(|parent, node, is_owner| {
let node: &dyn Node = unsafe { &*node };
let parent: Option<&dyn Node> = parent.map(|x| unsafe { &*x });
let node_key: String = format!("{}_{}", node.name(), node.rid());
document[&node_key] = toml::Item::Table(toml::Table::new());
document[&node_key]["metadata"] = toml::InlineTable::new().into();
document[&node_key]["metadata"]["type_name"] = node.name_as_type().into();
document[&node_key]["metadata"]["is_owner"] = is_owner.into();
if let Some(parent_rid) = parent.map(|p| p.rid()) {
document[&node_key]["metadata"]["parent"] = (parent_rid as i64).into();
}
let node_fields: FieldMap = node.save_from_owned();
for (field_name, value) in node_fields {
if unsafe { value.is_ghost_export() } {
continue;
}
document[&node_key][&field_name.to_string()] = toml::Item::Value(value.to_value());
}
});
let mut buffer: String = SCN_COMMENT.to_string();
buffer += &document.to_string();
Ok(buffer)
}
pub fn save(&self, path: &Path, name: &str) -> Result<(), String> {
let mut full_name: PathBuf = path.to_owned();
full_name.push(Path::new(&format!("{name}.scn")));
let buffer: String = self.save_to_str()?;
let mut file: fs::File = fs::File::create(full_name).map_err(|err| format!("{err}"))?;
file.write_all(buffer.as_bytes()).map_err(|err| format!("{err}"))?;
Ok(())
}
#[inline]
pub fn structural_hash(&self) -> u64 {
let mut hasher: hash::DefaultHasher = hash::DefaultHasher::new();
self.internal_structural_hash(&mut hasher);
hasher.finish()
}
fn internal_structural_hash<H: hash::Hasher>(&self, state: &mut H) {
unsafe { &*self.this }.name_as_type().hash(state);
self.is_owner.hash(state);
self.children.len().hash(state);
for child in &self.children {
child.hash(state);
}
}
#[inline]
pub fn append(&mut self, mut child: NodeScene) {
child.is_owner = false; self.children.push(child);
}
#[inline]
pub fn append_as_owner(&mut self, mut child: NodeScene) {
child.is_owner = true;
self.children.push(child);
}
#[inline]
pub unsafe fn get_node(&self) -> Box<dyn Node> {
unsafe {
Box::from_raw(self.this)
}
}
pub fn children(&self) -> &[NodeScene] {
&self.children
}
#[inline]
pub fn update_internal(&self, mut counter: u64) {
for child in &self.children {
counter += 1;
unsafe { (&mut *child.this).set_rid(counter) };
child.update_internal(counter);
}
}
}
impl Clone for NodeScene {
fn clone(&self) -> Self {
let cloned_node = unsafe {
let node_original: Box<dyn Node> = Box::from_raw(self.this);
let mut node_new: Box<dyn Node> = node_original.clone_as_instance();
node_new.set_rid(node_original.rid());
node_new.set_name(node_original.name());
let _ = Box::into_raw(node_original); Box::into_raw(node_new)
};
let cloned_children: Vec<NodeScene> = self.children.to_vec();
NodeScene {
this: cloned_node,
children: cloned_children,
from_disk: self.from_disk,
is_owner: self.is_owner,
}
}
}
impl hash::Hash for NodeScene {
#[inline]
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.internal_structural_hash(state)
}
}
impl Instanceable for NodeScene {
fn iterate<F: FnMut(Option<*mut dyn Node>, *mut dyn Node, bool)>(self, mut iterator: F) {
iterator(None, self.this, self.is_owner);
fn traverse<F: FnMut(Option<*mut dyn Node>, *mut dyn Node, bool)>(
node: NodeScene,
parent: *mut dyn Node,
iterator: &mut F
) {
for child in node.children {
if child.from_disk {
let child_mut: &mut dyn Node = unsafe { &mut *child.this };
unsafe {
child_mut.mark_as_loaded();
}
}
iterator(Some(parent), child.this, child.is_owner);
let child_this: *mut dyn Node = child.this;
traverse(child, child_this, iterator);
}
}
let self_this: *mut dyn Node = self.this;
if self.from_disk {
let self_mut: &mut dyn Node = unsafe { &mut *self_this };
unsafe {
self_mut.mark_as_loaded();
}
}
traverse(self, self_this, &mut iterator);
}
}