use std::fmt;
use glam::{Mat4, Vec2, Vec3, Vec4};
use ltk_hash::{BinHash, HashValue, WadHash};
use ltk_primitives::Color;
use crate::{path::MapKey, property::values, property::Kind, Error, PropertyValueEnum};
pub(crate) mod sealed {
pub trait Sealed {}
}
pub trait TreeKind: Copy + sealed::Sealed {
fn is_node(self) -> bool;
}
impl sealed::Sealed for Kind {}
impl TreeKind for Kind {
#[inline]
fn is_node(self) -> bool {
matches!(self, Kind::Struct | Kind::Embedded)
}
}
pub trait TreeValue<'a>: Copy + sealed::Sealed {
type Node: TreeNode<'a, Value = Self>;
type Children: Iterator<Item = Result<(ChildSegment<Self>, Self), Error>>;
fn kind(&self) -> Kind;
fn declaration(&self) -> Result<Declaration, Error>;
fn can_contain_node(&self) -> Result<bool, Error> {
let declaration = self.declaration()?;
Ok(match declaration.class {
Some(class) => *class != 0,
None => declaration.item_kind.is_some_and(TreeKind::is_node),
})
}
fn as_node(&self) -> Result<Option<Self::Node>, Error>;
fn children(&self) -> Result<Self::Children, Error>;
fn as_leaf(&self) -> Result<Option<Leaf<'a>>, Error>;
fn map_key(&self) -> Result<MapKey, Error> {
self.as_leaf()?
.and_then(MapKey::from_leaf)
.ok_or(Error::InvalidKeyType(self.kind()))
}
fn to_value(&self) -> Result<PropertyValueEnum, Error>;
}
pub trait TreeNode<'a>: Copy + sealed::Sealed {
type Value: TreeValue<'a, Node = Self>;
type Properties: Iterator<Item = Result<(BinHash, Self::Value), Error>>;
fn class_hash(&self) -> BinHash;
fn properties(&self) -> Self::Properties;
fn get(&self, field: BinHash) -> Result<Option<Self::Value>, Error>;
fn to_struct(&self) -> Result<values::Struct, Error>;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct Declaration {
pub kind: Kind,
pub item_kind: Option<Kind>,
pub key_kind: Option<Kind>,
pub class: Option<BinHash>,
pub count: Option<usize>,
}
impl Declaration {
pub(crate) const fn bare(kind: Kind) -> Self {
Self {
kind,
item_kind: None,
key_kind: None,
class: None,
count: None,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ChildSegment<V> {
Index(usize),
Key(V),
}
#[derive(Clone, Copy, Debug, PartialEq)]
#[non_exhaustive]
pub enum Leaf<'a> {
None,
Bool(bool),
I8(i8),
U8(u8),
I16(i16),
U16(u16),
I32(i32),
U32(u32),
I64(i64),
U64(u64),
F32(f32),
Vector2(Vec2),
Vector3(Vec3),
Vector4(Vec4),
Matrix44(Mat4),
Color(Color<u8>),
String(&'a str),
Hash(HashValue),
File(WadHash),
Link(BinHash),
Flag(bool),
}
impl Leaf<'_> {
#[must_use]
pub fn kind(&self) -> Kind {
match self {
Self::None => Kind::None,
Self::Bool(_) => Kind::Bool,
Self::I8(_) => Kind::I8,
Self::U8(_) => Kind::U8,
Self::I16(_) => Kind::I16,
Self::U16(_) => Kind::U16,
Self::I32(_) => Kind::I32,
Self::U32(_) => Kind::U32,
Self::I64(_) => Kind::I64,
Self::U64(_) => Kind::U64,
Self::F32(_) => Kind::F32,
Self::Vector2(_) => Kind::Vector2,
Self::Vector3(_) => Kind::Vector3,
Self::Vector4(_) => Kind::Vector4,
Self::Matrix44(_) => Kind::Matrix44,
Self::Color(_) => Kind::Color,
Self::String(_) => Kind::String,
Self::Hash(_) => Kind::Hash,
Self::File(_) => Kind::WadChunkLink,
Self::Link(_) => Kind::ObjectLink,
Self::Flag(_) => Kind::BitBool,
}
}
pub(crate) fn write_key(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::None => Ok(()),
Self::Bool(v) | Self::Flag(v) => write!(f, "{v}"),
Self::I8(v) => write!(f, "{v}"),
Self::U8(v) => write!(f, "{v}"),
Self::I16(v) => write!(f, "{v}"),
Self::U16(v) => write!(f, "{v}"),
Self::I32(v) => write!(f, "{v}"),
Self::U32(v) => write!(f, "{v}"),
Self::I64(v) => write!(f, "{v}"),
Self::U64(v) => write!(f, "{v}"),
Self::F32(v) => write!(f, "{v}"),
Self::Vector2(v) => write_tuple(f, &v.to_array()),
Self::Vector3(v) => write_tuple(f, &v.to_array()),
Self::Vector4(v) => write_tuple(f, &v.to_array()),
Self::Matrix44(v) => write_tuple(f, &v.transpose().to_cols_array()),
Self::Color(c) => write_tuple(f, &[c.r, c.g, c.b, c.a]),
Self::String(s) => write_json_string(f, s),
Self::Hash(h) => write!(f, "{h}"),
Self::Link(h) => write!(f, "{h:08x}"),
Self::File(h) => write!(f, "{h:016x}"),
}
}
}
fn write_tuple<T: fmt::Display>(f: &mut fmt::Formatter<'_>, items: &[T]) -> fmt::Result {
f.write_str("(")?;
for (i, item) in items.iter().enumerate() {
if i > 0 {
f.write_str(", ")?;
}
write!(f, "{item}")?;
}
f.write_str(")")
}
fn write_json_string(f: &mut fmt::Formatter<'_>, s: &str) -> fmt::Result {
f.write_str("\"")?;
for c in s.chars() {
match c {
'"' => f.write_str("\\\"")?,
'\\' => f.write_str("\\\\")?,
'\n' => f.write_str("\\n")?,
'\r' => f.write_str("\\r")?,
'\t' => f.write_str("\\t")?,
'\u{8}' => f.write_str("\\b")?,
'\u{c}' => f.write_str("\\f")?,
c if (c as u32) < 0x20 => write!(f, "\\u{:04x}", c as u32)?,
c => write!(f, "{c}")?,
}
}
f.write_str("\"")
}