// SPDX-License-Identifier: LGPL-2.1-only
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>

//! Wayland keyboard → ltk dispatch.
//!
//! Translates `wl_keyboard` events into focus / text-insertion /
//! widget-submit actions. Does not share state with the gesture state
//! machine — keyboard tracks its own focus (`AppData::keyboard_focus`)
//! and modifier flags (`shift_pressed`, `ctrl_pressed`).
//!
//! The only cross-cutting state it reads is `SurfaceState::focused_idx`
//! (set by pointer / touch focus updates) so keyboard navigation can
//! branch on "is a widget focused?" — Return submits / Space presses /
//! typing inserts all funnel through that check.

use smithay_client_toolkit::seat::keyboard::
{
	KeyboardHandler, KeyEvent, Keysym, Modifiers, RawModifiers, RepeatInfo,
};
use smithay_client_toolkit::reexports::client::
{
	protocol::{ wl_keyboard::WlKeyboard, wl_surface::WlSurface },
	Connection, QueueHandle,
};

use crate::app::App;
use crate::event_loop::{ AppData, SurfaceFocus };

pub( super ) mod dispatch;
pub( super ) mod text_keys;
pub( super ) mod shortcuts;
pub( super ) mod nav;

impl<A: App> KeyboardHandler for AppData<A>
{
	fn enter(
		&mut self,
		_conn:     &Connection,
		_qh:       &QueueHandle<Self>,
		_keyboard: &WlKeyboard,
		surface:   &WlSurface,
		_serial:   u32,
		_raw:      &[u32],
		_keysyms:  &[Keysym],
	)
	{
		let focus = self.focus_for_surface( surface ).unwrap_or( SurfaceFocus::Main );
		let _ = surface;
		self.keyboard_focus = focus;
		self.surface_mut( focus ).request_redraw();
		if let Some( ref mut a ) = self.a11y { a.set_window_focus( true ); }
	}

	fn leave(
		&mut self,
		_conn:     &Connection,
		_qh:       &QueueHandle<Self>,
		_keyboard: &WlKeyboard,
		_surface:  &WlSurface,
		_serial:   u32,
	)
	{
		self.stop_key_repeat();
		self.keyboard_focus = SurfaceFocus::Main;
		if let Some( ref mut a ) = self.a11y { a.set_window_focus( false ); }
	}

	fn press_key(
		&mut self,
		_conn:     &Connection,
		_qh:       &QueueHandle<Self>,
		_keyboard: &WlKeyboard,
		_serial:   u32,
		event:     KeyEvent,
	)
	{
		let focus = self.keyboard_focus;
		// Raw observer hook (e.g. forwarding to an embedded WPE view).
		// Fires before the focus-aware dispatch.
		self.app.on_raw_key( event.keysym, event.raw_code, true, self.ctrl_pressed, self.shift_pressed );
		// A new press always cancels any in-flight repeat — the user
		// has either released the previous key or is pressing a
		// different one. Either way, the prior timer's keysym should
		// not keep firing.
		self.stop_key_repeat();
		self.dispatch_key( focus, event.clone() );
		self.start_key_repeat( focus, event );
	}

	fn release_key(
		&mut self,
		_conn:     &Connection,
		_qh:       &QueueHandle<Self>,
		_keyboard: &WlKeyboard,
		_serial:   u32,
		event:     KeyEvent,
	)
	{
		self.app.on_raw_key( event.keysym, event.raw_code, false, self.ctrl_pressed, self.shift_pressed );
		// Only stop if the released key matches the one currently
		// repeating; releasing a non-repeating key (e.g. a shift that
		// snuck through, or any key we never armed) leaves the timer
		// untouched.
		if let Some( ref state ) = self.key_repeat
		{
			if state.event.keysym == event.keysym
			{
				self.stop_key_repeat();
			}
		}
	}

	fn update_modifiers(
		&mut self,
		_conn:          &Connection,
		_qh:            &QueueHandle<Self>,
		_keyboard:      &WlKeyboard,
		_serial:        u32,
		modifiers:      Modifiers,
		_raw_modifiers: RawModifiers,
		_layout:        u32,
	)
	{
		self.shift_pressed = modifiers.shift;
		self.ctrl_pressed  = modifiers.ctrl;
	}

	fn update_repeat_info(
		&mut self,
		_conn:     &Connection,
		_qh:       &QueueHandle<Self>,
		_keyboard: &WlKeyboard,
		info:      RepeatInfo,
	)
	{
		match info
		{
			RepeatInfo::Repeat { rate, delay } =>
			{
				self.compositor_repeat_rate  = rate.get();
				self.compositor_repeat_delay = delay;
			}
			RepeatInfo::Disable =>
			{
				self.compositor_repeat_rate  = 0;
				self.compositor_repeat_delay = 0;
				self.stop_key_repeat();
			}
		}
	}

	fn repeat_key(
		&mut self,
		_conn:     &Connection,
		_qh:       &QueueHandle<Self>,
		_keyboard: &WlKeyboard,
		_serial:   u32,
		event:     KeyEvent,
	)
	{
		// Compositor-driven repeat (wl_keyboard v10). When the
		// compositor takes the repeat job we just re-dispatch — and
		// suppress our internal timer to avoid double-firing.
		self.stop_key_repeat();
		let focus = self.keyboard_focus;
		self.dispatch_key( focus, event );
	}
}
