use mlua::{Lua, MetaMethod, Table, UserData, UserDataMethods, Value};
use retex_engine::maincontrol::Mode;
use retex_engine::nodes::NodeRef;
use crate::nodelib::{to_ref, wrap};
use crate::with_engine;
pub fn mode_number(m: Mode) -> i64 {
match m {
Mode::None => 0,
Mode::Vertical => 1,
Mode::InternalVertical => -1,
Mode::Horizontal => 134,
Mode::RestrictedHorizontal => -134,
Mode::DisplayMath => 267,
Mode::Math => -267,
}
}
#[derive(Clone, Copy)]
struct NestEntry(usize);
enum Field {
Int(i64),
Node(NodeRef),
Nil,
}
impl UserData for NestEntry {
fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
m.add_meta_method(MetaMethod::Index, |lua, this, key: String| {
let level = this.0;
let f = with_engine(lua, |e| {
let Some(l) = e.nest.get(level) else {
return Field::Nil;
};
match key.as_str() {
"mode" => Field::Int(mode_number(l.mode)),
"modeline" => Field::Int(i64::from(l.mode_line)),
"head" => Field::Node(l.head),
"tail" => Field::Node(l.tail),
"prevgraf" => Field::Int(i64::from(l.prev_graf)),
"prevdepth" => Field::Int(i64::from(l.prev_depth)),
"spacefactor" => Field::Int(i64::from(l.space_factor)),
"noad" | "incompleatnoad" => Field::Node(l.incompleat_noad),
"delimptr" => Field::Node(l.delim_ptr),
"mathstyle" => Field::Int(i64::from(l.math_style)),
_ => Field::Nil,
}
})?;
match f {
Field::Int(i) => Ok(Value::Integer(i)),
Field::Node(p) => wrap(lua, p, false),
Field::Nil => Ok(Value::Nil),
}
});
m.add_meta_method(
MetaMethod::NewIndex,
|lua, this, (key, v): (String, Value)| {
let level = this.0;
let p = to_ref(&v);
let n = match &v {
Value::Integer(n) => *n,
Value::Number(n) => *n as i64,
_ => 0,
};
with_engine(lua, |e| {
let Some(l) = e.nest.get_mut(level) else {
return;
};
match key.as_str() {
"tail" => {
if let Some(p) = p {
l.tail = p;
}
}
"head" => {
if let Some(p) = p {
l.head = p;
}
}
"prevgraf" => l.prev_graf = n as i32,
"prevdepth" => l.prev_depth = n as i32,
"spacefactor" => l.space_factor = n as i32,
"modeline" => l.mode_line = n as i32,
_ => {}
}
})
},
);
}
}
fn lookup(lua: &Lua, key: &Value) -> mlua::Result<Value> {
let ptr = with_engine(lua, |e| e.nest.len() - 1)?;
let level = match key {
Value::String(s) if &*s.to_str()? == "ptr" => return Ok(Value::Integer(ptr as i64)),
Value::String(s) if &*s.to_str()? == "top" => ptr,
Value::Nil => ptr,
Value::Integer(i) if (0..=ptr as i64).contains(i) => *i as usize,
Value::Number(f) if (0.0..=ptr as f64).contains(f) => *f as usize,
_ => return Ok(Value::Nil),
};
Ok(Value::UserData(lua.create_userdata(NestEntry(level))?))
}
pub fn install(lua: &Lua, tex: &Table) -> mlua::Result<()> {
let nest = lua.create_table()?;
let mt = lua.create_table()?;
mt.set(
"__index",
lua.create_function(|lua, (_t, k): (Value, Value)| lookup(lua, &k))?,
)?;
nest.set_metatable(Some(mt))?;
tex.set("nest", nest)?;
tex.set(
"getnest",
lua.create_function(|lua, k: Value| lookup(lua, &k))?,
)?;
tex.set(
"getmodevalues",
lua.create_function(|lua, ()| {
let t = lua.create_table()?;
for (k, v) in [
(0, "unset"),
(1, "vertical"),
(134, "horizontal"),
(267, "math"),
] {
t.set(k, v)?;
}
Ok(t)
})?,
)?;
Ok(())
}