use std::{
any::{Any, TypeId},
collections::HashMap,
sync::{LazyLock, Mutex},
};
use crossterm::event::KeyEvent;
use super::Mode;
use crate::{
buffer::Buffer,
context::{self, Handle},
data::{Pass, RwData},
hook::{self, FocusChanged, KeySent, ModeSwitched},
ui::{Node, Widget},
utils::{catch_panic, duat_name},
};
static MODE_NAME: Mutex<&str> = Mutex::new("");
static MODE: LazyLock<RwData<Option<Box<dyn Any + Send>>>> = LazyLock::new(RwData::default);
static SEND_KEY: LazyLock<RwData<Option<KeyFn>>> = LazyLock::new(RwData::default);
static RESET_MODES: LazyLock<Mutex<HashMap<TypeId, ResetFn>>> = LazyLock::new(Mutex::default);
type KeyFn = fn(&mut Pass, KeyEvent);
type ResetFn = Box<dyn FnMut(&mut Pass) + Send>;
pub fn set_default<M: Mode + Clone>(mode: M) {
let mut reset_modes = RESET_MODES.lock().unwrap();
let type_id = TypeId::of::<M::Widget>();
if let Some(reset_fn) = reset_modes.get_mut(&type_id) {
*reset_fn = Box::new(move |pa| {
let mode = mode.clone();
set::<M>(pa, mode);
});
} else {
reset_modes.insert(
TypeId::of::<M::Widget>(),
Box::new(move |pa| {
let mode = mode.clone();
set::<M>(pa, mode);
}),
);
};
}
pub fn set<M: Mode>(pa: &mut Pass, mode: M) {
if context::current_widget_node(pa).type_id(pa) != TypeId::of::<M::Widget>() {
let node = {
let windows = context::windows();
if TypeId::of::<M::Widget>() == TypeId::of::<Buffer>() {
let pk = context::current_buffer(pa).read(pa).path_kind();
windows
.buffer_entry(pa, pk)
.map(|(.., handle)| Node::from_handle(handle))
} else {
windows.node_of::<M::Widget>(pa).cloned()
}
};
match node {
Ok(node) => switch_widget(pa, node),
Err(err) => {
context::error!("{err}");
return;
}
}
} else {
context::current_widget(pa).clone()
};
let new_name = duat_name::<M>();
let old_name = std::mem::replace(context::mode_name().write(pa), new_name);
crate::mode::set_mode_for_remapper::<M>(pa);
let mode_opt = MODE.write(pa).take();
let new_mode = if let Some(old_mode) = mode_opt {
let new = (new_name, Box::new(mode) as Box<dyn Any + Send>);
let old = (old_name, old_mode);
let ms = hook::trigger(pa, ModeSwitched { old, new });
ms.new.1
} else {
Box::new(mode)
};
*MODE_NAME.lock().unwrap() = std::any::type_name::<M>();
*MODE.write(pa) = Some(new_mode);
*SEND_KEY.write(pa) = Some(|pa, keys| send_key_fn::<M>(pa, keys));
}
pub fn has_default<W: Widget>() -> bool {
RESET_MODES
.lock()
.unwrap()
.get(&TypeId::of::<W>())
.is_some()
}
pub fn reset<W: Widget>(pa: &mut Pass) {
let mut reset_modes = RESET_MODES.lock().unwrap();
let type_id = TypeId::of::<W>();
if let Some(reset_fn) = reset_modes.get_mut(&type_id) {
reset_fn(pa);
} else if TypeId::of::<W>() == TypeId::of::<Buffer>() {
panic!("Something went terribly wrong, somehow");
} else {
context::error!(
"There is no default [a]Mode[] set for [a]{}[]",
crate::utils::duat_name::<W>()
);
}
}
pub fn reset_to(pa: &mut Pass, handle: &Handle<impl Widget + ?Sized>) {
let mut reset_modes = RESET_MODES.lock().unwrap();
let type_id = handle.widget().type_id();
if let Some(reset_fn) = reset_modes.get_mut(&type_id) {
let node = context::windows()
.entries(pa)
.find(|(.., node)| node.ptr_eq(handle.widget()))
.map(|(.., node)| node.clone());
if let Some(node) = node {
let node = node.clone();
switch_widget(pa, node);
reset_fn(pa);
} else {
context::error!("The Handle was already closed");
}
} else {
context::error!("There is no default [a]Mode[] set for the [a]Widget",);
};
}
pub(super) fn switch_widget(pa: &mut Pass, node: Node) -> Handle<dyn Widget> {
let cur_widget = context::current_widget_node(pa);
let former = cur_widget.node(pa).handle().clone();
let current = node.handle().clone();
hook::trigger(pa, FocusChanged((former.clone(), current.clone())));
cur_widget.node(pa).on_unfocus(pa, current.clone());
context::set_current_node(pa, node.clone());
node.on_focus(pa, former.clone());
former.clone()
}
pub(super) fn send_keys_to(pa: &mut Pass, keys: Vec<KeyEvent>) {
let _ = catch_panic(|| {
for key in keys {
let send_keys = SEND_KEY.read(pa).unwrap();
send_keys(pa, key);
}
});
}
fn send_key_fn<M: Mode>(pa: &mut Pass, key_event: KeyEvent) {
let handle = context::current_widget_node(pa)
.node(pa)
.try_downcast()
.unwrap();
let mut mode: Box<M> = {
let mode = MODE.write(pa).take().unwrap();
mode.downcast().unwrap()
};
catch_panic(|| mode.send_key(pa, key_event, handle.clone()));
hook::trigger(pa, KeySent(key_event));
let mode_opt = MODE.write(pa).take();
match mode_opt {
Some(new_mode) => {
let old_name = duat_name::<M>();
let new_name = *context::mode_name().read(pa);
let new = (new_name, new_mode);
let old = (old_name, mode as Box<dyn Any + Send>);
let ms = hook::trigger(pa, ModeSwitched { old, new });
*MODE.write(pa) = Some(ms.new.1);
}
None => *MODE.write(pa) = Some(mode),
}
}