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

pub use smithay_client_toolkit::seat::keyboard::Keysym;
pub use calloop::channel::Sender as ChannelSender;

use crate::widget::Element;

/// Wayland `ext-foreign-toplevel-list-v1` event delivered to apps via
/// [`App::on_toplevel_event`]. `Opened` fires after the compositor
/// commits the first `done` for a new handle (so `app_id` is the value
/// in effect at that point — the protocol allows the compositor to
/// re-commit later, but most don't). `Closed` fires when the
/// compositor sends `closed` and the runtime is about to destroy the
/// handle proxy. Both carry the same `id`, the Wayland protocol id of
/// the handle, unique within the session and stable for the handle's
/// lifetime.
#[ derive( Debug, Clone ) ]
pub enum ToplevelEvent
{
	Opened { id: u32, app_id: String },
	Closed { id: u32 },
}

/// Wayland shell mode for the application surface.
#[ derive( Debug, Clone, Copy, PartialEq, Eq ) ]
pub enum ShellMode
{
	/// Normal application window using xdg-shell protocol.
	/// This is the default for regular applications.
	Window,

	/// Layer-shell surface at the specified layer.
	/// Used for shell components like panels, backgrounds, overlays.
	Layer( Layer ),

	/// `ext-session-lock-v1` lock surface. The compositor blanks the outputs
	/// and shows only this surface until the app requests exit (see
	/// [`App::requested_exit`]). Used by the screen locker.
	SessionLock,
}

/// Layer-shell layer position.
#[ derive( Debug, Clone, Copy, PartialEq, Eq ) ]
pub enum Layer
{
	/// Below normal windows (wallpapers, desktop backgrounds).
	Background,
	/// Below normal windows but above background.
	Bottom,
	/// Above normal windows (panels, docks).
	Top,
	/// Above everything (notifications, on-screen displays).
	Overlay,
}

impl Layer
{
	/// Convert to smithay's wlr-layer Layer type.
	pub( crate ) fn to_wlr_layer( self ) -> smithay_client_toolkit::shell::wlr_layer::Layer
	{
		use smithay_client_toolkit::shell::wlr_layer::Layer as WlrLayer;
		match self
		{
			Layer::Background => WlrLayer::Background,
			Layer::Bottom     => WlrLayer::Bottom,
			Layer::Top        => WlrLayer::Top,
			Layer::Overlay    => WlrLayer::Overlay,
		}
	}
}

/// Layer-shell anchor edges.
/// Determines which screen edges the surface is attached to.
#[ derive( Debug, Clone, Copy, PartialEq, Eq ) ]
pub struct Anchor
{
	pub top:    bool,
	pub bottom: bool,
	pub left:   bool,
	pub right:  bool,
}

impl Anchor
{
	/// Anchor to all edges (fill screen).
	pub const ALL: Self = Self { top: true, bottom: true, left: true, right: true };

	/// Anchor to top edge only (horizontal bar at top).
	pub const TOP: Self = Self { top: true, bottom: false, left: true, right: true };

	/// Anchor to bottom edge only (horizontal bar at bottom).
	pub const BOTTOM: Self = Self { top: false, bottom: true, left: true, right: true };

	/// Anchor to left edge only (vertical bar at left).
	pub const LEFT: Self = Self { top: true, bottom: true, left: true, right: false };

	/// Anchor to right edge only (vertical bar at right).
	pub const RIGHT: Self = Self { top: true, bottom: true, left: false, right: true };

	/// Convert to smithay's Anchor bitflags.
	pub( crate ) fn to_wlr_anchor( self ) -> smithay_client_toolkit::shell::wlr_layer::Anchor
	{
		use smithay_client_toolkit::shell::wlr_layer::Anchor as WlrAnchor;
		let mut anchor = WlrAnchor::empty();
		if self.top    { anchor |= WlrAnchor::TOP; }
		if self.bottom { anchor |= WlrAnchor::BOTTOM; }
		if self.left   { anchor |= WlrAnchor::LEFT; }
		if self.right  { anchor |= WlrAnchor::RIGHT; }
		anchor
	}
}

/// Stable identifier for an overlay surface. The runtime uses it to diff the
/// overlay list between frames: if the same `OverlayId` is returned from
/// [`App::overlays`] on consecutive frames the underlying Wayland surface
/// and its internal state are kept; if it disappears the surface is destroyed.
#[ derive( Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord ) ]
pub struct OverlayId( pub u32 );

/// Stable identifier for a subsurface, used to diff the list returned by
/// [`App::subsurfaces`] between frames the same way [`OverlayId`] diffs
/// overlays.
#[ derive( Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord ) ]
pub struct SubsurfaceId( pub u32 );

/// Which surface a [`SubsurfaceSpec`] is composited as a child of. `Main`
/// (the default position) parents to the app's main surface; `Overlay(id)`
/// parents to one of the [`App::overlays`] surfaces, so a slide can ride
/// above app windows the way an overlay panel does. An `Overlay` parent that
/// is absent or not yet configured is skipped for that frame.
#[ derive( Debug, Clone, Copy, PartialEq, Eq ) ]
pub enum SubsurfaceParent
{
	Main,
	Overlay( OverlayId ),
}

/// One of the surfaces an [`App`] can target with an invalidation. Used inside
/// [`InvalidationScope::Only`] to name the affected surfaces.
#[ derive( Debug, Clone, Copy, PartialEq, Eq, Hash ) ]
pub enum SurfaceTarget
{
	/// The application's main surface (the one returned by [`App::view`]).
	Main,
	/// The overlay with the given stable [`OverlayId`].
	Overlay( OverlayId ),
}

/// Which surfaces a given [`App::update`] mutation can affect.
///
/// Returned by [`App::invalidate_after`] (default [`InvalidationScope::All`])
/// to let the runtime skip redraws on surfaces whose contents could not
/// possibly have changed by the message in question. On a shell with many
/// overlays most messages only touch one of them, so the savings are large.
#[ derive( Debug, Clone ) ]
pub enum InvalidationScope
{
	/// Treat every surface as potentially affected (safe default).
	All,
	/// Only the listed surfaces may have changed; others can be skipped this
	/// iteration. An empty list means "nothing visible to redraw" — useful
	/// for messages that only touch internal bookkeeping.
	Only( Vec<SurfaceTarget> ),
}

impl InvalidationScope
{
	/// Combine two scopes. `All` absorbs any other scope; two `Only` scopes
	/// merge into a deduped list of targets. Used by the run loop to fold the
	/// per-message scopes from a batch of pending messages into a single
	/// decision before applying it.
	pub fn union( self, other: Self ) -> Self
	{
		match ( self, other )
		{
			( Self::All, _ ) | ( _, Self::All ) => Self::All,
			( Self::Only( mut a ), Self::Only( b ) ) =>
			{
				for t in b
				{
					if !a.contains( &t ) { a.push( t ); }
				}
				Self::Only( a )
			}
		}
	}
}

/// Description of an additional layer-shell surface rendered on top of the
/// main application surface.
///
/// Apps return a list of these from [`App::overlays`] each frame. The runtime
/// creates one Wayland layer-shell surface per active overlay, renders its
/// [`view`](Self::view) to its own canvas, and dispatches input to it
/// independently of the main surface. Overlays share the same message type as
/// the main app — messages emitted by widgets inside an overlay are delivered
/// to [`App::update`] exactly like messages from the main view.
///
/// Overlays are useful for building shells whose main surface sits behind
/// regular app windows (e.g. a homescreen on [`Layer::Background`]) while
/// still exposing on-demand panels above everything (launcher, quick
/// settings, power menu…).
pub struct OverlaySpec<Message: Clone>
{
	/// Stable identifier, used to diff overlays between frames.
	pub id: OverlayId,

	/// Wayland layer for this overlay. Typically [`Layer::Overlay`] or
	/// [`Layer::Top`].
	pub layer: Layer,

	/// Screen edges to anchor to.
	pub anchor: Anchor,

	/// Desired size `( width, height )`, resolved to logical pixels against
	/// the main surface (output) when the overlay is materialized. A
	/// [`Length::px`](crate::Length::px) keeps a fixed size; a
	/// [`Length::widget`](crate::Length::widget) / `vmin` / `dp` makes the
	/// overlay surface scale with the display like any other sized element.
	/// A component that resolves to `0` (e.g. `Length::px( 0.0 )`) means
	/// "fill available space in that dimension".
	pub size: ( crate::types::Length, crate::types::Length ),

	/// Exclusive zone in pixels reserved from the anchored edge.
	/// `-1` requests focus without reserving space, `0` is the default for
	/// transient overlays that should not push other surfaces around.
	pub exclusive_zone: i32,

	/// When `true`, the compositor sends keyboard events to this overlay
	/// without requiring a click first.
	pub keyboard_exclusive: bool,

	/// Interactive input region as a list of rects (logical pixels). Only
	/// these areas receive pointer/touch input; the rest passes through.
	/// `None` means the full surface receives input.
	pub input_region: Option<Vec<crate::types::Rect>>,

	/// Widget tree for this overlay.
	pub view: Element<Message>,

	/// Message sent when the overlay should be dismissed. The runtime
	/// fires it in three situations:
	///
	/// 1. The compositor sends `xdg_popup.popup_done` (xdg-popup mode)
	///    — typically when the user clicks outside the popup with the
	///    grab fully active.
	/// 2. The user presses pointer / touch on the main surface while
	///    the overlay is still mapped, and the press does not fall on
	///    the trigger rect identified by [`Self::anchor_widget_id`].
	///    This covers compositors (notably Mutter) that route the
	///    button to the parent surface instead of breaking the popup
	///    grab when the cursor was already over the parent.
	/// 3. The user presses Escape while at least one xdg-popup overlay
	///    is open.
	///
	/// The application is expected to flip its `is_open` flag (or
	/// equivalent) to `false` in `update()` so the next frame stops
	/// returning the spec from [`App::overlays`]. The runtime is
	/// idempotent if the message arrives more than once for the same
	/// open / close cycle.
	pub on_dismiss: Option<Message>,

	/// When set, the overlay is rendered as a Wayland **xdg-popup**
	/// anchored to the rect of the widget tagged with this
	/// [`crate::types::WidgetId`] in the previous frame's layout — the
	/// standard mechanism for combo / context-menu / tooltip popups in
	/// `xdg-shell` applications. The compositor positions the popup
	/// adjacent to the anchor and is allowed to flip it (drop-up
	/// instead of drop-down) when there is not enough room.
	///
	/// When this field is `None`, the overlay is rendered as a
	/// [`wlr-layer-shell`](Layer) surface — the path used by shells,
	/// panels, lock screens and overlays that need full-surface
	/// coverage. In that mode `layer` / `anchor` / `exclusive_zone` /
	/// `keyboard_exclusive` carry the placement; for the popup mode
	/// they are ignored.
	pub anchor_widget_id: Option<crate::types::WidgetId>,
}

/// An input-transparent child surface composited over the main surface and
/// moved by the compositor.
///
/// Returned from [`App::subsurfaces`]. The runtime creates one `wl_subsurface`
/// per active spec, full-size over the main surface, with an **empty input
/// region** — all pointer/touch falls through to the main surface, so the host
/// keeps a single input/gesture model. The win is in the move: the content
/// buffer is rasterised only when [`content_version`](Self::content_version)
/// (or the surface size) changes, while [`x`](Self::x) / [`y`](Self::y)
/// changes only emit `wl_subsurface.set_position` + a parent commit — no
/// re-raster, no buffer re-upload. This makes an animated reveal/slide track
/// the finger at the compositor's compositing cost rather than the client's
/// raster cost.
pub struct SubsurfaceSpec<Message: Clone>
{
	/// Stable identifier, used to diff subsurfaces between frames.
	pub id: SubsurfaceId,

	/// Surface this subsurface is composited as a child of. Sized to that
	/// parent; [`x`](Self::x) / [`y`](Self::y) are relative to its top-left.
	pub parent: SubsurfaceParent,

	/// Widget tree for this subsurface. Should paint its own opaque
	/// background if it must cover the main surface beneath it.
	pub view: Element<Message>,

	/// Position of the subsurface relative to the parent's top-left, in
	/// layout (physical) pixels — the same space as [`App::on_resize`] and
	/// widget rects. The runtime divides by the surface scale to obtain the
	/// logical position it hands to the compositor. Animate this for a cheap
	/// compositor-side move.
	pub x: i32,
	pub y: i32,

	/// Content revision. Bump it whenever [`view`](Self::view) would paint
	/// differently; leave it unchanged for position-only updates so the
	/// runtime skips the re-raster and only repositions.
	pub content_version: u64,

	/// Render through GLES (so `backdrop-filter` glass works) rather than the
	/// software rasteriser. GLES is only worth it for content that actually
	/// uses the blur: it pays a per-surface EGL-surface creation (and a
	/// one-time shader compile) the cheap software path avoids, so a panel
	/// without glass should leave this `false`.
	pub gpu: bool,
}

/// Trait that application types must implement to integrate with ltk.
pub trait App: 'static
{
	/// The message type produced by this application.
	type Message: Clone + 'static;

	/// Build the widget tree for the current frame.
	fn view( &self ) -> Element<Self::Message>;

	/// Apply a message to the application state.
	fn update( &mut self, msg: Self::Message );

	/// Tell the runtime which surfaces *could* have changed visibly as a
	/// result of [`update`](Self::update) being called with this message.
	///
	/// Default is [`InvalidationScope::All`] — every surface is redrawn.
	/// A shell that knows, for example, that a `SetVolume` message only
	/// affects its quick-settings overlay can return
	/// `InvalidationScope::Only( vec![ SurfaceTarget::Overlay( quick_id ) ] )`
	/// to skip pointless work on other surfaces.
	///
	/// Called *before* [`update`](Self::update) (so it sees the message but
	/// not the post-update state). Side-effect free: must not mutate `self`.
	fn invalidate_after( &self, _msg: &Self::Message ) -> InvalidationScope
	{
		InvalidationScope::All
	}

	/// Describe the auxiliary overlay surfaces that should be active this
	/// frame. Each entry becomes its own Wayland layer-shell surface
	/// independent from the main surface returned by [`view`](Self::view).
	///
	/// Return an empty `Vec` (the default) to disable multi-surface support —
	/// the app then renders only onto its main surface.
	///
	/// The runtime diffs the list across frames using [`OverlaySpec::id`]:
	/// stable IDs are kept alive, new IDs cause a surface to be created, and
	/// IDs that disappear cause the surface to be destroyed.
	fn overlays( &self ) -> Vec<OverlaySpec<Self::Message>> { Vec::new() }

	/// Describe the input-transparent child surfaces composited over the main
	/// surface this frame. Each becomes a `wl_subsurface` the compositor moves
	/// by position; see [`SubsurfaceSpec`]. Diffed across frames by
	/// [`SubsurfaceSpec::id`]. Default empty.
	fn subsurfaces( &self ) -> Vec<SubsurfaceSpec<Self::Message>> { Vec::new() }

	/// Return any pending messages from external sources (timers, async, etc.).
	fn poll_external( &mut self ) -> Vec<Self::Message> { vec![] }

	/// Tags for which the app wants a fresh `xdg-activation-v1` token this
	/// iteration. Drained once per loop; each tag is echoed back verbatim
	/// through [`App::on_activation_token`] once the compositor issues the
	/// token, so the app can hand it to a child it is about to spawn via the
	/// `$XDG_ACTIVATION_TOKEN` launch convention. Default none.
	fn take_activation_requests( &mut self ) -> Vec<String> { Vec::new() }

	/// Deliver a token issued for an earlier [`App::take_activation_requests`]
	/// tag. `tag` is the string the app handed out; `token` is the opaque
	/// activation token to place in the launched child's environment.
	fn on_activation_token( &mut self, _tag: String, _token: String ) -> Option<Self::Message> { None }

	/// Called once, immediately after the first buffer for the main surface
	/// has been rendered and committed to the compositor. Note: this is the
	/// client-side commit, not the compositor's present — under VT switching
	/// the actual present can be deferred (no DRM master yet), so a handoff
	/// signal that fires here is enough to tell a parent "ready to be
	/// presented as soon as my VT becomes active".
	fn on_first_frame_committed( &mut self ) {}

	/// Called when the surface is asked to close (compositor request, titlebar button,
	/// layer-shell closed event). Return `true` to allow the application to exit,
	/// or `false` to cancel.
	///
	/// Default: `true` (always close).
	fn on_close_requested( &mut self ) -> bool { true }

	/// Called when tapping/clicking outside any widget.
	fn on_tap( &mut self ) -> Option<Self::Message> { None }

	/// Called on key press when no text input is focused.
	fn on_key( &mut self, _keysym: Keysym ) -> Option<Self::Message> { None }

	/// Called on key press with modifier state. Override this instead of
	/// `on_key` when you need Ctrl/Shift/Alt awareness.
	fn on_key_with_modifiers( &mut self, keysym: Keysym, _ctrl: bool, _shift: bool ) -> Option<Self::Message>
	{
		self.on_key( keysym )
	}

	/// Called when a sufficient upward swipe gesture is detected.
	///
	/// Return a message to handle the swipe (e.g., opening an overview).
	/// The gesture is recognized when the user drags upward by at least
	/// `swipe_threshold()` × screen height and then releases.
	fn on_swipe_up( &mut self ) -> Option<Self::Message> { None }

	/// Called during an upward swipe gesture with progress ≥ 0.0.
	///
	/// Use this to create follow-the-finger animations. `progress` starts at 0.0
	/// when the drag begins and increases as the finger moves upward, reaching 1.0
	/// when the drag distance equals `swipe_threshold()` × screen height. It is
	/// **not** clamped at 1.0 — the value keeps growing if the finger drags further
	/// so that follow-the-finger panels can continue tracking the finger all the way
	/// up the screen. Clamp inside the handler if you need a bounded `[0, 1]` range
	/// for a visual indicator.
	///
	/// When the user releases without completing the gesture, this method is called
	/// once more with `progress = 0.0` to signal cancellation.
	fn on_swipe_progress( &mut self, _progress: f32 ) {}

	/// Called when a sufficient downward swipe gesture is detected.
	///
	/// Return a message to handle the swipe (e.g., toggling quick settings).
	/// The gesture is recognized when the user drags downward by at least
	/// `swipe_down_threshold()` × screen height and then releases.
	fn on_swipe_down( &mut self ) -> Option<Self::Message> { None }

	/// Called during a downward swipe gesture with progress ≥ 0.0.
	///
	/// Use this to create follow-the-finger animations. `progress` starts at 0.0
	/// when the drag begins and increases as the finger moves downward, reaching
	/// 1.0 when the drag distance equals `swipe_down_threshold()` × screen height.
	/// It is **not** clamped at 1.0 — the value keeps growing if the finger drags
	/// further so that follow-the-finger panels can continue tracking the finger
	/// all the way down the screen. Clamp inside the handler if you need a
	/// bounded `[0, 1]` range for a visual indicator.
	///
	/// When the user releases without completing the gesture, this method is called
	/// once more with `progress = 0.0` to signal cancellation.
	fn on_swipe_down_progress( &mut self, _progress: f32 ) {}

	/// Fraction of screen height required to trigger [`on_swipe_up`](Self::on_swipe_up).
	///
	/// Default: `0.6` (60% of screen height). Lower values make the gesture easier
	/// to trigger; higher values require more vertical travel.
	fn swipe_threshold( &self ) -> f32 { 0.6 }

	/// Fraction of screen height required to trigger [`on_swipe_down`](Self::on_swipe_down).
	///
	/// Default: `0.15` (15% of screen height). Typically lower than `swipe_threshold()`
	/// because downward swipes are often initiated from a small UI element like a top bar.
	fn swipe_down_threshold( &self ) -> f32 { 0.15 }

	/// Top-edge band where a downward swipe may originate, as a fraction of
	/// screen height. Presses below this band never fire
	/// [`on_swipe_down`](Self::on_swipe_down) nor
	/// [`on_swipe_down_progress`](Self::on_swipe_down_progress).
	///
	/// Default: `1.0` (entire screen — any starting point is accepted).
	/// Set to a small value like `0.05` to restrict the gesture to the top
	/// edge, matching the usual system-panel pull-down UX.
	fn swipe_down_edge( &self ) -> f32 { 1.0 }

	/// Called while a vertical `scroll` is pinned at
	/// its top and the finger keeps pulling down. `progress` is the
	/// accumulated overscroll as a fraction of the surface height (≥ 0.0,
	/// unbounded). Use it to drive a pull-from-top-to-dismiss panel that
	/// tracks the finger.
	fn on_overscroll_down_progress( &mut self, _progress: f32 ) {}

	/// Called when the finger releases after a top overscroll pull-down.
	/// `committed` is `true` when the pull passed
	/// [`overscroll_dismiss_threshold`](Self::overscroll_dismiss_threshold).
	fn on_overscroll_down( &mut self, _committed: bool ) -> Option<Self::Message> { None }

	/// Fraction of surface height a top overscroll pull-down must exceed to
	/// commit a dismiss on release. Default: `0.25`.
	fn overscroll_dismiss_threshold( &self ) -> f32 { 0.25 }

	/// Called when a sufficient leftward swipe gesture is detected.
	///
	/// Return a message to handle the swipe (e.g., paging to the next
	/// homescreen). Fires on release when the user dragged left by at least
	/// `swipe_horizontal_threshold()` × screen width. Unlike vertical swipes,
	/// horizontal swipes survive starting the press on an interactive widget:
	/// once the drag distance crosses an 8 px threshold the press is promoted
	/// to a horizontal drag and no tap will fire at release.
	fn on_swipe_left( &mut self ) -> Option<Self::Message> { None }

	/// Called when a sufficient rightward swipe gesture is detected.
	/// Mirror of [`on_swipe_left`](Self::on_swipe_left) for the other direction.
	fn on_swipe_right( &mut self ) -> Option<Self::Message> { None }

	/// Called during a horizontal swipe with signed progress. Negative values
	/// mean the finger has moved left, positive values mean right. The value
	/// is `dx / (threshold * width)`, so ±1.0 marks the commit threshold;
	/// it is **not** clamped so follow-the-finger panels can keep tracking
	/// past the threshold. When the user releases without completing the
	/// gesture this method is called once more with `0.0` to signal
	/// cancellation.
	fn on_swipe_horizontal_progress( &mut self, _progress: f32 ) {}

	/// Fraction of screen width required to commit [`App::on_swipe_left`] /
	/// [`App::on_swipe_right`]. Default: `0.5` (half the screen). A shell that
	/// wants easier paging can lower this; one that wants to avoid
	/// accidental pages can raise it.
	fn swipe_horizontal_threshold( &self ) -> f32 { 0.5 }

	/// Duration a press must remain stationary (within tolerance) before the
	/// widget's long-press message fires and the gesture transitions into
	/// drag mode. Default: `500 ms`.
	fn long_press_duration( &self ) -> std::time::Duration
	{
		std::time::Duration::from_millis( 500 )
	}

	/// Called on every pointer motion once a long-press has fired and the
	/// gesture has entered drag mode. `x`, `y` are in physical pixels of the
	/// surface that owned the original press.
	fn on_drag_move( &mut self, _x: f32, _y: f32 ) {}

	/// Called on release once a long-press has fired. Return a message to
	/// handle the drop (e.g., commit the drop target). `x`, `y` are in
	/// physical pixels of the surface that owned the original press. The
	/// regular tap / swipe handling is suppressed for this gesture.
	fn on_drop( &mut self, _x: f32, _y: f32 ) -> Option<Self::Message> { None }

	/// Called when a text-input widget gains or loses focus.
	fn on_text_input_focused( &mut self, _active: bool ) {}

	/// Translate a Wayland `ext-foreign-toplevel-list-v1` event into an
	/// app message. The runtime binds the protocol globally and forwards
	/// every open / close it sees here; apps that ignore window state
	/// leave the default (return `None`) and pay nothing.
	///
	/// The `id` field is the Wayland object id of the toplevel handle —
	/// unique within the session, stable for the handle's lifetime, the
	/// same value coming in on `Closed` as the matching `Opened`.
	/// `app_id` on `Opened` is the toplevel's `app_id` event (may be
	/// empty if the compositor never sent one before the first `done`).
	fn on_toplevel_event( &self, _event: ToplevelEvent ) -> Option<Self::Message>
	{
		None
	}

	/// Return `Some(id)` once to focus the widget with that [`WidgetId`](crate::types::WidgetId).
	/// The app must return `None` after the first call.
	fn take_focus_request( &mut self ) -> Option<crate::types::WidgetId> { None }

	/// Called on every pointer motion (mouse move, button press, button
	/// release). `x`, `y` are in physical pixels of the main surface,
	/// the same coordinate space as widget rects. Default: no-op.
	///
	/// Useful for embedding components that need to know the live
	/// pointer position without going through a full
	/// [`crate::widget::external::ExternalSource`] / `WidgetHandlers`
	/// dispatch path — for example forwarding clicks into an embedded
	/// WPE WebView.
	fn on_pointer_move( &mut self, _x: f32, _y: f32 ) {}

	/// Called on a wheel / touchpad scroll event whose position does
	/// not fall inside any LTK [`scroll`](crate::scroll) viewport. `x`,
	/// `y` are in physical pixels (same coordinate space as
	/// [`Self::on_pointer_move`]); `dx`, `dy` are scroll deltas in the
	/// raw axis units the compositor delivered (`AxisSource::Wheel`
	/// gives ~10 px ticks, touchpads give continuous values). Default:
	/// no-op.
	///
	/// Useful for embeddings that take over scrolling for their own
	/// content — for example forwarding the event to a WPE view that
	/// owns a scrollable web page — or for wheel-driven view state such
	/// as a stepped carousel. The runtime rebuilds and repaints after
	/// the hook returns, so state mutated here shows immediately.
	fn on_pointer_axis( &mut self, _x: f32, _y: f32, _dx: f32, _dy: f32 ) {}

	/// Raw multi-touch callbacks. Default: no-op.
	///
	/// The first finger to land becomes the *primary slot* and is fed
	/// through the regular gesture machine — `on_pointer_*`, swipe,
	/// scroll, long-press, drag-and-drop all run from that slot. Every
	/// additional finger fires `on_touch_down` / `on_touch_move` /
	/// `on_touch_up` instead, with `id` matching the `wl_touch.id`
	/// the compositor delivered.
	///
	/// Use this for app-defined multi-finger gestures (pinch-zoom,
	/// two-finger pan in a canvas, parallel keyboard keys) that the
	/// built-in single-slot machine cannot model. `(x, y)` are in
	/// physical pixels, matching the coordinate space of
	/// [`Self::on_pointer_move`].
	///
	/// When [`Self::claims_raw_touch`] returns `true` the primary
	/// finger arrives here as well, instead of driving the gesture
	/// machine.
	fn on_touch_down( &mut self, _id: i64, _x: f32, _y: f32 ) {}
	/// See [`Self::on_touch_down`].
	fn on_touch_move( &mut self, _id: i64, _x: f32, _y: f32 ) {}
	/// See [`Self::on_touch_down`]. `x`, `y` are the last known
	/// position of the finger (`wl_touch.up` does not carry one).
	fn on_touch_up( &mut self, _id: i64, _x: f32, _y: f32 ) {}

	/// Return `true` to receive the *primary* finger through the raw
	/// [`Self::on_touch_down`] / move / up stream as well, bypassing
	/// the built-in single-slot gesture machine entirely — widget
	/// presses, taps and swipes stop working on touch. For apps whose
	/// surface is one self-contained input consumer (an embedded
	/// WebView, a game canvas) that needs every finger verbatim.
	fn claims_raw_touch( &self ) -> bool { false }

	/// Pointer of a cross-application drag-and-drop entered or moved
	/// inside the main surface. `(x, y)` is in logical pixels.
	fn on_drop_motion( &mut self, _x: f32, _y: f32 ) {}
	/// The drag left without dropping.
	fn on_drop_leave( &mut self ) {}
	/// External drop payload landed on the surface. `mime` is the
	/// best mime the source offered (text/uri-list, text/plain, …)
	/// and `text` is the decoded payload (UTF-8). `(x, y)` is the
	/// release position in logical pixels.
	fn on_drop_received( &mut self, _x: f32, _y: f32, _mime: &str, _text: &str ) {}

	/// Called on every left-button mouse press / release before the
	/// regular gesture machine fires. `pressed = true` for press,
	/// `false` for release. `(x, y)` are in physical pixels (same
	/// coordinate space as [`Self::on_pointer_move`]). Default: no-op.
	///
	/// Lets embeddings see the raw button transitions without going
	/// through LTK's tap/long-press/drag classification — needed for
	/// forwarding clicks AND drags to an embedded view, since the tap
	/// classifier collapses press+release into a single event.
	fn on_pointer_button( &mut self, _x: f32, _y: f32, _pressed: bool ) {}

	/// Called on every keyboard event (press *and* release) before the
	/// regular focus-aware dispatch ([`Self::on_key`] /
	/// [`Self::on_key_with_modifiers`]) runs. Default: no-op.
	///
	/// `keycode` is the raw hardware scancode straight from the
	/// compositor (`wl_keyboard.key`'s `key` argument); `keysym` is
	/// the xkb-translated keysym the user effectively pressed. Most
	/// consumers only care about `keysym`, but embeddings that
	/// forward into an inner window system (a WPE web view, an X11
	/// emulator, …) typically need both — the inner system does its
	/// own keycode → keysym translation for layout-aware shortcuts.
	///
	/// This bypass exists for those embeddings: it fires *in addition*
	/// to the normal callbacks; consume or ignore at will.
	fn on_raw_key( &mut self, _keysym: Keysym, _keycode: u32, _pressed: bool, _ctrl: bool, _shift: bool ) {}

	/// Called when the surface is (re-)configured with a new size.
	///
	/// `width` and `height` are in **physical pixels** (the Wayland surface
	/// size multiplied by the current buffer scale). They therefore match the
	/// coordinate space used for layout and the pointer/touch callbacks
	/// (`on_drag_move`, `on_drop`, widget hit-testing). Also fired when the
	/// buffer scale changes, so apps can refresh any state keyed off the old
	/// physical dimensions.
	fn on_resize( &mut self, _width: u32, _height: u32 ) {}

	/// Called when the compositor reports a new integer buffer scale for the
	/// main surface. `scale` is the integer buffer scale (1 = standard DPI,
	/// 2 = HiDPI ×2); physical pixels equal logical pixels × scale. The default
	/// is inert so apps that only care about physical pixels can keep using
	/// [`on_resize`](Self::on_resize).
	fn on_scale_changed( &mut self, _scale: u32 ) {}

	/// Called once at startup with a channel sender that can be used from any
	/// thread to deliver messages into the event loop.  Sending a message
	/// immediately wakes the event loop — no polling delay.
	fn set_channel_sender( &mut self, _sender: ChannelSender<Self::Message> ) {}

	/// How often the event loop should call [`poll_external`](Self::poll_external) for
	/// periodic work such as clock ticks. Return `None` (the default) to disable the
	/// timer — `poll_external` will still be called after every Wayland event.
	fn poll_interval( &self ) -> Option<std::time::Duration> { None }

	/// Delay before the runtime starts repeating a held-down key.
	///
	/// `Some(d)` overrides whatever the compositor advertises through
	/// `wl_keyboard.repeat_info`; `None` (the default) means "use the
	/// compositor's setting, or fall back to 500 ms when the compositor
	/// did not provide one".
	///
	/// Returning `Some(Duration::ZERO)` effectively disables the wait
	/// before the first repeat — useful only for diagnostic builds.
	fn key_repeat_delay( &self ) -> Option<std::time::Duration> { None }

	/// Interval between successive synthetic key events while a key is
	/// held down past the initial [`Self::key_repeat_delay`].
	///
	/// `Some(d)` overrides the compositor's `wl_keyboard.repeat_info`
	/// rate; `None` (the default) means "use the compositor's setting,
	/// or fall back to 33 ms (~30 Hz) when the compositor did not
	/// provide one". Pass `Some(Duration::ZERO)` to disable repeats
	/// entirely (or override the per-key gate via
	/// [`Self::key_repeats`]).
	fn key_repeat_interval( &self ) -> Option<std::time::Duration> { None }

	/// Override the pointer cursor shape globally. When `Some(shape)`,
	/// the runtime sends that shape to the compositor regardless of
	/// which widget the pointer is over — useful for "the app is busy"
	/// states (return [`crate::CursorShape::Wait`]) or while the app
	/// is doing a long synchronous operation. Returning `None` (the
	/// default) lets per-widget defaults take effect.
	///
	/// The runtime calls this every frame, so flipping a `loading`
	/// boolean in [`Self::update`] propagates to the cursor on the
	/// next iteration without any extra wiring.
	fn cursor_override( &self ) -> Option<crate::types::CursorShape> { None }

	/// Decide whether a given key participates in held-key repeat.
	///
	/// Default: every non-modifier key repeats *except* `Escape`, `Tab`
	/// and `ISO_Left_Tab` — those drive one-shot UI semantics
	/// (dismiss / focus cycle) where a held key would multiply the
	/// effect in surprising ways. Override to widen or narrow the gate
	/// (a chess clock app, for example, might want even Tab to
	/// repeat).
	fn key_repeats( &self, keysym: Keysym ) -> bool
	{
		!matches!( keysym,
			Keysym::Escape | Keysym::Tab | Keysym::ISO_Left_Tab,
		)
	}

	/// Return `true` while a frame-by-frame animation is running.
	/// The event loop will keep requesting redraws at ~60 fps until this returns `false`.
	fn is_animating( &self ) -> bool { false }

	/// Cap [`Self::is_animating`] redraws to ~30 Hz when the **software**
	/// renderer is active. Default `true`: 60 fps software rasterization is a
	/// common battery sink on mobile (no GPU offload), and halving it is
	/// usually imperceptible for the animations software fallback can afford.
	/// The GLES backend is never capped. Return `false` to keep the full
	/// display rate on software.
	fn cap_software_animation( &self ) -> bool { true }

	/// Return `true` when a finger-tracked swipe or an [`Self::is_animating`]
	/// slide only repositions the app's input-transparent subsurfaces
	/// ([`Self::subsurfaces`]) while the main surface buffer stays unchanged.
	/// The runtime then skips the per-frame main re-raster that
	/// [`Self::on_swipe_progress`] and `is_animating` would otherwise force,
	/// keeping the animation pumped (motion events during the drag, a bare
	/// frame callback while `is_animating`) and letting the per-frame
	/// subsurface reconcile carry the motion. Use it for a slide-to-reveal
	/// panel over a static main surface.
	fn subsurface_motion_only( &self ) -> bool { false }

	/// Background color for the canvas. Override to make the surface
	/// transparent or to deviate from the theme. Default: the active
	/// theme's `bg` token, so the window matches the rest of the
	/// shell out of the box.
	fn background_color( &self ) -> crate::types::Color
	{
		crate::theme::palette().bg
	}

	/// Return the interactive input region as a list of rects (logical pixels).
	/// Only these areas receive pointer/touch input; the rest passes through.
	/// Return `None` (default) to receive input everywhere.
	fn input_region( &self ) -> Option<Vec<crate::types::Rect>> { None }

	/// Return `Some(( title, app_id ))` to force an XDG toplevel window instead of
	/// layer-shell overlay. The compositor will display the title in the title bar
	/// and use the app_id for taskbar/icon matching. Return `None` (default) to
	/// use layer-shell when available.
	///
	/// **Deprecated**: Use [`shell_mode`](Self::shell_mode) instead.
	fn window_config( &self ) -> Option<( &str, &str )> { None }

	/// Specify the Wayland shell mode for this application.
	///
	/// - [`ShellMode::Window`]: Normal application window (xdg-shell). **Default.**
	/// - [`ShellMode::Layer`]: System component at a specific layer (layer-shell).
	/// - [`ShellMode::SessionLock`]: `ext-session-lock-v1` lock surface (screen locker).
	///
	/// For regular applications, use the default `Window` mode.
	/// For shell components (panels, backgrounds, overlays), use `Layer`.
	/// For a screen locker, use `SessionLock` and request the unlock by
	/// returning `true` from [`requested_exit`](Self::requested_exit).
	fn shell_mode( &self ) -> ShellMode { ShellMode::Window }

	/// Content for the surfaces the runtime creates on every output beyond
	/// the first. In [`ShellMode::SessionLock`] these are lock surfaces —
	/// the compositor blanks any output without one, so each is covered. In
	/// [`ShellMode::Layer`] on [`Layer::Background`] they are background
	/// layer surfaces, so a shell's wallpaper reaches extended outputs
	/// (typically without the grid or any chrome). They take no input.
	/// `width` / `height` are that output's surface size in physical pixels.
	/// Default `None`: the surface is filled with
	/// [`background_color`](Self::background_color).
	fn secondary_view( &self, _width: u32, _height: u32 ) -> Option<Element<Self::Message>>
	{
		None
	}

	/// Return `true` to tear the surface down and exit the event loop. For a
	/// [`ShellMode::SessionLock`] surface the runtime calls `unlock` first, so
	/// the compositor lifts the lock instead of leaving the outputs blanked.
	/// Polled after every batch of `update`s.
	fn requested_exit( &self ) -> bool { false }

	/// Suggest an initial size for an xdg-shell window in logical pixels.
	///
	/// Returning `Some(( w, h ))` makes ltk call both
	/// `xdg_toplevel.set_min_size( w, h )` and
	/// `xdg_toplevel.set_max_size( w, h )` before the first commit, which
	/// most compositors honour as an exact size for the configure they
	/// send back. The window is still resizable from the application's
	/// point of view — if the compositor sends a different configure, the
	/// runtime adopts that size on the next frame.
	///
	/// Only applies to [`ShellMode::Window`] surfaces and to the
	/// xdg-shell fallback path used when `wlr-layer-shell` is missing.
	/// Ignored for layer-shell surfaces, which use [`Self::layer_size`]
	/// instead.
	///
	/// **Default**: `None` (let the compositor pick).
	fn window_size_hint( &self ) -> Option<( u32, u32 )> { None }

	/// Whether the user may resize an xdg-shell window.
	///
	/// When [`Self::window_size_hint`] is set, ltk pins
	/// `min_size == max_size` before the first commit so the compositor's
	/// first configure honours the requested size, and then releases
	/// `max_size` from the first configure so the surface becomes
	/// user-resizable. Returning `false` skips that release: the pin stays
	/// in place for the lifetime of the toplevel, declaring a fixed-size
	/// window that compositors must not resize or restore a remembered
	/// geometry over.
	///
	/// Ignored when [`Self::window_size_hint`] returns `None`.
	///
	/// **Default**: `true`.
	fn window_resizable( &self ) -> bool { true }

	/// Request fullscreen on the toplevel before its first commit.
	///
	/// When `true`, ltk calls `xdg_toplevel.set_fullscreen( None )` so
	/// the compositor picks an output and maps the surface fullscreen
	/// from the start (no flicker through a windowed configure first).
	/// The compositor's own decorations are suppressed for fullscreen
	/// surfaces; the built-in titlebar from [`Self::window_config`] is
	/// still painted by ltk on top of the surface unless the app opts
	/// out.
	///
	/// Only applies to [`ShellMode::Window`] surfaces and to the
	/// xdg-shell fallback path used when `wlr-layer-shell` is missing.
	///
	/// **Default**: `false`.
	fn start_fullscreen( &self ) -> bool { false }

	/// Specify the exclusive zone for layer-shell surfaces.
	///
	/// The exclusive zone reserves screen space for this surface.
	/// For example, a top panel with height 50 should return `50` to prevent
	/// other windows from overlapping it.
	///
	/// - `> 0`: Reserve this many pixels from the anchored edge
	/// - `0`: No exclusive zone (default for overlays)
	/// - `-1`: Don't reserve space but request focus
	///
	/// Only applies to [`ShellMode::Layer`] surfaces. Ignored for windows.
	fn exclusive_zone( &self ) -> i32 { -1 }

	/// Specify which screen edges the layer-shell surface is anchored to.
	///
	/// Anchoring determines the position and size of the surface:
	/// - [`Anchor::TOP`]: Top bar (anchored to top, left, right)
	/// - [`Anchor::BOTTOM`]: Bottom bar (anchored to bottom, left, right)
	/// - [`Anchor::LEFT`]: Left sidebar (anchored to left, top, bottom)
	/// - [`Anchor::RIGHT`]: Right sidebar (anchored to right, top, bottom)
	/// - [`Anchor::ALL`]: Full screen (anchored to all edges)
	///
	/// **Default**: `Anchor::ALL` (full screen)
	///
	/// Only applies to [`ShellMode::Layer`] surfaces. Ignored for windows.
	fn layer_anchor( &self ) -> Anchor { Anchor::ALL }

	/// Specify the desired size for layer-shell surfaces.
	///
	/// Returns `(width, height)` where:
	/// - `0` means "fill available space in that dimension"
	/// - `> 0` means "use this exact size in pixels"
	///
	/// **Examples:**
	/// - Top bar: `(0, 50)` - full width, 50px height
	/// - Bottom bar: `(0, 40)` - full width, 40px height
	/// - Side panel: `(300, 0)` - 300px width, full height
	/// - Full screen: `(0, 0)` - fill everything (default)
	///
	/// **Default**: `(0, 0)` (fill all available space)
	///
	/// Only applies to [`ShellMode::Layer`] surfaces. Ignored for windows.
	fn layer_size( &self ) -> ( u32, u32 ) { ( 0, 0 ) }

	/// Request exclusive keyboard focus for this layer-shell surface.
	///
	/// When `true`, the compositor sends keyboard events to this surface
	/// without requiring a pointer click first. Useful for greeters,
	/// lock screens, and other surfaces that must capture input immediately.
	///
	/// **Default**: `false` (on-demand keyboard interactivity)
	///
	/// Only applies to [`ShellMode::Layer`] surfaces. Ignored for windows.
	fn keyboard_exclusive( &self ) -> bool { false }
}

/// Run the application. Blocks until the window is closed.
///
/// Panics on init failure (no Wayland compositor, missing protocol, etc.).
/// Embedders that need to recover gracefully — e.g. fall back to a
/// software TTY UI when no compositor is reachable — should call
/// [`try_run`] instead and match on the returned [`RunError`].
pub fn run<A: App>( app: A )
{
	crate::event_loop::run( app );
}

/// Run the application, returning a typed error on init failure.
///
/// Same as [`run`] but recoverable: every fatal init step (Wayland
/// connection, registry, calloop event loop, `wl_compositor` / `wl_shm`
/// / `xdg_wm_base` bindings) is converted into a [`RunError`] variant
/// the caller can match on. Once init succeeds the function blocks
/// until the surface is closed and returns `Ok(())`. Errors from the
/// dispatch loop itself (already on screen) still panic — they are
/// non-recoverable since the surface state machine cannot be unwound
/// cleanly from this entry point.
///
/// Use this when:
///
/// * the application needs a graceful fallback path on systems without
///   a Wayland session (CI runners, minimal containers, X11-only
///   environments where ltk has no backend),
/// * an embedder wants to log the specific protocol that's missing
///   instead of panicking with a generic stack trace,
/// * the caller wants to retry / wait for a compositor to come up
///   instead of aborting.
///
/// The standard `run( app )` remains the simpler entry point for
/// applications that always run on a known-good Wayland session.
///
/// ```rust,no_run
/// # use ltk::{ button, App, Element, RunError };
/// # #[ derive( Clone ) ] enum Msg {}
/// # struct MyApp;
/// # impl App for MyApp {
/// #     type Message = Msg;
/// #     fn view( &self ) -> Element<Msg> { button( "x" ).into() }
/// #     fn update( &mut self, _: Msg ) {}
/// # }
/// match ltk::try_run( MyApp )
/// {
///     Ok( () ) => {}
///     Err( RunError::NoWaylandConnection( _ ) ) =>
///     {
///         eprintln!( "no compositor — falling back to stdio" );
///         // run a CLI fallback…
///     }
///     Err( RunError::MissingProtocol { name, .. } ) =>
///     {
///         eprintln!( "compositor lacks `{name}` — refusing to start" );
///         std::process::exit( 1 );
///     }
///     Err( e ) =>
///     {
///         eprintln!( "ltk init failed: {e}" );
///         std::process::exit( 1 );
///     }
/// }
/// ```
pub fn try_run<A: App>( app: A ) -> Result<(), RunError>
{
	crate::event_loop::try_run( app )
}

pub use crate::event_loop::RunError;
