//! Types for displaying a [`Widget`](crate::widget::Widget) inside of a desktop
//! window.

use std::cell::RefCell;
use std::collections::hash_map;
use std::ffi::OsStr;
use std::hash::Hash;
use std::io;
use std::marker::PhantomData;
use std::num::{NonZeroU32, TryFromIntError};
use std::ops::{Deref, DerefMut, Not};
use std::path::{Path, PathBuf};
use std::string::ToString;
use std::sync::{mpsc, Arc, OnceLock};
use std::time::{Duration, Instant};

use ahash::AHashMap;
use alot::LotId;
use arboard::Clipboard;
use figures::units::{Px, UPx};
use figures::{
    FloatConversion, Fraction, IntoSigned, IntoUnsigned, Point, Ranged, Rect, Round, ScreenScale,
    Size, UPx2D, Zero,
};
use image::{DynamicImage, RgbImage, RgbaImage};
use intentional::{Assert, Cast};
use kludgine::app::winit::dpi::{PhysicalPosition, PhysicalSize};
use kludgine::app::winit::event::{
    ElementState, Ime, Modifiers, MouseButton, MouseScrollDelta, TouchPhase,
};
use kludgine::app::winit::keyboard::{
    Key, KeyLocation, ModifiersState, NamedKey, NativeKeyCode, PhysicalKey, SmolStr,
};
use kludgine::app::winit::window::{self, Cursor, Fullscreen, Icon, WindowButtons, WindowLevel};
use kludgine::app::{winit, WindowAttributes, WindowBehavior as _};
use kludgine::cosmic_text::{fontdb, Family, FamilyOwned};
use kludgine::drawing::Drawing;
use kludgine::shapes::Shape;
use kludgine::wgpu::{self, CompositeAlphaMode, COPY_BYTES_PER_ROW_ALIGNMENT};
use kludgine::{Color, DrawableExt, Kludgine, KludgineId, Origin, Texture};
use parking_lot::{Mutex, MutexGuard};
use sealed::{Ize, PreShowCallback, WindowExecute};
use tracing::Level;
use unicode_segmentation::UnicodeSegmentation;

use crate::animation::{
    AnimationTarget, Easing, LinearInterpolate, PercentBetween, Spawn, ZeroToOne,
};
use crate::app::{Application, Cushy, Open, PendingApp, Run};
use crate::context::sealed::{InvalidationStatus, Trackable as _};
use crate::context::{
    AsEventContext, EventContext, Exclusive, GraphicsContext, LayoutContext, Trackable,
    WidgetContext,
};
use crate::fonts::FontCollection;
use crate::graphics::{FontState, Graphics};
use crate::styles::{Edges, FontFamilyList, ThemePair};
use crate::tree::Tree;
use crate::utils::ModifiersExt;
use crate::value::{
    Destination, Dynamic, DynamicReader, IntoDynamic, IntoValue, Source, Tracked, Value,
};
use crate::widget::{
    Callback, EventHandling, MakeWidget, MountedWidget, OnceCallback, RootBehavior, SharedCallback,
    WidgetId, WidgetInstance, HANDLED, IGNORED,
};
use crate::widgets::shortcuts::{ShortcutKey, ShortcutMap};
use crate::window::sealed::WindowCommand;
use crate::{App, ConstraintLimit};

/// A platform-dependent window implementation.
pub trait PlatformWindowImplementation {
    /// Marks the window to close as soon as possible.
    fn close(&mut self);
    /// Returns the underlying `winit` window, if one exists.
    fn winit(&self) -> Option<&Arc<winit::window::Window>>;
    /// Sets the window to redraw as soon as possible.
    fn set_needs_redraw(&mut self);
    /// Sets the window to redraw after a `duration`.
    fn redraw_in(&mut self, duration: Duration);
    /// Sets the window to redraw at a specified instant.
    fn redraw_at(&mut self, moment: Instant);
    /// Returns the current keyboard modifiers.
    fn modifiers(&self) -> Modifiers;
    /// Returns the amount of time that has elapsed since the last redraw.
    fn elapsed(&self) -> Duration;
    /// Sets the current cursor icon to `cursor`.
    fn set_cursor(&mut self, cursor: Cursor);
    /// Returns a handle for the window.
    fn handle(&self, redraw_status: InvalidationStatus) -> WindowHandle;
    /// Returns the current outer position of the window.
    fn outer_position(&self) -> Point<Px> {
        self.winit().map_or_else(Point::default, |w| {
            w.outer_position().unwrap_or_default().into()
        })
    }
    /// Returns the current inner position of the window.
    fn inner_position(&self) -> Point<Px> {
        self.winit().map_or_else(Point::default, |w| {
            w.inner_position().unwrap_or_default().into()
        })
    }
    /// Returns the current inner size of the window.
    fn inner_size(&self) -> Size<UPx>;
    /// Returns the current outer size of the window.
    fn outer_size(&self) -> Size<UPx> {
        self.winit()
            .map_or_else(|| self.inner_size(), |w| w.outer_size().into())
    }

    /// Returns true if the window can have its size changed.
    ///
    /// The provided implementation returns
    /// [`winit::window::Window::is_resizable`], or true if this window has no
    /// winit window.
    fn is_resizable(&self) -> bool {
        self.winit().map_or(true, |win| win.is_resizable())
    }

    /// Returns true if the window can have its size changed.
    ///
    /// The provided implementation returns [`winit::window::Window::theme`], or
    /// dark if this window has no winit window.
    fn theme(&self) -> winit::window::Theme {
        self.winit()
            .and_then(|win| win.theme())
            .unwrap_or(winit::window::Theme::Dark)
    }

    /// Requests that the window change its inner size.
    ///
    /// The provided implementation forwards the request onto the winit window,
    /// if present.
    ///
    /// The result is the same [`winit::window::Window::request_inner_size`] --
    /// if a size is returned, the change was made before the call returns, and
    /// no resized event will be emitted.
    #[must_use]
    fn request_inner_size(&mut self, inner_size: Size<UPx>) -> Option<Size<UPx>> {
        self.winit()
            .and_then(|winit| winit.request_inner_size(PhysicalSize::from(inner_size)))
            .map(Size::from)
    }

    /// Sets whether [`Ime`] events should be enabled.
    ///
    /// The provided implementation forwards the request onto the winit window,
    /// if present.
    fn set_ime_allowed(&self, allowed: bool) {
        if let Some(winit) = self.winit() {
            winit.set_ime_allowed(allowed);
        }
    }
    /// Sets the location of the cursor.
    fn set_ime_location(&self, location: Rect<Px>) {
        if let Some(winit) = self.winit() {
            winit.set_ime_cursor_area(
                PhysicalPosition::from(location.origin),
                PhysicalSize::from(location.size),
            );
        }
    }

    /// Sets the current [`Ime`] purpose.
    ///
    /// The provided implementation forwards the request onto the winit window,
    /// if present.
    fn set_ime_purpose(&self, purpose: winit::window::ImePurpose) {
        if let Some(winit) = self.winit() {
            winit.set_ime_purpose(purpose);
        }
    }

    /// Sets the window's minimum inner size.
    fn set_min_inner_size(&self, min_size: Option<Size<UPx>>) {
        if let Some(winit) = self.winit() {
            winit.set_min_inner_size::<PhysicalSize<u32>>(min_size.map(Into::into));
        }
    }

    /// Sets the window's maximum inner size.
    fn set_max_inner_size(&self, max_size: Option<Size<UPx>>) {
        if let Some(winit) = self.winit() {
            winit.set_max_inner_size::<PhysicalSize<u32>>(max_size.map(Into::into));
        }
    }

    /// Ensures that this window will be redrawn when `value` has been updated.
    fn redraw_when_changed(&self, value: &impl Trackable, invalidation_status: &InvalidationStatus)
    where
        Self: Sized,
    {
        value.inner_redraw_when_changed(self.handle(invalidation_status.clone()));
    }
}

impl PlatformWindowImplementation for kludgine::app::Window<'_, WindowCommand> {
    fn set_cursor(&mut self, cursor: Cursor) {
        self.winit().set_cursor(cursor);
    }

    fn inner_size(&self) -> Size<UPx> {
        self.winit().inner_size().into()
    }

    fn close(&mut self) {
        self.close();
    }

    fn winit(&self) -> Option<&Arc<winit::window::Window>> {
        Some(self.winit())
    }

    fn set_needs_redraw(&mut self) {
        self.set_needs_redraw();
    }

    fn redraw_in(&mut self, duration: Duration) {
        self.redraw_in(duration);
    }

    fn redraw_at(&mut self, moment: Instant) {
        self.redraw_at(moment);
    }

    fn modifiers(&self) -> Modifiers {
        self.modifiers()
    }

    fn elapsed(&self) -> Duration {
        self.elapsed()
    }

    fn handle(&self, redraw_status: InvalidationStatus) -> WindowHandle {
        WindowHandle::new(self.handle(), redraw_status)
    }

    fn request_inner_size(&mut self, inner_size: Size<UPx>) -> Option<Size<UPx>> {
        self.request_inner_size(inner_size)
    }
}

/// A platform-dependent window.
pub trait PlatformWindow {
    /// Marks the window to close as soon as possible.
    fn close(&mut self);
    /// Returns a handle for the window.
    fn handle(&self) -> WindowHandle;
    /// Returns the unique id of the [`Kludgine`] instance used by this window.
    fn kludgine_id(&self) -> KludgineId;
    /// Returns the dynamic that is synchrnoized with the window's focus.
    fn focused(&self) -> &Dynamic<bool>;
    /// Returns the dynamic that is synchronized with the window's occlusion
    /// status.
    fn occluded(&self) -> &Dynamic<bool>;
    /// Returns the current inner size of the window.
    fn inner_size(&self) -> &Dynamic<Size<UPx>>;
    /// Returns the current outer size of the window.
    fn outer_size(&self) -> Size<UPx>;
    /// Returns the shared application resources.
    fn cushy(&self) -> &Cushy;
    /// Returns the app managing this window's event loop.
    fn app(&self) -> Option<&App>;
    /// Sets the window to redraw as soon as possible.
    fn set_needs_redraw(&mut self);
    /// Sets the window to redraw after a `duration`.
    fn redraw_in(&mut self, duration: Duration);
    /// Sets the window to redraw at a specified instant.
    fn redraw_at(&mut self, moment: Instant);
    /// Returns the current keyboard modifiers.
    fn modifiers(&self) -> Modifiers;
    /// Returns the amount of time that has elapsed since the last redraw.
    fn elapsed(&self) -> Duration;
    /// Sets the current cursor icon to `cursor`.
    fn set_cursor(&mut self, cursor: Cursor);

    /// Sets the location of the cursor.
    fn set_ime_location(&self, location: Rect<Px>);
    /// Sets whether [`Ime`] events should be enabled.
    fn set_ime_allowed(&self, allowed: bool);
    /// Sets the current [`Ime`] purpose.
    fn set_ime_purpose(&self, purpose: winit::window::ImePurpose);

    /// Requests that the window change its inner size.
    #[must_use]
    fn request_inner_size(&mut self, inner_size: Size<UPx>) -> Option<Size<UPx>>;
    /// Sets the window's minimum inner size.
    fn set_min_inner_size(&self, min_size: Option<Size<UPx>>);
    /// Sets the window's maximum inner size.
    fn set_max_inner_size(&self, max_size: Option<Size<UPx>>);

    /// Returns a handle to the underlying winit window, if available.
    fn winit(&self) -> Option<&Arc<winit::window::Window>>;
}

/// A currently running Cushy window.
pub struct RunningWindow<W> {
    window: W,
    kludgine_id: KludgineId,
    invalidation_status: InvalidationStatus,
    app: App,
    focused: Dynamic<bool>,
    occluded: Dynamic<bool>,
    inner_size: Dynamic<Size<UPx>>,
    close_requested: Option<SharedCallback<(), bool>>,
}

impl<W> RunningWindow<W>
where
    W: PlatformWindowImplementation,
{
    #[allow(clippy::too_many_arguments)]
    pub(crate) fn new(
        window: W,
        kludgine_id: KludgineId,
        invalidation_status: &InvalidationStatus,
        app: &App,
        focused: &Dynamic<bool>,
        occluded: &Dynamic<bool>,
        inner_size: &Dynamic<Size<UPx>>,
        close_requested: &Option<SharedCallback<(), bool>>,
    ) -> Self {
        Self {
            window,
            kludgine_id,
            invalidation_status: invalidation_status.clone(),
            app: app.clone(),
            focused: focused.clone(),
            occluded: occluded.clone(),
            inner_size: inner_size.clone(),
            close_requested: close_requested.clone(),
        }
    }

    /// Returns the [`KludgineId`] of this window.
    ///
    /// Each window has its own unique `KludgineId`.
    #[must_use]
    pub const fn kludgine_id(&self) -> KludgineId {
        self.kludgine_id
    }

    /// Returns a dynamic that is updated whenever this window's focus status
    /// changes.
    #[must_use]
    pub const fn focused(&self) -> &Dynamic<bool> {
        &self.focused
    }

    /// Returns a dynamic that is updated whenever this window's occlusion
    /// status changes.
    #[must_use]
    pub const fn occluded(&self) -> &Dynamic<bool> {
        &self.occluded
    }

    /// Request that the window closes.
    ///
    /// A window may disallow itself from being closed by customizing
    /// [`WindowBehavior::close_requested`].
    pub fn request_close(&self) {
        self.handle().request_close();
    }

    /// Returns a handle to this window.
    #[must_use]
    pub fn handle(&self) -> WindowHandle {
        self.window.handle(self.invalidation_status.clone())
    }

    /// Returns a dynamic that is synchronized with this window's inner size.
    ///
    /// Whenever the window is resized, this dynamic will be updated with the
    /// new inner size. Setting a new value will request the new size from the
    /// operating system, but resize requests may be altered or ignored by the
    /// operating system.
    #[must_use]
    pub const fn inner_size(&self) -> &Dynamic<Size<UPx>> {
        &self.inner_size
    }

    /// Returns a locked mutex guard to the OS's clipboard, if one was able to be
    /// initialized when the window opened.
    #[must_use]
    pub fn clipboard_guard(&self) -> Option<MutexGuard<'_, Clipboard>> {
        self.app.cushy().clipboard_guard()
    }
}

impl<W> Deref for RunningWindow<W>
where
    W: PlatformWindowImplementation + 'static,
{
    type Target = dyn PlatformWindowImplementation;

    fn deref(&self) -> &Self::Target {
        &self.window
    }
}

impl<W> DerefMut for RunningWindow<W>
where
    W: PlatformWindowImplementation + 'static,
{
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.window
    }
}

impl<W> PlatformWindow for RunningWindow<W>
where
    W: PlatformWindowImplementation,
{
    fn close(&mut self) {
        self.window.close();
    }

    fn handle(&self) -> WindowHandle {
        self.window.handle(self.invalidation_status.clone())
    }

    fn kludgine_id(&self) -> KludgineId {
        self.kludgine_id
    }

    fn app(&self) -> Option<&App> {
        Some(&self.app)
    }

    fn focused(&self) -> &Dynamic<bool> {
        &self.focused
    }

    fn occluded(&self) -> &Dynamic<bool> {
        &self.occluded
    }

    fn inner_size(&self) -> &Dynamic<Size<UPx>> {
        &self.inner_size
    }

    fn outer_size(&self) -> Size<UPx> {
        self.window.outer_size()
    }

    fn cushy(&self) -> &Cushy {
        self.app.cushy()
    }

    fn set_needs_redraw(&mut self) {
        self.window.set_needs_redraw();
    }

    fn redraw_in(&mut self, duration: Duration) {
        self.window.redraw_in(duration);
    }

    fn redraw_at(&mut self, moment: Instant) {
        self.window.redraw_at(moment);
    }

    fn modifiers(&self) -> Modifiers {
        self.window.modifiers()
    }

    fn elapsed(&self) -> Duration {
        self.window.elapsed()
    }

    fn set_ime_allowed(&self, allowed: bool) {
        self.window.set_ime_allowed(allowed);
    }

    fn set_ime_purpose(&self, purpose: winit::window::ImePurpose) {
        self.window.set_ime_purpose(purpose);
    }

    fn set_cursor(&mut self, cursor: Cursor) {
        self.window.set_cursor(cursor);
    }

    fn set_min_inner_size(&self, min_size: Option<Size<UPx>>) {
        self.window.set_min_inner_size(min_size);
    }

    fn set_max_inner_size(&self, max_size: Option<Size<UPx>>) {
        self.window.set_max_inner_size(max_size);
    }

    fn request_inner_size(&mut self, inner_size: Size<UPx>) -> Option<Size<UPx>> {
        self.window.request_inner_size(inner_size)
    }

    fn set_ime_location(&self, location: Rect<Px>) {
        self.window.set_ime_location(location);
    }

    fn winit(&self) -> Option<&Arc<winit::window::Window>> {
        self.window.winit()
    }
}

/// A Cushy window that is not yet running.
#[must_use]
pub struct Window<Behavior = WidgetInstance>
where
    Behavior: WindowBehavior,
{
    /// The title to display in the title bar of the window.
    pub title: Value<String>,
    /// The colors to use to theme the user interface.
    pub theme: Value<ThemePair>,
    /// When true, the system fonts will be loaded into the font database. This
    /// is on by default.
    pub load_system_fonts: bool,
    /// The list of font families to try to find when a [`FamilyOwned::Serif`]
    /// font is requested.
    pub serif_font_family: FontFamilyList,
    /// The list of font families to try to find when a
    /// [`FamilyOwned::SansSerif`] font is requested.
    pub sans_serif_font_family: FontFamilyList,
    /// The list of font families to try to find when a [`FamilyOwned::Fantasy`]
    /// font is requested.
    pub fantasy_font_family: FontFamilyList,
    /// The list of font families to try to find when a
    /// [`FamilyOwned::Monospace`] font is requested.
    pub monospace_font_family: FontFamilyList,
    /// The list of font families to try to find when a [`FamilyOwned::Cursive`]
    /// font is requested.
    pub cursive_font_family: FontFamilyList,
    /// A collection of fonts that this window will load.
    pub fonts: FontCollection,
    /// When true, Cushy will try to use "vertical sync" to try to eliminate
    /// graphical tearing that can occur if the graphics card has a new frame
    /// presented while the monitor is currently rendering another frame.
    ///
    /// Under the hood, Cushy uses `wgpu::PresentMode::AutoVsync` when true and
    /// `wgpu::PresentMode::AutoNoVsync` when false.
    pub vsync: bool,
    /// The number of samples to perform for each pixel rendered to the screen.
    /// When 1, multisampling is disabled.
    pub multisample_count: NonZeroU32,
    /// Resizes the window to fit the contents if true.
    pub resize_to_fit: Value<bool>,

    context: Behavior::Context,
    pending: PendingWindow,
    attributes: WindowAttributes,
    on_closed: Option<OnceCallback>,
    on_init: Option<PreShowCallback>,
    on_open: Option<OnceCallback<WindowHandle>>,
    inner_size: Option<Dynamic<Size<UPx>>>,
    zoom: Option<Dynamic<Fraction>>,
    occluded: Option<Dynamic<bool>>,
    focused: Option<Dynamic<bool>>,
    theme_mode: Option<Value<ThemeMode>>,
    content_protected: Option<Value<bool>>,
    cursor_hittest: Option<Value<bool>>,
    cursor_visible: Option<Value<bool>>,
    cursor_position: Option<Dynamic<Point<Px>>>,
    window_level: Option<Value<WindowLevel>>,
    decorated: Option<Value<bool>>,
    maximized: Option<Dynamic<bool>>,
    minimized: Option<Dynamic<bool>>,
    resizable: Option<Value<bool>>,
    resize_increments: Option<Value<Size<UPx>>>,
    visible: Option<Dynamic<bool>>,
    outer_size: Option<Dynamic<Size<UPx>>>,
    inner_position: Option<Dynamic<Point<Px>>>,
    outer_position: Option<Dynamic<Point<Px>>>,
    close_requested: Option<SharedCallback<(), bool>>,
    icon: Option<Value<Option<RgbaImage>>>,
    modifiers: Option<Dynamic<Modifiers>>,
    enabled_buttons: Option<Value<WindowButtons>>,
    fullscreen: Option<Value<Option<Fullscreen>>>,
    shortcuts: Value<ShortcutMap>,
    on_file_drop: Option<Callback<FileDrop>>,
}

impl<Behavior> Default for Window<Behavior>
where
    Behavior: WindowBehavior,
    Behavior::Context: Default,
{
    fn default() -> Self {
        Self::new(Behavior::Context::default())
    }
}

impl Window {
    /// Returns a new instance using `widget` as its contents.
    pub fn for_widget<W>(widget: W) -> Self
    where
        W: MakeWidget,
    {
        Self::new(widget.make_widget())
    }
}

impl<Behavior> Window<Behavior>
where
    Behavior: WindowBehavior,
{
    /// Returns a new instance using `context` to initialize the window upon
    /// opening.
    pub fn new(context: Behavior::Context) -> Self {
        Self::new_with_pending(context, PendingWindow::default())
    }

    fn new_with_pending(context: Behavior::Context, pending: PendingWindow) -> Self {
        static EXECUTABLE_NAME: OnceLock<String> = OnceLock::new();

        let title = EXECUTABLE_NAME
            .get_or_init(|| {
                std::env::args_os()
                    .next()
                    .and_then(|path| {
                        Path::new(&path)
                            .file_name()
                            .and_then(OsStr::to_str)
                            .map(ToString::to_string)
                    })
                    .unwrap_or_else(|| String::from("Cushy App"))
            })
            .clone();
        Self {
            pending,
            title: Value::Constant(title),
            attributes: WindowAttributes::default(),
            on_open: None,
            on_closed: None,
            context,
            load_system_fonts: true,
            theme: Value::default(),
            occluded: None,
            focused: None,
            theme_mode: None,
            inner_size: None,
            serif_font_family: FontFamilyList::default(),
            sans_serif_font_family: FontFamilyList::default(),
            fantasy_font_family: FontFamilyList::default(),
            monospace_font_family: FontFamilyList::default(),
            cursive_font_family: FontFamilyList::default(),
            fonts: {
                let fonts = FontCollection::default();
                #[cfg(feature = "roboto-flex")]
                fonts.push(include_bytes!("../assets/RobotoFlex.ttf").to_vec());
                fonts
            },
            multisample_count: NonZeroU32::new(4).assert("not 0"),
            vsync: true,
            close_requested: None,
            zoom: None,
            resize_to_fit: Value::Constant(false),
            content_protected: None,
            cursor_hittest: None,
            cursor_visible: None,
            cursor_position: None,
            window_level: None,
            decorated: None,
            maximized: None,
            minimized: None,
            resizable: None,
            resize_increments: None,
            visible: None,
            outer_size: None,
            inner_position: None,
            outer_position: None,
            icon: None,
            modifiers: None,
            enabled_buttons: None,
            fullscreen: None,
            shortcuts: Value::default(),
            on_init: None,
            on_file_drop: None,
        }
    }

    /// Returns the handle to this window.
    pub const fn handle(&self) -> &WindowHandle {
        &self.pending.0
    }

    fn center_on_open(&mut self, app: App) {
        // We want to ensure that if the user has customized any of these
        // properties that we keep their dynamic.
        let outer_position = self.outer_position.clone().unwrap_or_else(|| {
            let outer_position = Dynamic::new(Point::default());
            self.outer_position = Some(outer_position.clone());
            outer_position
        });
        let outer_size = self.outer_size.clone().unwrap_or_else(|| {
            let outer_size = Dynamic::new(Size::default());
            self.outer_size = Some(outer_size.clone());
            outer_size
        });
        let visible = self.visible.clone().unwrap_or_else(|| {
            let visible = Dynamic::new(false);
            self.visible = Some(visible.clone());
            visible
        });
        visible.set(false);

        let callback_handle = Dynamic::new(None);
        callback_handle.set(Some(outer_size.for_each_subsequent({
            let visible = visible.clone();
            let callback_handle = callback_handle.clone();
            move |new_size| {
                if let Some(monitor) = app.monitors().and_then(|monitors| {
                    let initial_position = outer_position.get();
                    monitors
                        .available
                        .into_iter()
                        .find(|m| m.region().contains(initial_position))
                        .or(monitors.primary)
                }) {
                    let region = monitor.region();
                    let margin = region.size - new_size.into_signed();
                    outer_position.set(region.origin + margin / 2);
                }
                visible.set(true);
                // Uninstall this callback to ensure it doesn't fire again.
                let _ = callback_handle.take();
            }
        })));
    }

    /// Opens `self` in the center of the monitor the window initially appears
    /// on.
    pub fn open_centered<App>(mut self, app: &mut App) -> crate::Result<WindowHandle>
    where
        App: Application + ?Sized,
    {
        self.center_on_open(app.as_app());

        self.open(app)
    }

    /// Sets `focused` to be the dynamic updated when this window's focus status
    /// is changed.
    ///
    /// When the window is focused for user input, the dynamic will contain
    /// `true`.
    ///
    /// The current value of `focused` will inform the OS whether the window
    /// should be activated upon opening.
    pub fn focused(mut self, focused: impl IntoValue<bool>) -> Self {
        let focused = focused.into_value();
        self.attributes.active = focused.get();
        if let Value::Dynamic(focused) = focused {
            self.focused = Some(focused);
        }
        self
    }

    /// Sets `occluded` to be the dynamic updated when this window's occlusion
    /// status is changed.
    ///
    /// When the window is occluded (completely hidden/offscreen/minimized), the
    /// dynamic will contain `true`. If the window is at least partially
    /// visible, this value will contain `true`.
    pub fn occluded(mut self, occluded: impl IntoDynamic<bool>) -> Self {
        let occluded = occluded.into_dynamic();
        self.occluded = Some(occluded);
        self
    }

    /// Sets the full screen mode for this window.
    pub fn fullscreen(mut self, fullscreen: impl IntoValue<Option<Fullscreen>>) -> Self {
        let fullscreen = fullscreen.into_value();
        self.attributes.fullscreen = fullscreen.get();
        self.fullscreen = Some(fullscreen);
        self
    }

    /// Sets `inner_size` to be the dynamic synchronized with this window's
    /// inner size.
    ///
    /// When the window is resized, the dynamic will contain its new size. When
    /// the dynamic is updated with a new value, a resize request will be made
    /// with the new inner size.
    pub fn inner_size(mut self, inner_size: impl IntoDynamic<Size<UPx>>) -> Self {
        let inner_size = inner_size.into_dynamic();
        let initial_size = inner_size.get();
        if initial_size.width > 0 && initial_size.height > 0 {
            self.attributes.inner_size = Some(winit::dpi::Size::Physical(initial_size.into()));
        }
        self.inner_size = Some(inner_size);
        self
    }

    /// Sets `outer_size` to be a dynamic synchronized with this window's size,
    /// including decorations.
    ///
    /// When the window is resized, the dynamic will contain its new size.
    /// Setting this dynamic with a new value does not change the window in any
    /// way. To resize the window, use [`inner_size`](Self::inner_size).
    pub fn outer_size(mut self, outer_size: impl IntoDynamic<Size<UPx>>) -> Self {
        self.outer_size = Some(outer_size.into_dynamic());
        self
    }

    /// Sets `position`  to be a dynamic synchronized with this window's outer
    /// position.
    ///
    /// If `automatic_layout` is true, the initial value of `position` will be
    /// ignored and the window server will control the window's initial
    /// position.
    ///
    /// When the window is moved, this dynamic will contain its new position.
    /// Setting this dynamic will attempt to move the window to the provided
    /// location.
    pub fn outer_position(
        mut self,
        position: impl IntoValue<Point<Px>>,
        automatic_layout: bool,
    ) -> Self {
        let position = position.into_value();

        if let Some(initial_position) = (!automatic_layout).then(|| position.get()) {
            self.attributes.position =
                Some(winit::dpi::Position::Physical(initial_position.into()));
        }

        if let Value::Dynamic(position) = position {
            self.outer_position = Some(position);
        }

        self
    }

    /// Sets `position`  to be a dynamic synchronized with this window's inner
    /// position.
    ///
    /// When the window is moved, this dynamic will contain its new position.
    /// Setting this dynamic to a new value has no effect. To move a window, use
    /// [`outer_position`](Self::outer_position).
    pub fn inner_position(mut self, position: impl IntoDynamic<Point<Px>>) -> Self {
        self.inner_position = Some(position.into_dynamic());
        self
    }

    /// Resizes this window to fit the contents when `resize_to_fit` is true.
    pub fn resize_to_fit(mut self, resize_to_fit: impl IntoValue<bool>) -> Self {
        self.resize_to_fit = resize_to_fit.into_value();
        self
    }

    /// Prevents the window contents from being captured by other apps.
    pub fn content_protected(mut self, protected: impl IntoValue<bool>) -> Self {
        let protected = protected.into_value();
        self.attributes.content_protected = protected.get();
        self.content_protected = Some(protected);
        self
    }

    /// Controls whether the cursor should interact with this window or not.
    pub fn cursor_hittest(mut self, hittest: impl IntoValue<bool>) -> Self {
        self.cursor_hittest = Some(hittest.into_value());
        self
    }

    /// Sets whether the cursor is visible when above this window.
    pub fn cursor_visible(mut self, visible: impl IntoValue<bool>) -> Self {
        self.cursor_visible = Some(visible.into_value());
        self
    }

    /// A dynamic providing access to the window coordinate of the cursor, or
    /// -1, -1 if the cursor is not currently hovering the window.
    ///
    /// In the future, this dynamic will also support setting the position of
    /// the cursor within the window.
    pub fn cursor_position(mut self, window_position: impl IntoDynamic<Point<Px>>) -> Self {
        self.cursor_position = Some(window_position.into_dynamic());
        self
    }

    /// Controls whether window decorations are shown around this window.
    pub fn decorated(mut self, decorated: impl IntoValue<bool>) -> Self {
        let decorated = decorated.into_value();
        self.attributes.decorations = decorated.get();
        self.decorated = Some(decorated);
        self
    }

    /// Sets the enabled buttons for this window.
    pub fn enabled_buttons(mut self, buttons: impl IntoValue<WindowButtons>) -> Self {
        let buttons = buttons.into_value();
        self.attributes.enabled_buttons = buttons.get();
        self.enabled_buttons = Some(buttons);
        self
    }

    /// Controls the level of this window.
    pub fn window_level(mut self, window_level: impl IntoValue<WindowLevel>) -> Self {
        let window_level = window_level.into_value();
        self.attributes.window_level = window_level.get();
        self.window_level = Some(window_level);
        self
    }

    /// Provides a dynamic that is updated with the minimized status of this
    /// window.
    pub fn minimized(mut self, minimized: impl IntoDynamic<bool>) -> Self {
        self.minimized = Some(minimized.into_dynamic());
        self
    }

    /// Provides a dynamic that is updated with the maximized status of this
    /// window.
    pub fn maximized(mut self, maximized: impl IntoDynamic<bool>) -> Self {
        let maximized = maximized.into_dynamic();
        self.attributes.maximized = maximized.get();
        self.maximized = Some(maximized);
        self
    }

    /// Controls whether the window is resizable by the user or not.
    pub fn resizable(mut self, resizable: impl IntoValue<bool>) -> Self {
        let resizable = resizable.into_value();
        self.attributes.resizable = resizable.get();
        self.resizable = Some(resizable);
        self
    }

    /// Controls the increments in which the window can be resized.
    pub fn resize_increments(mut self, resize_increments: impl IntoValue<Size<UPx>>) -> Self {
        self.resize_increments = Some(resize_increments.into_value());
        self
    }

    /// Sets this window to render with a transparent background.
    pub fn transparent(mut self) -> Self {
        self.attributes.transparent = true;
        self
    }

    /// Controls the visibility of this window.
    pub fn visible(mut self, visible: impl IntoDynamic<bool>) -> Self {
        let visible = visible.into_dynamic();
        self.attributes.visible = visible.get();
        self.visible = Some(visible);
        self
    }

    /// Sets this window's `zoom` factor.
    ///
    /// The zoom factor is multiplied with the DPI scaling from the window
    /// server to allow an additional scaling factor to be applied.
    pub fn zoom(mut self, zoom: impl IntoDynamic<Fraction>) -> Self {
        self.zoom = Some(zoom.into_dynamic().map_each_into());
        self
    }

    /// Sets the [`ThemeMode`] for this window.
    ///
    /// If a [`ThemeMode`] is provided, the window will be set to this theme
    /// mode upon creation and will not be updated while the window is running.
    ///
    /// If a [`Dynamic`] is provided, the initial value will be ignored and the
    /// dynamic will be updated when the window opens with the user's current
    /// theme mode. The dynamic will also be updated any time the user's theme
    /// mode changes.
    ///
    /// Setting the [`Dynamic`]'s value will also update the window with the new
    /// mode until a mode change is detected, upon which the new mode will be
    /// stored.
    pub fn themed_mode(mut self, theme_mode: impl IntoValue<ThemeMode>) -> Self {
        self.theme_mode = Some(theme_mode.into_value());
        self
    }

    /// Applies `theme` to the widgets in this window.
    pub fn themed(mut self, theme: impl IntoValue<ThemePair>) -> Self {
        self.theme = theme.into_value();
        self
    }

    /// Adds `font_data` to the list of fonts to load for availability when
    /// rendering.
    ///
    /// All font families contained in `font_data` will be loaded.
    pub fn loading_font(self, font_data: Vec<u8>) -> Self {
        self.fonts.push(font_data);
        self
    }

    /// Invokes `on_open` when this window is first opened, even if it is not
    /// visible.
    pub fn on_open<Function>(mut self, on_open: Function) -> Self
    where
        Function: FnOnce(WindowHandle) + Send + 'static,
    {
        self.on_open = Some(OnceCallback::new(on_open));
        self
    }

    /// Invokes `on_init` before the window initialization begins.
    pub fn on_init<Function>(mut self, on_init: Function) -> Self
    where
        Function: FnOnce(&winit::window::Window) + Send + 'static,
    {
        self.on_init = Some(PreShowCallback(Box::new(Some(on_init))));
        self
    }

    /// Invokes `on_close` when this window is closed.
    pub fn on_close<Function>(mut self, on_close: Function) -> Self
    where
        Function: FnOnce() + Send + 'static,
    {
        self.on_closed = Some(OnceCallback::new(|()| on_close()));
        self
    }

    /// Invokes `on_close_requested` when the window is requested to be closed.
    ///
    /// If the function returns true, the window is allowed to be closed,
    /// otherwise the window remains open.
    pub fn on_close_requested<Function>(mut self, on_close_requested: Function) -> Self
    where
        Function: FnMut(()) -> bool + Send + 'static,
    {
        self.close_requested = Some(SharedCallback::new(on_close_requested));
        self
    }

    /// Invokes `on_file_drop` when a file is hovered or dropped on this window.
    pub fn on_file_drop<Function>(mut self, on_file_drop: Function) -> Self
    where
        Function: FnMut(FileDrop) + Send + 'static,
    {
        self.on_file_drop = Some(Callback::new(on_file_drop));
        self
    }

    /// Sets the window's title.
    pub fn titled(mut self, title: impl IntoValue<String>) -> Self {
        self.title = title.into_value();
        self
    }

    /// Sets the window's icon.
    pub fn icon(mut self, icon: impl IntoValue<Option<RgbaImage>>) -> Self {
        self.icon = Some(icon.into_value());
        self
    }

    /// Sets `modifiers` to contain the state of the keyboard modifiers when
    /// this window has keyboard focus.
    pub fn modifiers(mut self, modifiers: impl IntoDynamic<Modifiers>) -> Self {
        self.modifiers = Some(modifiers.into_dynamic());
        self
    }

    /// Sets the name of the application.
    ///
    /// - `WM_CLASS` on X11
    /// - application ID on wayland
    /// - class name on windows
    pub fn app_name(mut self, name: impl Into<String>) -> Self {
        self.attributes.app_name = Some(name.into());
        self
    }

    /// Invokes `callback` when `key` is pressed while `modifiers` are pressed.
    ///
    /// Widgets have a chance to handle keyboard input before the Window.
    pub fn with_shortcut<F>(
        mut self,
        key: impl Into<ShortcutKey>,
        modifiers: ModifiersState,
        callback: F,
    ) -> Self
    where
        F: FnMut(KeyEvent) -> EventHandling + Send + 'static,
    {
        self.shortcuts
            .map_mut(|mut shortcuts| shortcuts.insert(key, modifiers, callback));
        self
    }

    /// Invokes `shortcuts` when keyboard input is unhandled in this window.
    pub fn with_shortcuts(mut self, shortcuts: impl IntoValue<ShortcutMap>) -> Self {
        self.shortcuts = shortcuts.into_value();
        self
    }

    /// Invokes `callback` when `key` is pressed while `modifiers` are pressed.
    /// If the shortcut is held, the callback will be invoked on repeat events.
    ///
    /// Widgets have a chance to handle keyboard input before the Window.
    pub fn with_repeating_shortcut<F>(
        mut self,
        key: impl Into<ShortcutKey>,
        modifiers: ModifiersState,
        callback: F,
    ) -> Self
    where
        F: FnMut(KeyEvent) -> EventHandling + Send + 'static,
    {
        self.shortcuts
            .map_mut(|mut shortcuts| shortcuts.insert_repeating(key, modifiers, callback));
        self
    }
}

impl<Behavior> Run for Window<Behavior>
where
    Behavior: WindowBehavior,
{
    fn run(self) -> crate::Result {
        let mut app = PendingApp::default();
        self.open(&mut app)?;
        app.run()
    }
}

impl<T> Open for T
where
    T: MakeWindow,
{
    fn open<App>(self, app: &mut App) -> crate::Result<WindowHandle>
    where
        App: Application + ?Sized,
    {
        let this = self.make_window();
        let app_app = app.as_app();
        let handle = this.pending.handle();
        OpenWindow::<T::Behavior>::open_with(
            app,
            sealed::Context {
                user: this.context,
                settings: RefCell::new(sealed::WindowSettings {
                    app: app_app,
                    title: this.title,
                    redraw_status: this.pending.0.redraw_status.clone(),
                    on_open: this.on_open,
                    on_init: this.on_init,
                    on_closed: this.on_closed,
                    transparent: this.attributes.transparent,
                    attributes: Some(this.attributes),
                    occluded: this.occluded.unwrap_or_default(),
                    focused: this.focused.unwrap_or_default(),
                    inner_size: this.inner_size.unwrap_or_default(),
                    theme: Some(this.theme),
                    theme_mode: this.theme_mode,
                    font_data_to_load: this.fonts,
                    serif_font_family: this.serif_font_family,
                    sans_serif_font_family: this.sans_serif_font_family,
                    fantasy_font_family: this.fantasy_font_family,
                    monospace_font_family: this.monospace_font_family,
                    cursive_font_family: this.cursive_font_family,
                    vsync: this.vsync,
                    multisample_count: this.multisample_count,
                    close_requested: this.close_requested,
                    zoom: this.zoom.unwrap_or_else(|| Dynamic::new(Fraction::ONE)),
                    resize_to_fit: this.resize_to_fit,
                    content_protected: this.content_protected.unwrap_or_default(),
                    cursor_hittest: this.cursor_hittest.unwrap_or_else(|| Value::Constant(true)),
                    cursor_visible: this.cursor_visible.unwrap_or_else(|| Value::Constant(true)),
                    cursor_position: this.cursor_position.unwrap_or_default(),
                    window_level: this.window_level.unwrap_or_default(),
                    decorated: this.decorated.unwrap_or_else(|| Value::Constant(true)),
                    maximized: this.maximized.unwrap_or_default(),
                    minimized: this.minimized.unwrap_or_default(),
                    resizable: this.resizable.unwrap_or_else(|| Value::Constant(true)),
                    resize_increments: this.resize_increments.unwrap_or_default(),
                    visible: this.visible.unwrap_or_default(),
                    inner_position: this.inner_position.unwrap_or_default(),
                    outer_position: this.outer_position.unwrap_or_default(),
                    outer_size: this.outer_size.unwrap_or_default(),
                    window_icon: this.icon.unwrap_or_default(),
                    modifiers: this.modifiers.unwrap_or_default(),
                    enabled_buttons: this
                        .enabled_buttons
                        .unwrap_or(Value::Constant(WindowButtons::all())),
                    fullscreen: this.fullscreen.unwrap_or_default(),
                    shortcuts: this.shortcuts,
                    on_file_drop: this.on_file_drop,
                }),
                pending: this.pending,
            },
        )?;

        Ok(handle)
    }

    fn run_in(self, mut app: PendingApp) -> crate::Result {
        self.open(&mut app)?;
        app.run()
    }
}

/// A type that can be made into a [`Window`].
pub trait MakeWindow {
    /// The behavior associated with this window.
    type Behavior: WindowBehavior;

    /// Returns a new window from `self`.
    fn make_window(self) -> Window<Self::Behavior>;

    /// Opens `self` in the center of the monitor the window initially appears
    /// on.
    fn open_centered<App>(self, app: &mut App) -> crate::Result<WindowHandle>
    where
        Self: Sized,
        App: Application + ?Sized,
    {
        self.make_window().open_centered(app)
    }

    /// Runs `self` in the center of the monitor the window
    /// initially appears on.
    fn run_centered(self) -> crate::Result
    where
        Self: Sized,
    {
        self.make_window().run()
    }

    /// Runs `app` after opening `self` in the center of the monitor the window
    /// initially appears on.
    fn run_centered_in(self, mut app: PendingApp) -> crate::Result
    where
        Self: Sized,
    {
        self.make_window().open_centered(&mut app)?;
        app.run()
    }
}

impl<Behavior> MakeWindow for Window<Behavior>
where
    Behavior: WindowBehavior,
{
    type Behavior = Behavior;

    fn make_window(self) -> Window<Self::Behavior> {
        self
    }
}

impl<T> MakeWindow for T
where
    T: MakeWidget,
{
    type Behavior = WidgetInstance;

    fn make_window(self) -> Window<Self::Behavior> {
        Window::for_widget(self.make_widget())
    }
}

/// A file drop event for a window.
pub struct FileDrop {
    /// The handle to the window the file drop event is for.
    pub window: WindowHandle,
    /// The file drop event.
    pub drop: DropEvent<PathBuf>,
}

/// A drop event.
pub enum DropEvent<T> {
    /// The payload is being hovered over the container.
    Hover(T),
    /// The payload has been dropped on the container.
    Dropped(T),
    /// The payload previously hovered has been cancelled.
    Cancelled,
}

impl<T> DropEvent<T> {
    /// Returns the payload of this event.
    #[must_use]
    pub fn payload(&self) -> Option<&T> {
        match self {
            Self::Hover(t) | Self::Dropped(t) => Some(t),
            Self::Cancelled => None,
        }
    }
}

/// The behavior of a Cushy window.
pub trait WindowBehavior: Sized + 'static {
    /// The type that is provided when initializing this window.
    type Context: Send + 'static;

    /// Return a new instance of this behavior using `context`.
    fn initialize(
        window: &mut RunningWindow<kludgine::app::Window<'_, WindowCommand>>,
        context: Self::Context,
    ) -> Self;

    /// Create the window's root widget. This function is only invoked once.
    fn make_root(&mut self) -> WidgetInstance;

    /// Invoked once the window has been fully initialized.
    #[allow(unused_variables)]
    fn initialized<W>(&mut self, window: &mut W)
    where
        W: PlatformWindow,
    {
    }

    /// The window has been requested to close. If this function returns true,
    /// the window will be closed. Returning false prevents the window from
    /// closing.
    #[allow(unused_variables)]
    fn close_requested<W>(&self, window: &mut W) -> bool
    where
        W: PlatformWindow,
    {
        true
    }

    /// Runs this behavior as an application.
    fn run() -> crate::Result
    where
        Self::Context: Default,
    {
        Self::run_with(<Self::Context>::default())
    }

    /// Runs this behavior as an application, initialized with `context`.
    fn run_with(context: Self::Context) -> crate::Result {
        Window::<Self>::new(context).run()
    }
}

#[allow(clippy::struct_excessive_bools)]
struct OpenWindow<T> {
    behavior: T,
    tree: Tree,
    root: MountedWidget,
    contents: Drawing,
    cursor: CursorState,
    mouse_buttons: AHashMap<DeviceId, AHashMap<MouseButton, WidgetId>>,
    redraw_status: InvalidationStatus,
    initial_frame: bool,
    occluded: Dynamic<bool>,
    focused: Dynamic<bool>,
    inner_size: Tracked<Dynamic<Size<UPx>>>,
    outer_size: Dynamic<Size<UPx>>,
    keyboard_activated: Option<WidgetId>,
    min_inner_size: Option<Size<UPx>>,
    max_inner_size: Option<Size<UPx>>,
    resize_to_fit: Value<bool>,
    theme: Option<DynamicReader<ThemePair>>,
    current_theme: ThemePair,
    theme_mode: Value<ThemeMode>,
    transparent: bool,
    fonts: FontState,
    app: App,
    on_closed: Option<OnceCallback>,
    vsync: bool,
    dpi_scale: Dynamic<Fraction>,
    zoom: Tracked<Dynamic<Fraction>>,
    close_requested: Option<SharedCallback<(), bool>>,
    content_protected: Tracked<Value<bool>>,
    cursor_hittest: Tracked<Value<bool>>,
    cursor_visible: Tracked<Value<bool>>,
    cursor_position: Tracked<Dynamic<Point<Px>>>,
    window_level: Tracked<Value<WindowLevel>>,
    decorated: Tracked<Value<bool>>,
    maximized: Tracked<Dynamic<bool>>,
    minimized: Tracked<Dynamic<bool>>,
    resizable: Tracked<Value<bool>>,
    resize_increments: Tracked<Value<Size<UPx>>>,
    visible: Tracked<Dynamic<bool>>,
    outer_position: Tracked<Dynamic<Point<Px>>>,
    inner_position: Dynamic<Point<Px>>,
    window_icon: Tracked<Value<Option<RgbaImage>>>,
    enabled_buttons: Tracked<Value<WindowButtons>>,
    fullscreen: Tracked<Value<Option<Fullscreen>>>,
    modifiers: Dynamic<Modifiers>,
    shortcuts: Value<ShortcutMap>,
    on_file_drop: Option<Callback<FileDrop>>,
    disabled_resize_automatically: bool,
}

impl<T> OpenWindow<T>
where
    T: WindowBehavior,
{
    fn request_close(
        behavior: &mut T,
        window: &mut RunningWindow<kludgine::app::Window<'_, WindowCommand>>,
    ) -> bool {
        behavior.close_requested(window)
            && window
                .close_requested
                .as_ref()
                .map_or(true, |close| close.invoke(()))
    }

    fn keyboard_activate_widget<W>(
        &mut self,
        is_pressed: bool,
        widget: Option<LotId>,
        window: &mut W,
        kludgine: &mut Kludgine,
    ) where
        W: PlatformWindow,
    {
        if is_pressed {
            if let Some(default) = widget.and_then(|id| self.tree.widget_from_node(id)) {
                if let Some(previously_active) = self
                    .keyboard_activated
                    .take()
                    .and_then(|id| self.tree.widget(id))
                {
                    EventContext::new(
                        WidgetContext::new(
                            previously_active,
                            &self.current_theme,
                            window,
                            &mut self.fonts,
                            self.theme_mode.get(),
                            &mut self.cursor,
                        ),
                        kludgine,
                    )
                    .deactivate();
                }
                EventContext::new(
                    WidgetContext::new(
                        default.clone(),
                        &self.current_theme,
                        window,
                        &mut self.fonts,
                        self.theme_mode.get(),
                        &mut self.cursor,
                    ),
                    kludgine,
                )
                .activate();
                self.keyboard_activated = self.tree.active_widget_id();
            }
        } else if let Some(keyboard_activated) = self
            .keyboard_activated
            .take()
            .and_then(|id| self.tree.widget(id))
        {
            EventContext::new(
                WidgetContext::new(
                    keyboard_activated,
                    &self.current_theme,
                    window,
                    &mut self.fonts,
                    self.theme_mode.get(),
                    &mut self.cursor,
                ),
                kludgine,
            )
            .deactivate();
        }
    }

    fn constrain_window_resizing<W>(
        &mut self,
        resizable: bool,
        window: &mut RunningWindow<W>,
        graphics: &mut kludgine::Graphics<'_>,
    ) -> RootMode
    where
        W: PlatformWindowImplementation,
    {
        let mut root_or_child = self.root.widget.clone();
        let mut root_mode = None;
        let mut padding = Edges::<Px>::default();

        loop {
            let Some(managed) = self.tree.widget(root_or_child.id()) else {
                break;
            };

            let mut context = EventContext::new(
                WidgetContext::new(
                    managed,
                    &self.current_theme,
                    window,
                    &mut self.fonts,
                    self.theme_mode.get(),
                    &mut self.cursor,
                ),
                graphics,
            );
            let mut widget = root_or_child.lock();
            match widget.as_widget().root_behavior(&mut context) {
                Some((behavior, child)) => {
                    let child = child.clone();
                    match behavior {
                        RootBehavior::PassThrough => {}
                        RootBehavior::Expand => {
                            root_mode = root_mode.or(Some(RootMode::Expand));
                        }
                        RootBehavior::Align => {
                            root_mode = root_mode.or(Some(RootMode::Align));
                        }
                        RootBehavior::Pad(edges) => {
                            padding += edges.into_px(context.kludgine.scale());
                        }
                        RootBehavior::Resize(range) => {
                            let padding = padding.size();
                            let min_width = range
                                .width
                                .minimum()
                                .map_or(Px::ZERO, |width| width.into_px(context.kludgine.scale()))
                                .saturating_add(padding.width);
                            let max_width = range
                                .width
                                .maximum()
                                .map_or(Px::MAX, |width| width.into_px(context.kludgine.scale()))
                                .saturating_add(padding.width);
                            let min_height = range
                                .height
                                .minimum()
                                .map_or(Px::ZERO, |height| height.into_px(context.kludgine.scale()))
                                .saturating_add(padding.height);
                            let max_height = range
                                .height
                                .maximum()
                                .map_or(Px::MAX, |height| height.into_px(context.kludgine.scale()))
                                .saturating_add(padding.height);

                            let new_min_size = (min_width > 0 || min_height > 0)
                                .then_some(Size::new(min_width, min_height).into_unsigned());

                            if new_min_size != self.min_inner_size && resizable {
                                context.set_min_inner_size(new_min_size);
                                self.min_inner_size = new_min_size;
                            }
                            let new_max_size = (max_width > 0 || max_height > 0)
                                .then_some(Size::new(max_width, max_height).into_unsigned());

                            if new_max_size != self.max_inner_size && resizable {
                                context.set_max_inner_size(new_max_size);
                            }
                            self.max_inner_size = new_max_size;

                            break;
                        }
                    }
                    drop(widget);

                    root_or_child = child.clone();
                }
                None => break,
            }
        }

        root_mode.unwrap_or(RootMode::Fit)
    }

    fn load_fonts(
        settings: &mut sealed::WindowSettings,
        app_fonts: FontCollection,
        fontdb: &mut fontdb::Database,
    ) -> FontState {
        let fonts = FontState::new(fontdb, settings.font_data_to_load.clone(), app_fonts);
        fonts.apply_font_family_list(
            &settings.serif_font_family,
            || default_family(Family::Serif),
            |name| fontdb.set_serif_family(name),
        );

        fonts.apply_font_family_list(
            &settings.sans_serif_font_family,
            || {
                let bundled_font_name;
                #[cfg(feature = "roboto-flex")]
                {
                    bundled_font_name = Some(String::from("Roboto Flex"));
                }
                #[cfg(not(feature = "roboto-flex"))]
                {
                    bundled_font_name = None;
                }

                bundled_font_name.map_or_else(
                    || default_family(Family::SansSerif),
                    |name| Some(FamilyOwned::Name(name)),
                )
            },
            |name| fontdb.set_sans_serif_family(name),
        );
        fonts.apply_font_family_list(
            &settings.fantasy_font_family,
            || default_family(Family::Fantasy),
            |name| fontdb.set_fantasy_family(name),
        );
        fonts.apply_font_family_list(
            &settings.monospace_font_family,
            || default_family(Family::Monospace),
            |name| fontdb.set_monospace_family(name),
        );
        fonts.apply_font_family_list(
            &settings.cursive_font_family,
            || default_family(Family::Cursive),
            |name| fontdb.set_cursive_family(name),
        );
        fonts
    }

    fn handle_window_keyboard_input<W>(
        &mut self,
        window: &mut W,
        kludgine: &mut Kludgine,
        input: KeyEvent,
    ) -> EventHandling
    where
        W: PlatformWindow,
    {
        match input.logical_key {
            Key::Character(ch) if ch == "w" && window.modifiers().primary() => {
                if !input.repeat
                    && input.state.is_pressed()
                    && self.behavior.close_requested(window)
                {
                    window.close();
                    window.set_needs_redraw();
                }
                HANDLED
            }
            Key::Named(NamedKey::Space) if !window.modifiers().possible_shortcut() => {
                let target = self.tree.focused_widget().unwrap_or(self.root.node_id);
                let target = self.tree.widget_from_node(target).expect("missing widget");
                let mut target = EventContext::new(
                    WidgetContext::new(
                        target,
                        &self.current_theme,
                        window,
                        &mut self.fonts,
                        self.theme_mode.get(),
                        &mut self.cursor,
                    ),
                    kludgine,
                );

                match input.state {
                    ElementState::Pressed => {
                        if target.active() {
                            target.deactivate();
                            target.apply_pending_state();
                        }
                        target.activate();
                    }
                    ElementState::Released => {
                        target.deactivate();
                    }
                }
                HANDLED
            }

            Key::Named(NamedKey::Tab) if !window.modifiers().possible_shortcut() => {
                if input.state.is_pressed() {
                    let reverse = window.modifiers().state().shift_key();

                    let target = self.tree.focused_widget().unwrap_or(self.root.node_id);
                    let target = self.tree.widget_from_node(target).expect("missing widget");
                    let mut target = EventContext::new(
                        WidgetContext::new(
                            target,
                            &self.current_theme,
                            window,
                            &mut self.fonts,
                            self.theme_mode.get(),
                            &mut self.cursor,
                        ),
                        kludgine,
                    );

                    if reverse {
                        target.return_focus();
                    } else {
                        target.advance_focus();
                    }
                }
                HANDLED
            }
            Key::Named(NamedKey::Enter) => {
                self.keyboard_activate_widget(
                    input.state.is_pressed(),
                    self.tree.default_widget(),
                    window,
                    kludgine,
                );
                HANDLED
            }
            Key::Named(NamedKey::Escape) => {
                self.keyboard_activate_widget(
                    input.state.is_pressed(),
                    self.tree.escape_widget(),
                    window,
                    kludgine,
                );
                HANDLED
            }
            _ => {
                tracing::event!(
                    Level::DEBUG,
                    logical = ?input.logical_key,
                    physical = ?input.physical_key,
                    state = ?input.state,
                    "Ignored Keyboard Input",
                );
                IGNORED
            }
        }
    }

    #[allow(clippy::needless_pass_by_value)]
    fn new<W>(
        mut behavior: T,
        mut window: W,
        graphics: &mut kludgine::Graphics<'_>,
        mut settings: sealed::WindowSettings,
    ) -> Self
    where
        W: PlatformWindowImplementation,
    {
        let redraw_status = settings.redraw_status.clone();
        if let Value::Dynamic(title) = &settings.title {
            let handle = window.handle(redraw_status.clone());
            title
                .for_each_cloned(move |title| {
                    handle.inner.send(WindowCommand::SetTitle(title));
                })
                .persist();
        }

        let app = settings.app.clone();
        let fonts = Self::load_fonts(
            &mut settings,
            app.cushy().fonts.clone(),
            graphics.font_system().db_mut(),
        );

        let dpi_scale = Dynamic::new(graphics.dpi_scale());
        settings.inner_position.set(window.inner_position());
        settings.outer_position.set(window.outer_position());

        let theme_mode = match settings.theme_mode.take() {
            Some(Value::Dynamic(dynamic)) => {
                dynamic.set(window.theme().into());
                Value::Dynamic(dynamic)
            }
            Some(Value::Constant(mode)) => Value::Constant(mode),
            None => Value::dynamic(window.theme().into()),
        };

        let tree = Tree::default();
        let root = tree.push_boxed(behavior.make_root(), None);

        let theme = settings.theme.unwrap_or_default();
        let (current_theme, theme) = match theme {
            Value::Constant(theme) => (theme, None),
            Value::Dynamic(dynamic) => (dynamic.get(), Some(dynamic.into_reader())),
        };

        if let Some(on_open) = settings.on_open {
            let handle = window.handle(redraw_status.clone());
            on_open.invoke(handle);
        }

        let mut this = Self {
            behavior,
            root,
            tree,
            contents: Drawing::default(),
            cursor: CursorState {
                location: None,
                widget: None,
            },
            mouse_buttons: AHashMap::default(),
            redraw_status,
            initial_frame: true,
            occluded: settings.occluded,
            focused: settings.focused,
            inner_size: Tracked::from(settings.inner_size).ignoring_first(),
            keyboard_activated: None,
            min_inner_size: None,
            max_inner_size: None,
            resize_to_fit: settings.resize_to_fit,
            current_theme,
            theme,
            theme_mode,
            transparent: settings.transparent,
            fonts,
            app,
            on_closed: settings.on_closed,
            vsync: settings.vsync,
            close_requested: settings.close_requested,
            dpi_scale,
            zoom: Tracked::from(settings.zoom),
            content_protected: Tracked::from(settings.content_protected).ignoring_first(),
            cursor_hittest: Tracked::from(settings.cursor_hittest),
            cursor_visible: Tracked::from(settings.cursor_visible),
            cursor_position: Tracked::from(settings.cursor_position),
            window_level: Tracked::from(settings.window_level).ignoring_first(),
            decorated: Tracked::from(settings.decorated).ignoring_first(),
            maximized: Tracked::from(settings.maximized),
            minimized: Tracked::from(settings.minimized),
            resizable: Tracked::from(settings.resizable).ignoring_first(),
            resize_increments: Tracked::from(settings.resize_increments),
            visible: Tracked::from(settings.visible).ignoring_first(),
            outer_size: settings.outer_size,
            inner_position: settings.inner_position,
            outer_position: Tracked::from(settings.outer_position).ignoring_first(),
            window_icon: Tracked::from(settings.window_icon),
            modifiers: settings.modifiers,
            enabled_buttons: Tracked::from(settings.enabled_buttons).ignoring_first(),
            fullscreen: Tracked::from(settings.fullscreen).ignoring_first(),
            shortcuts: settings.shortcuts,
            on_file_drop: settings.on_file_drop,
            disabled_resize_automatically: false,
        };

        this.synchronize_platform_window(&mut window);

        // Perform an initial layout.
        this.prepare(window, graphics);

        this
    }

    fn new_frame(&mut self, graphics: &mut kludgine::Graphics<'_>) {
        if let Some(theme) = &mut self.theme {
            if theme.has_updated() {
                self.current_theme = theme.get();
                self.root.invalidate();
            }
        }

        self.redraw_status.refresh_received();
        graphics.reset_text_attributes();
        if let Some(zoom) = self.zoom.updated() {
            graphics.set_zoom(*zoom);
            self.redraw_status.invalidate(self.root.id());
        }

        self.tree
            .new_frame(self.redraw_status.invalidations().drain());
    }

    fn prepare<W>(&mut self, mut window: W, graphics: &mut kludgine::Graphics<'_>)
    where
        W: PlatformWindowImplementation,
    {
        let cushy = self.app.cushy().clone();
        let _guard = cushy.enter_runtime();

        self.synchronize_platform_window(&mut window);
        self.new_frame(graphics);

        let resize_to_fit = self.resize_to_fit.get();
        let resizable = *self.resizable.peek() || resize_to_fit;
        let mut window = RunningWindow::new(
            window,
            graphics.id(),
            &self.redraw_status,
            &self.app,
            &self.focused,
            &self.occluded,
            self.inner_size.source(),
            &self.close_requested,
        );
        let root_mode = self.constrain_window_resizing(resizable, &mut window, graphics);

        let fonts_changed = self.fonts.next_frame(graphics.font_system().db_mut());
        if fonts_changed {
            graphics.rebuild_font_system();
        }
        let graphics = self.contents.new_frame(graphics);
        let mut context = GraphicsContext {
            widget: WidgetContext::new(
                self.root.clone(),
                &self.current_theme,
                &mut window,
                &mut self.fonts,
                self.theme_mode.get(),
                &mut self.cursor,
            ),
            gfx: Exclusive::Owned(Graphics::new(graphics)),
        };
        self.theme_mode.redraw_when_changed(&context);
        self.inner_size.invalidate_when_changed(&context);
        self.resize_to_fit.invalidate_when_changed(&context);
        let mut layout_context = LayoutContext::new(&mut context);
        let window_size = layout_context.gfx.size();

        if !self.transparent {
            let background_color = layout_context.theme().surface.color;
            layout_context.graphics.gfx.fill(background_color);
        }

        let layout_size =
            layout_context.layout(if matches!(root_mode, RootMode::Expand | RootMode::Align) {
                window_size.map(ConstraintLimit::Fill)
            } else {
                window_size.map(ConstraintLimit::SizeToFit)
            });
        let actual_size = if root_mode == RootMode::Align {
            window_size.max(layout_size)
        } else {
            layout_size
        };
        let render_size = actual_size.min(window_size);

        self.root.set_layout(Rect::from(render_size.into_signed()));

        if self.initial_frame {
            self.initial_frame = false;
            Self::mount_and_focus_root(&self.root, &mut layout_context);
        }

        if render_size.width < window_size.width || render_size.height < window_size.height {
            layout_context
                .clipped_to(Rect::from(render_size.into_signed()))
                .redraw();
        } else {
            layout_context.redraw();
        }

        let resizable = resizable
            && !Self::enforce_fixed_size(
                self.max_inner_size,
                self.max_inner_size,
                &self.resizable,
                &mut self.disabled_resize_automatically,
                &mut layout_context,
            );

        let new_size = if let Some(new_size) = self.inner_size.updated() {
            layout_context.request_inner_size(*new_size)
        } else if actual_size != window_size && !resizable {
            let mut new_size = actual_size;
            if let Some(min_size) = self.min_inner_size {
                new_size = new_size.max(min_size);
            }
            if let Some(max_size) = self.max_inner_size {
                new_size = new_size.min(max_size);
            }
            layout_context.request_inner_size(new_size)
        } else if resize_to_fit && window_size != layout_size {
            layout_context.request_inner_size(layout_size)
        } else {
            None
        };

        if let Some(new_size) = new_size {
            self.inner_size.set_and_read(new_size);
            self.outer_size.set(layout_context.window().outer_size());
            self.root.invalidate();
        }

        layout_context.as_event_context().update_hovered_widget();
    }

    fn mount_and_focus_root(root: &MountedWidget, context: &mut LayoutContext<'_, '_, '_, '_>) {
        root.lock()
            .as_widget()
            .mounted(&mut context.as_event_context());
        context.focus();
        context.as_event_context().apply_pending_state();
    }

    fn enforce_fixed_size(
        min_inner_size: Option<Size<UPx>>,
        max_inner_size: Option<Size<UPx>>,
        resizable: &Tracked<Value<bool>>,
        disabled_resize_automatically: &mut bool,
        context: &mut LayoutContext<'_, '_, '_, '_>,
    ) -> bool {
        let fixed_size = max_inner_size.is_some()
            && min_inner_size.is_some()
            && max_inner_size == min_inner_size;
        if fixed_size && *resizable.peek() && !*disabled_resize_automatically {
            *disabled_resize_automatically = true;
            if let Some(winit) = context.window().winit() {
                winit.set_resizable(false);
            }
        } else if !fixed_size && *resizable.peek() && *disabled_resize_automatically {
            *disabled_resize_automatically = false;
            if let Some(winit) = context.window().winit() {
                winit.set_resizable(true);
            }
        }
        fixed_size
    }

    fn close_requested<W>(&mut self, window: W, kludgine: &mut Kludgine) -> bool
    where
        W: PlatformWindowImplementation,
    {
        let cushy = self.app.cushy().clone();
        let _guard = cushy.enter_runtime();
        let mut window = RunningWindow::new(
            window,
            kludgine.id(),
            &self.redraw_status,
            &self.app,
            &self.focused,
            &self.occluded,
            self.inner_size.source(),
            &self.close_requested,
        );
        if self.behavior.close_requested(&mut window) {
            window.close();
            true
        } else {
            false
        }
    }

    fn resized<W>(&mut self, new_size: Size<UPx>, window: &W)
    where
        W: PlatformWindowImplementation,
    {
        self.inner_size.set_and_read(new_size);
        self.outer_size.set(window.outer_size());
        self.update_ized(window);
        self.root.invalidate();
    }

    fn moved(&mut self, new_inner_position: Point<Px>, new_outer_position: Point<Px>) {
        self.outer_position.set_and_read(new_outer_position);
        self.inner_position.set(new_inner_position);
    }

    fn update_ized<W>(&mut self, window: &W)
    where
        W: PlatformWindowImplementation,
    {
        if let Some(winit) = window.winit() {
            // TODO should these be supported outside of winit? Put in a feature
            // request if you read this and need them.
            self.maximized.set_and_read(winit.is_maximized());
            if let Some(minimized) = winit.is_minimized() {
                self.minimized.set_and_read(minimized);
            }
            self.decorated.set_and_read(winit.is_decorated());
        }
    }

    fn synchronize_platform_window<W>(&mut self, window: &mut W)
    where
        W: PlatformWindowImplementation,
    {
        macro_rules! when_updated {
            ($prop:ident, $handle:ident, $block:expr) => {
                self.$prop.inner_sync_when_changed($handle.clone());
                if let Some($prop) = self.$prop.updated() {
                    $block
                }
            };
        }
        self.redraw_status.sync_received();
        self.update_ized(window);
        if let Some(winit) = window.winit() {
            let mut redraw = false;
            let handle = window.handle(self.redraw_status.clone());

            when_updated!(outer_position, handle, {
                winit.set_outer_position(PhysicalPosition::<i32>::from(*outer_position));
            });
            when_updated!(content_protected, handle, {
                winit.set_content_protected(*content_protected);
            });
            when_updated!(cursor_hittest, handle, {
                let _ = winit.set_cursor_hittest(*cursor_hittest);
            });
            when_updated!(cursor_visible, handle, {
                winit.set_cursor_visible(*cursor_visible);
            });
            when_updated!(window_level, handle, {
                winit.set_window_level(*window_level);
            });
            when_updated!(decorated, handle, {
                winit.set_decorations(*decorated);
            });
            when_updated!(resize_increments, handle, {
                let increments: Option<PhysicalSize<f32>> =
                    if resize_increments.width > 0 || resize_increments.height > 0 {
                        Some(PhysicalSize::new(
                            resize_increments.width.into_float(),
                            resize_increments.height.into_float(),
                        ))
                    } else {
                        None
                    };
                winit.set_resize_increments(increments);
            });
            when_updated!(visible, handle, {
                winit.set_visible(*visible);
            });
            when_updated!(resizable, handle, {
                winit.set_resizable(*resizable);
                println!("Setting resizable {resizable}");
                redraw = true;
            });
            when_updated!(window_icon, handle, {
                let icon = window_icon.as_ref().map(|icon| {
                    Icon::from_rgba(icon.as_raw().clone(), icon.width(), icon.height())
                        .expect("valid image")
                });
                winit.set_window_icon(icon);
            });
            when_updated!(enabled_buttons, handle, {
                winit.set_enabled_buttons(*enabled_buttons);
            });
            when_updated!(fullscreen, handle, {
                winit.set_fullscreen(fullscreen.clone());
            });

            if redraw {
                window.set_needs_redraw();
            }
        }
    }

    pub fn set_focused(&mut self, focused: bool) {
        self.focused.set(focused);
    }

    pub fn set_occluded<W>(&mut self, window: &W, occluded: bool)
    where
        W: PlatformWindowImplementation,
    {
        self.occluded.set(occluded);
        self.update_ized(window);
    }

    pub fn keyboard_input<W>(
        &mut self,
        window: W,
        kludgine: &mut Kludgine,
        device_id: DeviceId,
        input: KeyEvent,
        is_synthetic: bool,
    ) -> EventHandling
    where
        W: PlatformWindowImplementation,
    {
        let cushy = self.app.cushy().clone();
        let _guard = cushy.enter_runtime();
        let mut window = RunningWindow::new(
            window,
            kludgine.id(),
            &self.redraw_status,
            &self.app,
            &self.focused,
            &self.occluded,
            self.inner_size.source(),
            &self.close_requested,
        );
        let target = self.tree.focused_widget().unwrap_or(self.root.node_id);
        let Some(target) = self.tree.widget_from_node(target) else {
            return IGNORED;
        };
        let mut target = EventContext::new(
            WidgetContext::new(
                target,
                &self.current_theme,
                &mut window,
                &mut self.fonts,
                self.theme_mode.get(),
                &mut self.cursor,
            ),
            kludgine,
        );

        if recursively_handle_event(&mut target, |widget| {
            widget.keyboard_input(device_id, input.clone(), is_synthetic)
        })
        .is_some()
        {
            return HANDLED;
        }
        if self
            .shortcuts
            .map(|shortcuts| shortcuts.input(input.clone()))
            .is_break()
        {
            return HANDLED;
        }

        drop(target);

        self.handle_window_keyboard_input(&mut window, kludgine, input)
    }

    pub fn mouse_wheel<W>(
        &mut self,
        window: W,
        kludgine: &mut Kludgine,
        device_id: DeviceId,
        delta: MouseScrollDelta,
        phase: TouchPhase,
    ) -> EventHandling
    where
        W: PlatformWindowImplementation,
    {
        let cushy = self.app.cushy().clone();
        let _guard = cushy.enter_runtime();
        let mut window = RunningWindow::new(
            window,
            kludgine.id(),
            &self.redraw_status,
            &self.app,
            &self.focused,
            &self.occluded,
            self.inner_size.source(),
            &self.close_requested,
        );
        let widget = self
            .tree
            .hovered_widget()
            .and_then(|hovered| self.tree.widget_from_node(hovered))
            .unwrap_or_else(|| self.tree.widget(self.root.id()).expect("missing widget"));

        let mut widget = EventContext::new(
            WidgetContext::new(
                widget,
                &self.current_theme,
                &mut window,
                &mut self.fonts,
                self.theme_mode.get(),
                &mut self.cursor,
            ),
            kludgine,
        );
        if recursively_handle_event(&mut widget, |widget| {
            widget.mouse_wheel(device_id, delta, phase)
        })
        .is_some()
        {
            HANDLED
        } else {
            IGNORED
        }
    }

    fn ime<W>(&mut self, window: W, kludgine: &mut Kludgine, ime: &Ime) -> EventHandling
    where
        W: PlatformWindowImplementation,
    {
        let cushy = self.app.cushy().clone();
        let _guard = cushy.enter_runtime();
        let mut window = RunningWindow::new(
            window,
            kludgine.id(),
            &self.redraw_status,
            &self.app,
            &self.focused,
            &self.occluded,
            self.inner_size.source(),
            &self.close_requested,
        );
        let widget = self
            .tree
            .focused_widget()
            .and_then(|hovered| self.tree.widget_from_node(hovered))
            .unwrap_or_else(|| self.tree.widget(self.root.id()).expect("missing widget"));
        let mut target = EventContext::new(
            WidgetContext::new(
                widget,
                &self.current_theme,
                &mut window,
                &mut self.fonts,
                self.theme_mode.get(),
                &mut self.cursor,
            ),
            kludgine,
        );

        if recursively_handle_event(&mut target, |widget| widget.ime(ime.clone())).is_some() {
            HANDLED
        } else {
            IGNORED
        }
    }

    fn cursor_moved<W>(
        &mut self,
        window: W,
        kludgine: &mut Kludgine,
        device_id: DeviceId,
        position: impl Into<Point<Px>>,
    ) where
        W: PlatformWindowImplementation,
    {
        let cushy = self.app.cushy().clone();
        let _guard = cushy.enter_runtime();
        let mut window = RunningWindow::new(
            window,
            kludgine.id(),
            &self.redraw_status,
            &self.app,
            &self.focused,
            &self.occluded,
            self.inner_size.source(),
            &self.close_requested,
        );

        let location = position.into();
        self.cursor.location = Some(location);
        self.cursor_position.set_and_read(location);

        EventContext::new(
            WidgetContext::new(
                self.root.clone(),
                &self.current_theme,
                &mut window,
                &mut self.fonts,
                self.theme_mode.get(),
                &mut self.cursor,
            ),
            kludgine,
        )
        .update_hovered_widget();

        if let Some(state) = self.mouse_buttons.get(&device_id) {
            // Mouse Drag
            for (button, handler) in state {
                let Some(handler) = self.tree.widget(*handler) else {
                    continue;
                };
                let mut context = EventContext::new(
                    WidgetContext::new(
                        handler.clone(),
                        &self.current_theme,
                        &mut window,
                        &mut self.fonts,
                        self.theme_mode.get(),
                        &mut self.cursor,
                    ),
                    kludgine,
                );
                let Some(last_rendered_at) = context.last_layout() else {
                    continue;
                };
                context.mouse_drag(location - last_rendered_at.origin, device_id, *button);
            }
        }
    }

    fn cursor_left<W>(&mut self, window: W, kludgine: &mut Kludgine)
    where
        W: PlatformWindowImplementation,
    {
        let cushy = self.app.cushy().clone();
        let _guard = cushy.enter_runtime();
        self.cursor.location = None;
        self.cursor_position
            .set_and_read(Point::squared(Px::new(-1)));
        if self.cursor.widget.take().is_some() {
            let mut window = RunningWindow::new(
                window,
                kludgine.id(),
                &self.redraw_status,
                &self.app,
                &self.focused,
                &self.occluded,
                self.inner_size.source(),
                &self.close_requested,
            );

            let mut context = EventContext::new(
                WidgetContext::new(
                    self.root.clone(),
                    &self.current_theme,
                    &mut window,
                    &mut self.fonts,
                    self.theme_mode.get(),
                    &mut self.cursor,
                ),
                kludgine,
            );
            context.clear_hover();
        }
    }

    fn mouse_input<W>(
        &mut self,
        window: W,
        kludgine: &mut Kludgine,
        device_id: DeviceId,
        state: ElementState,
        button: MouseButton,
    ) -> EventHandling
    where
        W: PlatformWindowImplementation,
    {
        let cushy = self.app.cushy().clone();
        let _guard = cushy.enter_runtime();
        let mut window = RunningWindow::new(
            window,
            kludgine.id(),
            &self.redraw_status,
            &self.app,
            &self.focused,
            &self.occluded,
            self.inner_size.source(),
            &self.close_requested,
        );
        match state {
            ElementState::Pressed => {
                if let (ElementState::Pressed, Some(location), Some(hovered)) = (
                    state,
                    self.cursor.location,
                    self.cursor
                        .widget
                        .as_ref()
                        .and_then(|hover| self.tree.widget(hover.id)),
                ) {
                    if let Some(handler) = recursively_handle_event(
                        &mut EventContext::new(
                            WidgetContext::new(
                                hovered.clone(),
                                &self.current_theme,
                                &mut window,
                                &mut self.fonts,
                                self.theme_mode.get(),
                                &mut self.cursor,
                            ),
                            kludgine,
                        ),
                        |context| {
                            let Some(layout) = context.last_layout() else {
                                return IGNORED;
                            };
                            let relative = location - layout.origin;
                            context.mouse_down(relative, device_id, button)
                        },
                    ) {
                        self.mouse_buttons
                            .entry(device_id)
                            .or_default()
                            .insert(button, handler.id());
                        return HANDLED;
                    }
                } else {
                    EventContext::new(
                        WidgetContext::new(
                            self.root.clone(),
                            &self.current_theme,
                            &mut window,
                            &mut self.fonts,
                            self.theme_mode.get(),
                            &mut self.cursor,
                        ),
                        kludgine,
                    )
                    .clear_focus();
                }
                IGNORED
            }
            ElementState::Released => {
                let Some(device_buttons) = self.mouse_buttons.get_mut(&device_id) else {
                    return IGNORED;
                };
                let Some(handler) = device_buttons.remove(&button) else {
                    return IGNORED;
                };
                if device_buttons.is_empty() {
                    self.mouse_buttons.remove(&device_id);
                }
                let Some(handler) = self.tree.widget(handler) else {
                    return IGNORED;
                };
                let cursor_location = self.cursor.location;
                let mut context = EventContext::new(
                    WidgetContext::new(
                        handler,
                        &self.current_theme,
                        &mut window,
                        &mut self.fonts,
                        self.theme_mode.get(),
                        &mut self.cursor,
                    ),
                    kludgine,
                );

                let relative = if let (Some(last_rendered), Some(location)) =
                    (context.last_layout(), cursor_location)
                {
                    Some(location - last_rendered.origin)
                } else {
                    None
                };

                context.mouse_up(relative, device_id, button);
                HANDLED
            }
        }
    }

    fn handle_drop(
        &mut self,
        drop: DropEvent<PathBuf>,
        window: &kludgine::app::Window<'_, WindowCommand>,
    ) {
        if let Some(on_file_drop) = &mut self.on_file_drop {
            on_file_drop.invoke(FileDrop {
                window: WindowHandle::new(window.handle(), self.redraw_status.clone()),
                drop,
            });
        }
    }
}

#[derive(Clone, Copy, Eq, PartialEq, Debug)]
enum RootMode {
    Fit,
    Expand,
    Align,
}

impl<T> kludgine::app::WindowBehavior<WindowCommand> for OpenWindow<T>
where
    T: WindowBehavior,
{
    type Context = sealed::Context<T::Context>;

    fn pre_initialize(context: &Self::Context, winit: &winit::window::Window) {
        let Some(mut on_init) = context.settings.borrow_mut().on_init.take() else {
            return;
        };
        on_init.0.pre_show(winit);
    }

    fn initialize(
        window: kludgine::app::Window<'_, WindowCommand>,
        graphics: &mut kludgine::Graphics<'_>,
        context: Self::Context,
    ) -> Self {
        context.pending.opened(window.handle());
        let settings = context.settings.borrow();
        let cushy = settings.app.cushy().clone();
        let _guard = cushy.enter_runtime();
        let mut window = RunningWindow::new(
            window,
            graphics.id(),
            &settings.redraw_status,
            &settings.app,
            &settings.focused,
            &settings.occluded,
            &settings.inner_size,
            &settings.close_requested,
        );
        drop(settings);

        let behavior = T::initialize(&mut window, context.user);
        Self::new(
            behavior,
            window.window,
            graphics,
            context.settings.into_inner(),
        )
    }

    fn initialized(
        &mut self,
        window: kludgine::app::Window<'_, WindowCommand>,
        kludgine: &mut Kludgine,
    ) {
        let cushy = self.app.cushy().clone();
        let _guard = cushy.enter_runtime();
        self.focused.set(window.focused());
        self.occluded.set(window.occluded());
        let inner_size = window.inner_size();
        self.resized(inner_size, &window);

        self.behavior.initialized(&mut RunningWindow::new(
            window,
            kludgine.id(),
            &self.redraw_status,
            &self.app,
            &self.focused,
            &self.occluded,
            self.inner_size.source(),
            &self.close_requested,
        ));
    }

    fn prepare(
        &mut self,
        window: kludgine::app::Window<'_, WindowCommand>,
        graphics: &mut kludgine::Graphics<'_>,
    ) {
        self.prepare(window, graphics);
    }

    fn present_mode(&self) -> wgpu::PresentMode {
        if self.vsync {
            wgpu::PresentMode::AutoVsync
        } else {
            wgpu::PresentMode::AutoNoVsync
        }
    }

    fn multisample_count(context: &Self::Context) -> std::num::NonZeroU32 {
        context.settings.borrow().multisample_count
    }

    fn memory_hints(_context: &Self::Context) -> wgpu::MemoryHints {
        wgpu::MemoryHints::MemoryUsage
    }

    fn focus_changed(
        &mut self,
        window: kludgine::app::Window<'_, WindowCommand>,
        _kludgine: &mut Kludgine,
    ) {
        self.set_focused(window.focused());
    }

    fn occlusion_changed(
        &mut self,
        window: kludgine::app::Window<'_, WindowCommand>,
        _kludgine: &mut Kludgine,
    ) {
        self.set_occluded(&window, window.occluded());
    }

    fn render<'pass>(
        &'pass mut self,
        _window: kludgine::app::Window<'_, WindowCommand>,
        graphics: &mut kludgine::RenderingGraphics<'_, 'pass>,
    ) {
        self.contents.render(1., graphics);
    }

    fn initial_window_attributes(context: &Self::Context) -> kludgine::app::WindowAttributes {
        let mut settings = context.settings.borrow_mut();
        let mut attrs = settings.attributes.take().expect("called more than once");
        if let Some(Value::Constant(theme_mode)) = &settings.theme_mode {
            attrs.preferred_theme = Some((*theme_mode).into());
        }
        attrs.title = settings.title.get();
        if attrs.inner_size.is_none() {
            let dynamic_inner = settings.inner_size.get();
            if !dynamic_inner.is_zero() {
                attrs.inner_size = Some(winit::dpi::Size::Physical(dynamic_inner.into()));
            }
        }
        attrs
    }

    fn close_requested(
        &mut self,
        window: kludgine::app::Window<'_, WindowCommand>,
        kludgine: &mut Kludgine,
    ) -> bool {
        let cushy = self.app.cushy().clone();
        let _guard = cushy.enter_runtime();
        Self::request_close(
            &mut self.behavior,
            &mut RunningWindow::new(
                window,
                kludgine.id(),
                &self.redraw_status,
                &self.app,
                &self.focused,
                &self.occluded,
                self.inner_size.source(),
                &self.close_requested,
            ),
        )
    }

    // fn power_preference() -> wgpu::PowerPreference {
    //     wgpu::PowerPreference::default()
    // }

    // fn limits(adapter_limits: wgpu::Limits) -> wgpu::Limits {
    //     wgpu::Limits::downlevel_webgl2_defaults().using_resolution(adapter_limits)
    // }

    fn clear_color(&self) -> Option<kludgine::Color> {
        Some(if self.transparent {
            kludgine::Color::CLEAR_BLACK
        } else {
            kludgine::Color::BLACK
        })
    }

    fn composite_alpha_mode(&self, supported_modes: &[CompositeAlphaMode]) -> CompositeAlphaMode {
        if self.transparent && supported_modes.contains(&CompositeAlphaMode::PreMultiplied) {
            CompositeAlphaMode::PreMultiplied
        } else {
            CompositeAlphaMode::Auto
        }
    }

    // fn focus_changed(&mut self, window: kludgine::app::Window<'_, ()>) {}

    // fn occlusion_changed(&mut self, window: kludgine::app::Window<'_, ()>) {}

    fn scale_factor_changed(
        &mut self,
        mut window: kludgine::app::Window<'_, WindowCommand>,
        kludgine: &mut Kludgine,
    ) {
        self.dpi_scale.set(kludgine.dpi_scale());
        window.set_needs_redraw();
    }

    fn resized(
        &mut self,
        window: kludgine::app::Window<'_, WindowCommand>,
        _kludgine: &mut Kludgine,
    ) {
        self.resized(window.inner_size(), &window);
    }

    fn moved(
        &mut self,
        window: kludgine::app::Window<'_, WindowCommand>,
        _kludgine: &mut Kludgine,
    ) {
        self.moved(window.inner_position(), window.outer_position());
    }

    fn dropped_file(
        &mut self,
        window: kludgine::app::Window<'_, WindowCommand>,
        _kludgine: &mut Kludgine,
        path: PathBuf,
    ) {
        self.handle_drop(DropEvent::Dropped(path), &window);
    }

    fn hovered_file(
        &mut self,
        window: kludgine::app::Window<'_, WindowCommand>,
        _kludgine: &mut Kludgine,
        path: PathBuf,
    ) {
        self.handle_drop(DropEvent::Hover(path), &window);
    }

    fn hovered_file_cancelled(
        &mut self,
        window: kludgine::app::Window<'_, WindowCommand>,
        _kludgine: &mut Kludgine,
    ) {
        self.handle_drop(DropEvent::Cancelled, &window);
    }

    // fn received_character(&mut self, window: kludgine::app::Window<'_, ()>, char: char) {}

    fn keyboard_input(
        &mut self,
        window: kludgine::app::Window<'_, WindowCommand>,
        kludgine: &mut Kludgine,
        device_id: winit::event::DeviceId,
        input: winit::event::KeyEvent,
        is_synthetic: bool,
    ) {
        let event = KeyEvent::from_winit(input, window.modifiers());
        self.keyboard_input(window, kludgine, device_id.into(), event, is_synthetic);
    }

    fn mouse_wheel(
        &mut self,
        window: kludgine::app::Window<'_, WindowCommand>,
        kludgine: &mut Kludgine,
        device_id: winit::event::DeviceId,
        delta: MouseScrollDelta,
        phase: TouchPhase,
    ) {
        self.mouse_wheel(window, kludgine, device_id.into(), delta, phase);
    }

    fn modifiers_changed(
        &mut self,
        window: kludgine::app::Window<'_, WindowCommand>,
        _kludgine: &mut Kludgine,
    ) {
        self.modifiers.set(window.modifiers());
    }

    fn ime(
        &mut self,
        window: kludgine::app::Window<'_, WindowCommand>,
        kludgine: &mut Kludgine,
        ime: Ime,
    ) {
        self.ime(window, kludgine, &ime);
    }

    fn cursor_moved(
        &mut self,
        window: kludgine::app::Window<'_, WindowCommand>,
        kludgine: &mut Kludgine,
        device_id: winit::event::DeviceId,
        position: PhysicalPosition<f64>,
    ) {
        self.cursor_moved(window, kludgine, device_id.into(), position);
    }

    fn cursor_left(
        &mut self,
        window: kludgine::app::Window<'_, WindowCommand>,
        kludgine: &mut Kludgine,
        _device_id: winit::event::DeviceId,
    ) {
        self.cursor_left(window, kludgine);
    }

    fn mouse_input(
        &mut self,
        window: kludgine::app::Window<'_, WindowCommand>,
        kludgine: &mut Kludgine,
        device_id: winit::event::DeviceId,
        state: ElementState,
        button: MouseButton,
    ) {
        self.mouse_input(window, kludgine, device_id.into(), state, button);
    }

    fn theme_changed(
        &mut self,
        window: kludgine::app::Window<'_, WindowCommand>,
        _kludgine: &mut Kludgine,
    ) {
        if let Value::Dynamic(theme_mode) = &self.theme_mode {
            theme_mode.set(window.theme().into());
        }
    }

    fn event(
        &mut self,
        mut window: kludgine::app::Window<'_, WindowCommand>,
        kludgine: &mut Kludgine,
        event: WindowCommand,
    ) {
        match event {
            WindowCommand::Redraw => {
                window.set_needs_redraw();
            }
            WindowCommand::Sync => {
                self.synchronize_platform_window(&mut window);
            }
            WindowCommand::RequestClose => {
                let mut window = RunningWindow::new(
                    window,
                    kludgine.id(),
                    &self.redraw_status,
                    &self.app,
                    &self.focused,
                    &self.occluded,
                    self.inner_size.source(),
                    &self.close_requested,
                );
                if self.behavior.close_requested(&mut window) {
                    window.close();
                }
            }
            WindowCommand::SetTitle(new_title) => {
                window.set_title(&new_title);
            }
            WindowCommand::ResetDeadKeys => {
                window.winit().reset_dead_keys();
            }
            WindowCommand::RequestUserAttention(request_type) => {
                window.winit().request_user_attention(request_type);
            }
            WindowCommand::Focus => {
                window.winit().focus_window();
            }
            WindowCommand::Ize(ize) => {
                let (minimize, maximize) = match ize {
                    Some(Ize::Maximize) => (false, true),
                    Some(Ize::Minimize) => (true, false),
                    None => (false, false),
                };
                if window
                    .winit()
                    .is_minimized()
                    .map_or(true, |minimized| minimized != minimize)
                {
                    window.winit().set_minimized(minimize);
                }
                if window.winit().is_maximized() != maximize {
                    window.winit().set_maximized(maximize);
                }
            }
            WindowCommand::Execute(func) => {
                let mut window = RunningWindow::new(
                    window,
                    kludgine.id(),
                    &self.redraw_status,
                    &self.app,
                    &self.focused,
                    &self.occluded,
                    self.inner_size.source(),
                    &self.close_requested,
                );
                let mut context = EventContext::new(
                    WidgetContext::new(
                        self.root.clone(),
                        &self.current_theme,
                        &mut window,
                        &mut self.fonts,
                        self.theme_mode.get(),
                        &mut self.cursor,
                    ),
                    kludgine,
                );
                func.execute(&mut context);
            }
        }
    }

    // fn dropped_file(
    //     &mut self,
    //     window: kludgine::app::Window<'_, WindowCommand>,
    //     kludgine: &mut Kludgine,
    //     path: std::path::PathBuf,
    // ) {
    // }

    // fn hovered_file(
    //     &mut self,
    //     window: kludgine::app::Window<'_, WindowCommand>,
    //     kludgine: &mut Kludgine,
    //     path: std::path::PathBuf,
    // ) {
    // }

    // fn hovered_file_cancelled(
    //     &mut self,
    //     window: kludgine::app::Window<'_, WindowCommand>,
    //     kludgine: &mut Kludgine,
    // ) {
    // }

    // fn received_character(
    //     &mut self,
    //     window: kludgine::app::Window<'_, WindowCommand>,
    //     kludgine: &mut Kludgine,
    //     char: char,
    // ) {
    // }

    // fn modifiers_changed(
    //     &mut self,
    //     window: kludgine::app::Window<'_, WindowCommand>,
    //     kludgine: &mut Kludgine,
    // ) {
    // }
}

impl<Behavior> Drop for OpenWindow<Behavior> {
    fn drop(&mut self) {
        if let Some(on_closed) = self.on_closed.take() {
            on_closed.invoke(());
        }
    }
}

fn recursively_handle_event(
    context: &mut EventContext<'_>,
    mut each_widget: impl FnMut(&mut EventContext<'_>) -> EventHandling,
) -> Option<MountedWidget> {
    match each_widget(context) {
        HANDLED => Some(context.widget().clone()),
        IGNORED => context.parent().and_then(|parent| {
            recursively_handle_event(&mut context.for_other(&parent), each_widget)
        }),
    }
}

#[derive(Default)]
pub(crate) struct CursorState {
    pub(crate) location: Option<Point<Px>>,
    pub(crate) widget: Option<WidgetCursorState>,
}

#[derive(Eq, PartialEq)]
pub(crate) struct WidgetCursorState {
    pub(crate) id: WidgetId,
    pub(crate) last_hovered: Point<Px>,
}

pub(crate) mod sealed {
    use std::cell::RefCell;
    use std::fmt::Debug;
    use std::num::NonZeroU32;

    use figures::units::{Px, UPx};
    use figures::{Fraction, Point, Size};
    use image::{DynamicImage, RgbaImage};
    use kludgine::app::winit;
    use kludgine::app::winit::event::Modifiers;
    use kludgine::app::winit::window::{Fullscreen, UserAttentionType, WindowButtons, WindowLevel};
    use kludgine::Color;

    use crate::context::sealed::InvalidationStatus;
    use crate::context::EventContext;
    use crate::fonts::FontCollection;
    use crate::styles::{FontFamilyList, ThemePair};
    use crate::value::{Dynamic, Value};
    use crate::widget::{Callback, OnceCallback, SharedCallback};
    use crate::widgets::shortcuts::ShortcutMap;
    use crate::window::{FileDrop, PendingWindow, ThemeMode, WindowAttributes, WindowHandle};
    use crate::App;

    pub struct Context<C> {
        pub user: C,
        pub pending: PendingWindow,
        pub settings: RefCell<WindowSettings>,
    }

    pub struct WindowSettings {
        pub app: App,
        pub redraw_status: InvalidationStatus,
        pub title: Value<String>,
        pub attributes: Option<WindowAttributes>,
        pub occluded: Dynamic<bool>,
        pub focused: Dynamic<bool>,
        pub inner_size: Dynamic<Size<UPx>>,
        pub zoom: Dynamic<Fraction>,
        pub theme: Option<Value<ThemePair>>,
        pub theme_mode: Option<Value<ThemeMode>>,
        pub transparent: bool,
        pub serif_font_family: FontFamilyList,
        pub sans_serif_font_family: FontFamilyList,
        pub fantasy_font_family: FontFamilyList,
        pub monospace_font_family: FontFamilyList,
        pub cursive_font_family: FontFamilyList,
        pub font_data_to_load: FontCollection,
        pub on_open: Option<OnceCallback<WindowHandle>>,
        pub on_init: Option<PreShowCallback>,
        pub on_closed: Option<OnceCallback>,
        pub vsync: bool,
        pub multisample_count: NonZeroU32,
        pub resize_to_fit: Value<bool>,
        pub close_requested: Option<SharedCallback<(), bool>>,
        pub content_protected: Value<bool>,
        pub cursor_hittest: Value<bool>,
        pub cursor_visible: Value<bool>,
        pub cursor_position: Dynamic<Point<Px>>,
        pub window_level: Value<WindowLevel>,
        pub decorated: Value<bool>,
        pub maximized: Dynamic<bool>,
        pub minimized: Dynamic<bool>,
        pub resizable: Value<bool>,
        pub resize_increments: Value<Size<UPx>>,
        pub visible: Dynamic<bool>,
        pub inner_position: Dynamic<Point<Px>>,
        pub outer_position: Dynamic<Point<Px>>,
        pub outer_size: Dynamic<Size<UPx>>,
        pub window_icon: Value<Option<RgbaImage>>,
        pub modifiers: Dynamic<Modifiers>,
        pub enabled_buttons: Value<WindowButtons>,
        pub fullscreen: Value<Option<Fullscreen>>,
        pub shortcuts: Value<ShortcutMap>,
        pub on_file_drop: Option<Callback<FileDrop>>,
    }

    pub struct WindowExecute(Box<dyn ExecuteFunc>);

    impl WindowExecute {
        pub fn new<F>(func: F) -> Self
        where
            F: FnOnce(&mut EventContext<'_>) + Send + 'static,
        {
            Self(Box::new(Some(func)))
        }

        pub fn execute(mut self, context: &mut EventContext<'_>) {
            self.0.execute(context);
        }
    }

    impl Debug for WindowExecute {
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
            f.debug_struct("WindowExecute").finish_non_exhaustive()
        }
    }

    pub trait ExecuteFunc: Send + 'static {
        fn execute(&mut self, context: &mut EventContext<'_>);
    }

    impl<F> ExecuteFunc for Option<F>
    where
        F: FnOnce(&mut EventContext<'_>) + Send + 'static,
    {
        fn execute(&mut self, context: &mut EventContext<'_>) {
            let func = self.take().expect("not executed");
            func(context);
        }
    }

    #[derive(Debug)]
    pub enum WindowCommand {
        Redraw,
        Sync,
        RequestClose,
        ResetDeadKeys,
        RequestUserAttention(Option<UserAttentionType>),
        Focus,
        Ize(Option<Ize>),
        SetTitle(String),
        Execute(WindowExecute),
    }

    #[derive(Debug, Clone)]
    pub enum Ize {
        Maximize,
        Minimize,
    }

    pub trait CaptureFormat {
        const HAS_ALPHA: bool;

        fn convert_rgba(data: &mut Vec<u8>, width: u32, bytes_per_row: u32);
        fn load_image(data: &[u8], size: Size<UPx>) -> DynamicImage;
        fn pixel_color(location: Point<UPx>, data: &[u8], size: Size<UPx>) -> Color;
    }

    pub struct PreShowCallback(pub Box<dyn PreShowFn>);

    pub trait PreShowFn: Send + 'static {
        fn pre_show(&mut self, winit: &winit::window::Window);
    }

    impl<F> PreShowFn for Option<F>
    where
        F: FnOnce(&winit::window::Window) + Send + 'static,
    {
        fn pre_show(&mut self, winit: &winit::window::Window) {
            let Some(this) = self.take() else { return };
            this(winit);
        }
    }
}

/// Controls whether the light or dark theme is applied.
#[derive(Default, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, LinearInterpolate)]
pub enum ThemeMode {
    /// Applies the light theme
    Light,
    /// Applies the dark theme
    #[default]
    Dark,
}

impl ThemeMode {
    /// Returns the opposite mode of `self`.
    #[must_use]
    pub const fn inverse(self) -> Self {
        match self {
            ThemeMode::Light => Self::Dark,
            ThemeMode::Dark => Self::Light,
        }
    }

    /// Updates `self` with its [inverse](Self::inverse).
    pub fn toggle(&mut self) {
        *self = !*self;
    }
}

impl Not for ThemeMode {
    type Output = Self;

    fn not(self) -> Self::Output {
        self.inverse()
    }
}

impl From<window::Theme> for ThemeMode {
    fn from(value: window::Theme) -> Self {
        match value {
            window::Theme::Light => Self::Light,
            window::Theme::Dark => Self::Dark,
        }
    }
}

impl From<ThemeMode> for window::Theme {
    fn from(value: ThemeMode) -> Self {
        match value {
            ThemeMode::Light => Self::Light,
            ThemeMode::Dark => Self::Dark,
        }
    }
}

impl PercentBetween for ThemeMode {
    fn percent_between(&self, min: &Self, max: &Self) -> ZeroToOne {
        if *min == *max || *self == *min {
            ZeroToOne::ZERO
        } else {
            ZeroToOne::ONE
        }
    }
}

impl Ranged for ThemeMode {
    const MAX: Self = Self::Dark;
    const MIN: Self = Self::Light;
}

#[cfg(any(target_os = "macos", target_os = "ios", target_os = "windows"))]
fn default_family(_query: Family<'_>) -> Option<FamilyOwned> {
    // fontdb uses system APIs to determine these defaults.
    None
}

#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "windows")))]
fn default_family(query: Family<'_>) -> Option<FamilyOwned> {
    // fontdb does not yet support configuring itself automatically. We will try
    // to use `fc-match` to query font config. Once this is supported, we can
    // remove this functionality.
    // <https://github.com/RazrFalcon/fontdb/issues/24>
    let query = match query {
        Family::Serif => "serif",
        Family::SansSerif => "sans",
        Family::Cursive => "cursive",
        Family::Fantasy => "fantasy",
        Family::Monospace => "monospace",
        Family::Name(_) => return None,
    };

    std::process::Command::new("fc-match")
        .arg("-f")
        .arg("%{family}")
        .arg(query)
        .output()
        .ok()
        .and_then(|output| String::from_utf8(output.stdout).ok())
        .map(FamilyOwned::Name)
}

/// A handle to an open Cushy window.
#[derive(Debug, Clone)]
pub struct WindowHandle {
    inner: InnerWindowHandle,
    pub(crate) redraw_status: InvalidationStatus,
}

impl WindowHandle {
    pub(crate) fn new(
        kludgine: kludgine::app::WindowHandle<WindowCommand>,
        redraw_status: InvalidationStatus,
    ) -> Self {
        Self {
            inner: InnerWindowHandle::Known(kludgine),
            redraw_status,
        }
    }

    fn pending() -> Self {
        Self {
            inner: InnerWindowHandle::Pending(Arc::default()),
            redraw_status: InvalidationStatus::default(),
        }
    }

    /// Request that the window closes.
    ///
    /// A window may disallow itself from being closed by customizing
    /// [`WindowBehavior::close_requested`].
    pub fn request_close(&self) {
        self.inner.send(sealed::WindowCommand::RequestClose);
    }

    /// Requests that the window redraws.
    pub fn redraw(&self) {
        if self.redraw_status.should_send_refresh() {
            self.inner.send(WindowCommand::Redraw);
        }
    }

    pub(crate) fn sync(&self) {
        if self.redraw_status.should_send_sync() {
            self.inner.send(WindowCommand::Sync);
        }
    }

    /// Marks `widget` as invalidated, and if needed, refreshes the window.
    pub fn invalidate(&self, widget: WidgetId) {
        if self.redraw_status.invalidate(widget) {
            self.redraw();
        }
    }

    /// Executes `func` on the window thread.
    pub fn execute<F>(&self, func: F)
    where
        F: FnOnce(&mut EventContext<'_>) + Send + 'static,
    {
        self.inner
            .send(WindowCommand::Execute(WindowExecute::new(func)));
    }
}

impl Eq for WindowHandle {}

impl PartialEq for WindowHandle {
    fn eq(&self, other: &Self) -> bool {
        self.redraw_status == other.redraw_status
    }
}

impl Hash for WindowHandle {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        self.redraw_status.hash(state);
    }
}

#[derive(Debug, Clone)]
enum InnerWindowHandle {
    Pending(Arc<PendingWindowHandle>),
    Known(kludgine::app::WindowHandle<WindowCommand>),
    Virtual(WindowDynamicState),
}

impl InnerWindowHandle {
    fn send(&self, message: WindowCommand) {
        match self {
            InnerWindowHandle::Pending(pending) => {
                if let Some(handle) = pending.handle.get() {
                    let _result = handle.send(message);
                } else {
                    pending.commands.lock().push(message);
                }
            }
            InnerWindowHandle::Known(handle) => {
                let _result = handle.send(message);
            }
            InnerWindowHandle::Virtual(state) => match message {
                WindowCommand::Redraw => state.redraw_target.set(RedrawTarget::Now),
                WindowCommand::RequestClose => state.close_requested.set(true),
                WindowCommand::SetTitle(title) => state.title.set(title),
                WindowCommand::Execute(_func) => {
                    tracing::error!("ignoring execution of window function on virtual window");
                }
                WindowCommand::ResetDeadKeys
                | WindowCommand::RequestUserAttention(_)
                | WindowCommand::Focus
                | WindowCommand::Ize(_)
                | WindowCommand::Sync => {}
            },
        };
    }
}

/// A [`Window`] that doesn't have its root widget yet.
///
/// [`PendingWindow::handle()`] returns a handle that allows code to interact
/// with a window before it has had its contents initialized. This is useful,
/// for example, for a button's `on_click` to be able to close the window that
/// contains it.
pub struct PendingWindow(WindowHandle);

impl Default for PendingWindow {
    fn default() -> Self {
        Self(WindowHandle::pending())
    }
}

impl PendingWindow {
    /// Returns a [`Window`] using `context` to initialize its contents.
    pub fn with<Behavior>(self, context: Behavior::Context) -> Window<Behavior>
    where
        Behavior: WindowBehavior,
    {
        Window::new_with_pending(context, self)
    }

    /// Returns a [`Window`] containing `root`.
    pub fn with_root(self, root: impl MakeWidget) -> Window {
        Window::new_with_pending(root.make_widget(), self)
    }

    /// Returns a [`Window`] using the default context to initialize its
    /// contents.
    pub fn using<Behavior>(self) -> Window<Behavior>
    where
        Behavior: WindowBehavior,
        Behavior::Context: Default,
    {
        self.with(<Behavior::Context>::default())
    }

    /// Returns a handle for this window.
    #[must_use]
    pub fn handle(&self) -> WindowHandle {
        self.0.clone()
    }

    fn opened(self, handle: kludgine::app::WindowHandle<WindowCommand>) -> WindowHandle {
        let InnerWindowHandle::Pending(pending) = &self.0.inner else {
            unreachable!("always pending")
        };

        let initialized = pending.handle.set(handle.clone());
        assert!(initialized.is_ok());

        for command in pending.commands.lock().drain(..) {
            let _result = handle.send(command);
        }

        WindowHandle::new(handle, self.0.redraw_status.clone())
    }
}

#[derive(Debug, Default)]
struct PendingWindowHandle {
    handle: OnceLock<kludgine::app::WindowHandle<WindowCommand>>,
    commands: Mutex<Vec<WindowCommand>>,
}

/// A collection that stores an instance of `T` per window.
///
/// This is a convenience wrapper around a `HashMap<KludgineId, T>`.
#[derive(Debug, Clone)]
pub struct WindowLocal<T> {
    by_window: AHashMap<KludgineId, T>,
}

impl<T> WindowLocal<T> {
    /// Looks up the entry for this window.
    ///
    /// Internally this API uses [`HashMap::entry`](hash_map::HashMap::entry).
    pub fn entry(&mut self, context: &WidgetContext<'_>) -> hash_map::Entry<'_, KludgineId, T> {
        self.by_window.entry(context.kludgine_id())
    }

    /// Sets `value` as the local value for `context`'s window.
    pub fn set(&mut self, context: &WidgetContext<'_>, value: T) {
        self.by_window.insert(context.kludgine_id(), value);
    }

    /// Looks up the value for this window, returning None if not found.
    ///
    /// Internally this API uses [`HashMap::get`](hash_map::HashMap::get).
    #[must_use]
    pub fn get(&self, context: &WidgetContext<'_>) -> Option<&T> {
        self.by_window.get(&context.kludgine_id())
    }

    /// Looks up an exclusive reference to the value for this window, returning
    /// None if not found.
    ///
    /// Internally this API uses [`HashMap::get`](hash_map::HashMap::get).
    #[must_use]
    pub fn get_mut(&mut self, context: &WidgetContext<'_>) -> Option<&mut T> {
        self.by_window.get_mut(&context.kludgine_id())
    }

    /// Removes any stored value for this window.
    pub fn clear_for(&mut self, context: &WidgetContext<'_>) -> Option<T> {
        self.by_window.remove(&context.kludgine_id())
    }

    /// Returns an iterator over the per-window values stored in this
    /// collection.
    #[must_use]
    pub fn iter(&self) -> hash_map::Iter<'_, KludgineId, T> {
        self.into_iter()
    }
}

impl<T> Default for WindowLocal<T> {
    fn default() -> Self {
        Self {
            by_window: AHashMap::default(),
        }
    }
}

impl<T> IntoIterator for WindowLocal<T> {
    type IntoIter = hash_map::IntoIter<KludgineId, T>;
    type Item = (KludgineId, T);

    fn into_iter(self) -> Self::IntoIter {
        self.by_window.into_iter()
    }
}

impl<'a, T> IntoIterator for &'a WindowLocal<T> {
    type IntoIter = hash_map::Iter<'a, KludgineId, T>;
    type Item = (&'a KludgineId, &'a T);

    fn into_iter(self) -> Self::IntoIter {
        self.by_window.iter()
    }
}

/// The state of a [`VirtualWindow`].
pub struct VirtualState {
    /// State that may be updated outside of the window's event callbacks.
    pub dynamic: WindowDynamicState,
    /// When true, this window should be closed.
    pub closed: bool,
    /// The current keyboard modifers.
    pub modifiers: Modifiers,
    /// The amount of time elapsed since the last redraw call.
    pub elapsed: Duration,
    /// The currently set cursor.
    pub cursor: Cursor,
    /// The inner size of the virtual window.
    pub size: Size<UPx>,
}

impl VirtualState {
    fn new() -> Self {
        Self {
            dynamic: WindowDynamicState::default(),
            closed: false,
            modifiers: Modifiers::default(),
            elapsed: Duration::ZERO,
            cursor: Cursor::default(),
            size: Size::upx(800, 600),
        }
    }
}

/// Window state that is able to be updated outside of event handling,
/// potentially via other threads depending on the application.
#[derive(Clone, Debug, Default)]
pub struct WindowDynamicState {
    /// The target of the next frame to draw.
    pub redraw_target: Dynamic<RedrawTarget>,
    /// When true, the window has been asked to close. To ensure full Cushy
    /// functionality, upon detecting this, [`VirtualWindow::request_close`]
    /// should be invoked.
    pub close_requested: Dynamic<bool>,
    /// The current title of the window.
    pub title: Dynamic<String>,
}

/// A target for the next redraw of a window.
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)]
pub enum RedrawTarget {
    /// The window should not redraw.
    #[default]
    Never,
    /// The window should redraw as soon as possible.
    Now,
    /// The window should try to redraw at the given instant.
    At(Instant),
}

impl PlatformWindowImplementation for &mut VirtualState {
    fn close(&mut self) {
        self.closed = true;
    }

    fn winit(&self) -> Option<&Arc<winit::window::Window>> {
        None
    }

    fn handle(&self, redraw_status: InvalidationStatus) -> WindowHandle {
        WindowHandle {
            inner: InnerWindowHandle::Virtual(self.dynamic.clone()),
            redraw_status,
        }
    }

    fn set_needs_redraw(&mut self) {
        self.dynamic.redraw_target.set(RedrawTarget::Now);
    }

    fn redraw_in(&mut self, duration: Duration) {
        self.redraw_at(Instant::now() + duration);
    }

    fn redraw_at(&mut self, moment: Instant) {
        self.dynamic.redraw_target.map_mut(|mut redraw_at| {
            if match *redraw_at {
                RedrawTarget::At(instant) => moment < instant,
                RedrawTarget::Never => true,
                RedrawTarget::Now => false,
            } {
                *redraw_at = RedrawTarget::At(moment);
            }
        });
    }

    fn modifiers(&self) -> Modifiers {
        self.modifiers
    }

    fn elapsed(&self) -> Duration {
        self.elapsed
    }

    fn set_cursor(&mut self, cursor: Cursor) {
        self.cursor = cursor;
    }

    fn inner_size(&self) -> Size<UPx> {
        self.size
    }

    fn request_inner_size(&mut self, inner_size: Size<UPx>) -> Option<Size<UPx>> {
        self.size = inner_size;
        self.set_needs_redraw();
        Some(inner_size)
    }
}

/// A builder that creates either a [`VirtualWindow`] or a [`CushyWindow`].
pub struct StandaloneWindowBuilder {
    widget: WidgetInstance,
    multisample_count: NonZeroU32,
    initial_size: Size<UPx>,
    scale: f32,
    transparent: bool,
    zoom: Dynamic<Fraction>,
    resize_to_fit: Value<bool>,
}

impl StandaloneWindowBuilder {
    /// Returns a new builder for a standalone window that contains `contents`.
    #[must_use]
    pub fn new(contents: impl MakeWidget) -> Self {
        Self {
            widget: contents.make_widget(),
            multisample_count: NonZeroU32::new(4).assert("not 0"),
            initial_size: Size::upx(800, 600),
            scale: 1.,
            zoom: Dynamic::new(Fraction::ONE),
            transparent: false,
            resize_to_fit: Value::Constant(false),
        }
    }

    /// Sets this window's multi-sample count.
    ///
    /// By default, 4 samples are taken. When 1 sample is used, multisampling is
    /// fully disabled.
    #[must_use]
    pub fn multisample_count(mut self, count: NonZeroU32) -> Self {
        self.multisample_count = count;
        self
    }

    /// Sets the size of the window.
    #[must_use]
    pub fn size<Unit>(mut self, size: Size<Unit>) -> Self
    where
        Unit: Into<UPx>,
    {
        self.initial_size = size.map(Into::into);
        self
    }

    /// Sets the DPI scaling factor of the window.
    #[must_use]
    pub fn scale(mut self, scale: f32) -> Self {
        self.scale = scale;
        self
    }

    /// Sets the window not fill its background before rendering its contents.
    #[must_use]
    pub fn transparent(mut self) -> Self {
        self.transparent = true;
        self
    }

    /// Resizes this window to fit the contents when `resize_to_fit` is true.
    #[must_use]
    pub fn resize_to_fit(mut self, resize_to_fit: impl IntoValue<bool>) -> Self {
        self.resize_to_fit = resize_to_fit.into_value();
        self
    }

    /// Returns the initialized window.
    #[must_use]
    pub fn finish<W>(self, window: W, device: &wgpu::Device, queue: &wgpu::Queue) -> CushyWindow
    where
        W: PlatformWindowImplementation,
    {
        let mut kludgine = Kludgine::new(
            device,
            queue,
            wgpu::TextureFormat::Rgba8UnormSrgb,
            wgpu::MultisampleState {
                count: self.multisample_count.get(),
                ..Default::default()
            },
            self.initial_size,
            self.scale,
        );
        let window = OpenWindow::<WidgetInstance>::new(
            self.widget,
            window,
            &mut kludgine::Graphics::new(&mut kludgine, device, queue),
            sealed::WindowSettings {
                app: App::standalone(),
                redraw_status: InvalidationStatus::default(),
                title: Value::default(),
                attributes: None,
                occluded: Dynamic::default(),
                focused: Dynamic::default(),
                inner_size: Dynamic::default(),
                theme: None,
                theme_mode: None,
                transparent: self.transparent,
                serif_font_family: FontFamilyList::default(),
                sans_serif_font_family: FontFamilyList::default(),
                fantasy_font_family: FontFamilyList::default(),
                monospace_font_family: FontFamilyList::default(),
                cursive_font_family: FontFamilyList::default(),
                font_data_to_load: FontCollection::default(),
                on_open: None,
                on_closed: None,
                vsync: false,
                multisample_count: self.multisample_count,
                close_requested: None,
                zoom: self.zoom,
                resize_to_fit: self.resize_to_fit,
                content_protected: Value::Constant(false),
                cursor_hittest: Value::Constant(true),
                cursor_visible: Value::Constant(true),
                cursor_position: Dynamic::default(),
                window_level: Value::default(),
                decorated: Value::Constant(true),
                maximized: Dynamic::new(false),
                minimized: Dynamic::new(false),
                resizable: Value::Constant(true),
                resize_increments: Value::default(),
                visible: Dynamic::new(true),
                inner_position: Dynamic::default(),
                outer_position: Dynamic::default(),
                outer_size: Dynamic::default(),
                window_icon: Value::Constant(None),
                modifiers: Dynamic::default(),
                enabled_buttons: Value::dynamic(WindowButtons::all()),
                fullscreen: Value::default(),
                shortcuts: Value::default(),
                on_init: None,
                on_file_drop: None,
            },
        );

        CushyWindow { window, kludgine }
    }

    /// Returns an initialized [`VirtualWindow`].
    #[must_use]
    pub fn finish_virtual(self, device: &wgpu::Device, queue: &wgpu::Queue) -> VirtualWindow {
        let mut state = VirtualState::new();
        state.size = self.initial_size;
        let mut cushy = self.finish(&mut state, device, queue);
        cushy.set_focused(true);

        VirtualWindow {
            cushy,
            state,
            last_rendered_at: None,
        }
    }
}

/// A standalone Cushy window.
///
/// This type allows rendering Cushy applications directly into any wgpu
/// application.
pub struct CushyWindow {
    window: OpenWindow<WidgetInstance>,
    kludgine: Kludgine,
}

impl CushyWindow {
    /// Prepares all necessary resources and operations necessary to render the
    /// next frame.
    pub fn prepare<W>(&mut self, window: W, device: &wgpu::Device, queue: &wgpu::Queue)
    where
        W: PlatformWindowImplementation,
    {
        self.window.prepare(
            window,
            &mut kludgine::Graphics::new(&mut self.kludgine, device, queue),
        );
    }

    /// Renders this window in a wgpu render pass created from `pass`.
    ///
    /// Returns the submission index of the last command submission, if any
    /// commands were submitted.
    pub fn render(
        &mut self,
        pass: &wgpu::RenderPassDescriptor<'_>,
        device: &wgpu::Device,
        queue: &wgpu::Queue,
    ) -> Option<wgpu::SubmissionIndex> {
        self.render_with(pass, device, queue, None)
    }

    /// Renders this window in a wgpu render pass created from `pass`.
    ///
    /// Returns the submission index of the last command submission, if any
    /// commands were submitted.
    pub fn render_with(
        &mut self,
        pass: &wgpu::RenderPassDescriptor<'_>,
        device: &wgpu::Device,
        queue: &wgpu::Queue,
        additional_drawing: Option<&Drawing>,
    ) -> Option<wgpu::SubmissionIndex> {
        let mut frame = self.kludgine.next_frame();
        let mut gfx = frame.render(pass, device, queue);
        self.window.contents.render(1., &mut gfx);
        if let Some(additional) = additional_drawing {
            additional.render(1., &mut gfx);
        }
        drop(gfx);
        frame.submit(queue)
    }

    /// Renders this window into `texture` after performing `load_op`.
    pub fn render_into(
        &mut self,
        texture: &kludgine::Texture,
        load_op: wgpu::LoadOp<Color>,
        device: &wgpu::Device,
        queue: &wgpu::Queue,
    ) -> Option<wgpu::SubmissionIndex> {
        let mut frame = self.kludgine.next_frame();
        let mut gfx = frame.render_into(texture, load_op, device, queue);
        self.window.contents.render(1., &mut gfx);
        drop(gfx);
        frame.submit(queue)
    }

    /// Returns a new [`kludgine::Graphics`] context for this window.
    #[must_use]
    pub fn graphics<'gfx>(
        &'gfx mut self,
        device: &'gfx wgpu::Device,
        queue: &'gfx wgpu::Queue,
    ) -> kludgine::Graphics<'gfx> {
        kludgine::Graphics::new(&mut self.kludgine, device, queue)
    }

    /// Sets the window's focused status.
    ///
    /// Being focused means that the window is expecting to be able to receive
    /// user input.
    pub fn set_focused(&mut self, focused: bool) {
        self.window.set_focused(focused);
    }

    /// Sets the window's occlusion status.
    ///
    /// This should only be set to true if the window is not visible at all to
    /// the end user due to being offscreen, minimized, or fully hidden behind
    /// other windows.
    pub fn set_occluded<W>(&mut self, window: &W, occluded: bool)
    where
        W: PlatformWindowImplementation,
    {
        self.window.set_occluded(window, occluded);
    }

    /// Requests that the window close.
    ///
    /// Returns true if the request should be honored.
    pub fn request_close<W>(&mut self, window: W) -> bool
    where
        W: PlatformWindowImplementation,
    {
        self.window.close_requested(window, &mut self.kludgine)
    }

    /// Returns the current size of the window.
    pub const fn size(&self) -> Size<UPx> {
        self.kludgine.size()
    }

    /// Returns the current DPI scale of the window.
    pub const fn dpi_scale(&self) -> Fraction {
        self.kludgine.dpi_scale()
    }

    /// Returns the effective scale of the window.
    pub fn effective_scale(&self) -> Fraction {
        self.kludgine.scale()
    }

    /// Updates the dimensions and DPI scaling of the window.
    pub fn resize<W>(
        &mut self,
        window: &W,
        new_size: Size<UPx>,
        new_scale: impl Into<Fraction>,
        new_zoom: impl Into<Fraction>,
        queue: &wgpu::Queue,
    ) where
        W: PlatformWindowImplementation,
    {
        self.kludgine.resize(new_size, new_scale, new_zoom, queue);
        self.window.resized(new_size, window);
    }

    /// Sets the window's position.
    pub fn set_position(&mut self, new_position: Point<Px>) {
        self.window.moved(new_position, new_position);
    }

    /// Provide keyboard input to this virtual window.
    ///
    /// Returns whether the event was [`HANDLED`] or [`IGNORED`].
    pub fn keyboard_input<W>(
        &mut self,
        window: W,
        device_id: DeviceId,
        input: KeyEvent,
        is_synthetic: bool,
    ) -> EventHandling
    where
        W: PlatformWindowImplementation,
    {
        self.window
            .keyboard_input(window, &mut self.kludgine, device_id, input, is_synthetic)
    }

    /// Provides mouse wheel input to this window.
    ///
    /// Returns whether the event was [`HANDLED`] or [`IGNORED`].
    pub fn mouse_wheel<W>(
        &mut self,
        window: W,
        device_id: DeviceId,
        delta: MouseScrollDelta,
        phase: TouchPhase,
    ) -> EventHandling
    where
        W: PlatformWindowImplementation,
    {
        self.window
            .mouse_wheel(window, &mut self.kludgine, device_id, delta, phase)
    }

    /// Provides input manager events to this window.
    ///
    /// Returns whether the event was [`HANDLED`] or [`IGNORED`].
    pub fn ime<W>(&mut self, window: W, ime: &Ime) -> EventHandling
    where
        W: PlatformWindowImplementation,
    {
        self.window.ime(window, &mut self.kludgine, ime)
    }

    /// Provides cursor movement events to this window.
    pub fn cursor_moved<W>(
        &mut self,
        window: W,
        device_id: DeviceId,
        position: impl Into<Point<Px>>,
    ) where
        W: PlatformWindowImplementation,
    {
        self.window
            .cursor_moved(window, &mut self.kludgine, device_id, position);
    }

    /// Notifies the window that the cursor is no longer within the window.
    pub fn cursor_left<W>(&mut self, window: W)
    where
        W: PlatformWindowImplementation,
    {
        self.window.cursor_left(window, &mut self.kludgine);
    }

    /// Provides mouse input events to tihs window.
    ///
    /// Returns whether the event was [`HANDLED`] or [`IGNORED`].
    pub fn mouse_input<W>(
        &mut self,
        window: W,
        device_id: DeviceId,
        state: ElementState,
        button: MouseButton,
    ) -> EventHandling
    where
        W: PlatformWindowImplementation,
    {
        self.window
            .mouse_input(window, &mut self.kludgine, device_id, state, button)
    }
}

/// A virtual Cushy window.
///
/// This type allows rendering Cushy applications directly into any wgpu
/// application.
pub struct VirtualWindow {
    cushy: CushyWindow,
    state: VirtualState,
    last_rendered_at: Option<Instant>,
}

impl VirtualWindow {
    /// Prepares all necessary resources and operations necessary to render the
    /// next frame.
    ///
    /// # Errors
    ///
    /// If during the preparation of rendering, the window is resized,
    /// `Err(Resized)` is returned and Cushy will immediately resize the
    /// graphics context and begin rendering again.
    pub fn prepare(&mut self, device: &wgpu::Device, queue: &wgpu::Queue) {
        let now = Instant::now();
        self.state.elapsed = self
            .last_rendered_at
            .map(|i| now.duration_since(i))
            .unwrap_or_default();
        self.last_rendered_at = Some(now);
        self.state.dynamic.redraw_target.set(RedrawTarget::Never);
        self.cushy.prepare(&mut self.state, device, queue);
    }

    /// Renders this window in a wgpu render pass created from `pass`.
    ///
    /// Returns the submission index of the last command submission, if any
    /// commands were submitted.
    pub fn render(
        &mut self,
        pass: &wgpu::RenderPassDescriptor<'_>,
        device: &wgpu::Device,
        queue: &wgpu::Queue,
    ) -> Option<wgpu::SubmissionIndex> {
        self.render_with(pass, device, queue, None)
    }

    /// Renders this window in a wgpu render pass created from `pass`.
    ///
    /// Returns the submission index of the last command submission, if any
    /// commands were submitted.
    pub fn render_with(
        &mut self,
        pass: &wgpu::RenderPassDescriptor<'_>,
        device: &wgpu::Device,
        queue: &wgpu::Queue,
        additional_drawing: Option<&Drawing>,
    ) -> Option<wgpu::SubmissionIndex> {
        self.cushy
            .render_with(pass, device, queue, additional_drawing)
    }

    /// Renders this window into `texture` after performing `load_op`.
    pub fn render_into(
        &mut self,
        texture: &kludgine::Texture,
        load_op: wgpu::LoadOp<Color>,
        device: &wgpu::Device,
        queue: &wgpu::Queue,
    ) -> Option<wgpu::SubmissionIndex> {
        self.cushy.render_into(texture, load_op, device, queue)
    }

    /// Returns a new [`kludgine::Graphics`] context for this window.
    #[must_use]
    pub fn graphics<'gfx>(
        &'gfx mut self,
        device: &'gfx wgpu::Device,
        queue: &'gfx wgpu::Queue,
    ) -> kludgine::Graphics<'gfx> {
        self.cushy.graphics(device, queue)
    }

    /// Requests that the window close.
    ///
    /// Returns true if the request should be honored.
    pub fn request_close(&mut self) -> bool {
        if self.cushy.request_close(&mut self.state) {
            self.state.closed = true;
            true
        } else {
            self.state.dynamic.close_requested.set(false);
            false
        }
    }

    /// Sets the window's focused status.
    ///
    /// Being focused means that the window is expecting to be able to receive
    /// user input.
    pub fn set_focused(&mut self, focused: bool) {
        self.cushy.set_focused(focused);
    }

    /// Sets the window's occlusion status.
    ///
    /// This should only be set to true if the window is not visible at all to
    /// the end user due to being offscreen, minimized, or fully hidden behind
    /// other windows.
    pub fn set_occluded(&mut self, occluded: bool) {
        self.cushy.set_occluded(&&mut self.state, occluded);
    }

    /// Returns true if this window should no longer be open.
    #[must_use]
    pub fn closed(&self) -> bool {
        self.state.closed
    }

    /// Returns a reference to the window's state.
    #[must_use]
    pub const fn state(&self) -> &VirtualState {
        &self.state
    }

    /// Returns the current size of the window.
    pub const fn size(&self) -> Size<UPx> {
        self.cushy.size()
    }

    /// Returns the current DPI scale of the window.
    pub const fn dpi_scale(&self) -> Fraction {
        self.cushy.dpi_scale()
    }

    /// Updates the dimensions and DPI scaling of the window.
    pub fn resize(
        &mut self,
        new_size: Size<UPx>,
        new_scale: impl Into<Fraction>,
        queue: &wgpu::Queue,
    ) {
        self.cushy.resize(
            &&mut self.state,
            new_size,
            new_scale,
            self.cushy.kludgine.zoom(),
            queue,
        );
    }

    /// Sets the window's position.
    pub fn set_position(&mut self, new_position: Point<Px>) {
        self.cushy.set_position(new_position);
    }

    /// Provide keyboard input to this virtual window.
    ///
    /// Returns whether the event was [`HANDLED`] or [`IGNORED`].
    pub fn keyboard_input(
        &mut self,
        device_id: DeviceId,
        input: KeyEvent,
        is_synthetic: bool,
    ) -> EventHandling {
        self.cushy
            .keyboard_input(&mut self.state, device_id, input, is_synthetic)
    }

    /// Provides mouse wheel input to this window.
    ///
    /// Returns whether the event was [`HANDLED`] or [`IGNORED`].
    pub fn mouse_wheel(
        &mut self,
        device_id: DeviceId,
        delta: MouseScrollDelta,
        phase: TouchPhase,
    ) -> EventHandling {
        self.cushy
            .mouse_wheel(&mut self.state, device_id, delta, phase)
    }

    /// Provides input manager events to this window.
    ///
    /// Returns whether the event was [`HANDLED`] or [`IGNORED`].
    pub fn ime(&mut self, ime: &Ime) -> EventHandling {
        self.cushy.ime(&mut self.state, ime)
    }

    /// Provides cursor movement events to this window.
    pub fn cursor_moved(&mut self, device_id: DeviceId, position: impl Into<Point<Px>>) {
        self.cushy
            .cursor_moved(&mut self.state, device_id, position);
    }

    /// Notifies the window that the cursor is no longer within the window.
    pub fn cursor_left(&mut self) {
        self.cushy.cursor_left(&mut self.state);
    }

    /// Provides mouse input events to tihs window.
    ///
    /// Returns whether the event was [`HANDLED`] or [`IGNORED`].
    pub fn mouse_input(
        &mut self,
        device_id: DeviceId,
        state: ElementState,
        button: MouseButton,
    ) -> EventHandling {
        self.cushy
            .mouse_input(&mut self.state, device_id, state, button)
    }
}

/// A color format containing 8-bit red, green, and blue channels.
pub struct Rgb8;

/// A color format containing 8-bit red, green, blue, and alpha channels.
pub struct Rgba8;

/// A format that can be captured in a [`VirtualRecorder`].
pub trait CaptureFormat: sealed::CaptureFormat {}

impl CaptureFormat for Rgb8 {}

impl sealed::CaptureFormat for Rgb8 {
    const HAS_ALPHA: bool = false;

    fn convert_rgba(data: &mut Vec<u8>, width: u32, bytes_per_row: u32) {
        let packed_width = width * 4;
        // Tightly pack the rgb data, discarding the alpha and extra padding.q
        let mut index = 0;
        data.retain(|_| {
            let retain = index % bytes_per_row < packed_width && index % 4 < 3;
            index += 1;
            retain
        });
    }

    fn load_image(data: &[u8], size: Size<UPx>) -> DynamicImage {
        DynamicImage::ImageRgb8(
            RgbImage::from_vec(size.width.get(), size.height.get(), data.to_vec())
                .expect("incorrect dimensions"),
        )
    }

    fn pixel_color(location: Point<UPx>, data: &[u8], size: Size<UPx>) -> Color {
        let pixel_offset = pixel_offset(data, location, size, 3);
        Color::new(pixel_offset[0], pixel_offset[1], pixel_offset[2], 255)
    }
}

fn pixel_offset(
    data: &[u8],
    location: Point<UPx>,
    size: Size<UPx>,
    bytes_per_component: u32,
) -> &[u8] {
    assert!(location.x < size.width && location.y < size.height);

    let width = size.width.get();
    let index = location.y.get() * width + location.x.get();
    &data[usize::try_from(index * bytes_per_component).expect("offset out of bounds")..]
}

impl CaptureFormat for Rgba8 {}

impl sealed::CaptureFormat for Rgba8 {
    const HAS_ALPHA: bool = true;

    fn convert_rgba(data: &mut Vec<u8>, width: u32, bytes_per_row: u32) {
        let packed_width = width * 4;
        if packed_width != bytes_per_row {
            // Tightly pack the rgba data
            let mut index = 0;
            data.retain(|_| {
                let retain = index % bytes_per_row < packed_width;
                index += 1;
                retain
            });
        }
    }

    fn load_image(data: &[u8], size: Size<UPx>) -> DynamicImage {
        DynamicImage::ImageRgba8(
            RgbaImage::from_vec(size.width.get(), size.height.get(), data.to_vec())
                .expect("incorrect dimensions"),
        )
    }

    fn pixel_color(location: Point<UPx>, data: &[u8], size: Size<UPx>) -> Color {
        let pixel_offset = pixel_offset(data, location, size, 4);
        Color::new(
            pixel_offset[0],
            pixel_offset[1],
            pixel_offset[2],
            pixel_offset[3],
        )
    }
}

/// A builder of a [`VirtualRecorder`].
pub struct VirtualRecorderBuilder<Format> {
    contents: WidgetInstance,
    size: Size<UPx>,
    scale: f32,
    format: PhantomData<Format>,
    resize_to_fit: bool,
}

impl VirtualRecorderBuilder<Rgb8> {
    /// Returns a builder of a [`VirtualRecorder`] that renders `contents`.
    pub fn new(contents: impl MakeWidget) -> Self {
        Self {
            contents: contents.make_widget(),
            size: Size::upx(800, 600),
            scale: 1.0,
            format: PhantomData,
            resize_to_fit: false,
        }
    }

    /// Enables transparency support to render the contents without a background
    /// color.
    #[must_use]
    pub fn with_alpha(self) -> VirtualRecorderBuilder<Rgba8> {
        VirtualRecorderBuilder {
            contents: self.contents,
            size: self.size,
            scale: self.scale,
            resize_to_fit: self.resize_to_fit,
            format: PhantomData,
        }
    }
}

impl<Format> VirtualRecorderBuilder<Format>
where
    Format: CaptureFormat,
{
    /// Sets the size of the virtual window.
    #[must_use]
    pub fn size<Unit>(mut self, size: Size<Unit>) -> Self
    where
        Unit: Into<UPx>,
    {
        self.size = size.map(Into::into);
        self
    }

    /// Sets the DPI scaling to apply to this virtual window.
    ///
    /// When scale is 1.0, resolution-independent content will be rendered at
    /// 96-ppi.
    ///
    /// This setting does not affect the image's pixel dimensions.
    #[must_use]
    pub fn scale(mut self, scale: f32) -> Self {
        self.scale = scale;
        self
    }

    /// Sets this virtual recorder to allow updating its size based on the
    /// contents being rendered.
    #[must_use]
    pub fn resize_to_fit(mut self) -> Self {
        self.resize_to_fit = true;
        self
    }

    /// Returns an initialized [`VirtualRecorder`].
    pub fn finish(self) -> Result<VirtualRecorder<Format>, VirtualRecorderError> {
        VirtualRecorder::new(self.size, self.scale, self.resize_to_fit, self.contents)
    }
}

struct Capture {
    bytes: u64,
    bytes_per_row: u32,
    buffer: wgpu::Buffer,
    texture: Texture,
    multisample: Texture,
}

impl Capture {
    fn map_into<Format>(
        &self,
        buffer: &mut Vec<u8>,
        device: &wgpu::Device,
        queue: &wgpu::Queue,
    ) -> Result<(), wgpu::BufferAsyncError>
    where
        Format: CaptureFormat,
    {
        let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
        self.texture.copy_to_buffer(
            wgpu::ImageCopyBuffer {
                buffer: &self.buffer,
                layout: wgpu::ImageDataLayout {
                    offset: 0,
                    bytes_per_row: Some(self.bytes_per_row),
                    rows_per_image: None,
                },
            },
            &mut encoder,
        );
        queue.submit([encoder.finish()]);

        let map_result = Arc::new(Mutex::new(None));
        let slice = self.buffer.slice(0..self.bytes);

        slice.map_async(wgpu::MapMode::Read, {
            let map_result = map_result.clone();
            move |result| {
                *map_result.lock() = Some(result);
            }
        });

        buffer.clear();
        buffer.reserve(self.bytes.cast());

        loop {
            device.poll(wgpu::Maintain::Poll);
            let mut result = map_result.lock();
            if let Some(result) = result.take() {
                result?;
                break;
            }
        }

        buffer.extend_from_slice(&slice.get_mapped_range());
        self.buffer.unmap();

        Format::convert_rgba(buffer, self.texture.size().width.get(), self.bytes_per_row);

        Ok(())
    }
}

/// A recorder of a [`VirtualWindow`].
pub struct VirtualRecorder<Format = Rgb8> {
    /// The virtual window being recorded.
    pub window: VirtualWindow,
    device: Arc<wgpu::Device>,
    queue: Arc<wgpu::Queue>,
    capture: Option<Box<Capture>>,
    data: Vec<u8>,
    data_size: Size<UPx>,
    cursor: Dynamic<Point<Px>>,
    cursor_visible: bool,
    cursor_graphic: Drawing,
    format: PhantomData<Format>,
}

impl<Format> VirtualRecorder<Format>
where
    Format: CaptureFormat,
{
    /// Returns a new virtual recorder that renders `contents` into a graphic of
    /// `size`.
    ///
    /// `scale` adjusts the default DPI scaling to perform. It does not affect
    /// the `size`.
    pub fn new(
        size: Size<UPx>,
        scale: f32,
        resize_to_fit: bool,
        contents: impl MakeWidget,
    ) -> Result<Self, VirtualRecorderError> {
        let wgpu = wgpu::Instance::default();
        let adapter =
            pollster::block_on(wgpu.request_adapter(&wgpu::RequestAdapterOptions::default()))
                .ok_or(VirtualRecorderError::NoAdapter)?;
        let (device, queue) = pollster::block_on(adapter.request_device(
            &wgpu::DeviceDescriptor {
                label: None,
                required_features: Kludgine::REQURED_FEATURES,
                required_limits: Kludgine::adjust_limits(wgpu::Limits::downlevel_webgl2_defaults()),
                memory_hints: wgpu::MemoryHints::MemoryUsage,
            },
            None,
        ))?;

        let window = contents
            .build_standalone_window()
            .size(size)
            .scale(scale)
            .transparent()
            .resize_to_fit(resize_to_fit)
            .finish_virtual(&device, &queue);

        let mut recorder = Self {
            window,
            device: Arc::new(device),
            queue: Arc::new(queue),
            cursor: Dynamic::default(),
            cursor_graphic: Drawing::default(),
            cursor_visible: false,
            capture: None,
            data: Vec::new(),
            data_size: Size::ZERO,
            format: PhantomData,
        };
        recorder.refresh()?;

        if resize_to_fit && recorder.window.state.size != recorder.window.size() {
            recorder.refresh()?;
        }
        Ok(recorder)
    }

    /// Returns the tightly-packed captured bytes.
    ///
    /// The layout of this data is determined by the `Format` generic.
    pub fn bytes(&self) -> &[u8] {
        &self.data
    }

    /// Returns the color of the pixel at `location`.
    ///
    /// # Panics
    ///
    /// This function will panic if location is outside of the bounds of the
    /// captured image. When the window's size has been changed, this function
    /// operates on the size of the window when the last call to
    /// [`Self::refresh()`] was made.
    pub fn pixel_color<Unit>(&self, location: Point<Unit>) -> Color
    where
        Unit: Into<UPx>,
    {
        Format::pixel_color(location.map(Into::into), self.bytes(), self.data_size)
    }

    /// Asserts that the color of the pixel at `location` is `expected`.
    ///
    /// This function allows for slight color variations. This is because of how
    /// colorspace corrections can lead to rounding errors.
    ///
    /// # Panics
    ///
    /// This function panics if the color is not the expected color.
    #[track_caller]
    pub fn assert_pixel_color<Unit>(&self, location: Point<Unit>, expected: Color, component: &str)
    where
        Unit: Into<UPx>,
    {
        let location = location.map(Into::into);
        let color = self.pixel_color(location);
        let max_delta = color
            .red()
            .abs_diff(expected.red())
            .max(color.green().abs_diff(expected.green()))
            .max(color.blue().abs_diff(expected.blue()))
            .max(color.alpha().abs_diff(expected.alpha()));
        assert!(
            max_delta <= 1,
            "assertion failed: {component} at {location:?} was {color:?}, not {expected:?}"
        );
    }

    /// Returns the current contents as an image.
    pub fn image(&self) -> DynamicImage {
        Format::load_image(self.bytes(), self.data_size)
    }

    fn recreate_buffers_if_needed(&mut self, size: Size<UPx>, bytes: u64, bytes_per_row: u32) {
        if self
            .capture
            .as_ref()
            .map_or(true, |capture| capture.texture.size() != size)
        {
            let texture = Texture::new(
                &self.window.graphics(&self.device, &self.queue),
                size,
                wgpu::TextureFormat::Rgba8UnormSrgb,
                wgpu::TextureUsages::RENDER_ATTACHMENT
                    | wgpu::TextureUsages::COPY_SRC
                    | wgpu::TextureUsages::TEXTURE_BINDING,
                wgpu::FilterMode::Linear,
            );
            let multisample = Texture::multisampled(
                &self.window.graphics(&self.device, &self.queue),
                4,
                size,
                wgpu::TextureFormat::Rgba8UnormSrgb,
                wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
                wgpu::FilterMode::Linear,
            );
            let buffer = self.device.create_buffer(&wgpu::BufferDescriptor {
                label: None,
                size: bytes,
                usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
                mapped_at_creation: false,
            });
            self.capture = Some(Box::new(Capture {
                bytes,
                bytes_per_row,
                buffer,
                texture,
                multisample,
            }));
        }
    }

    fn redraw(&mut self) {
        let mut render_size = self.window.size().ceil();
        if self.window.state.size != render_size {
            let current_scale = self.window.dpi_scale();
            self.window
                .resize(self.window.state.size, current_scale, &self.queue);
            render_size = self.window.state.size;
        }
        let bytes_per_row = copy_buffer_aligned_bytes_per_row(render_size.width.get() * 4);
        let size = u64::from(bytes_per_row) * u64::from(render_size.height.get());
        self.recreate_buffers_if_needed(render_size, size, bytes_per_row);

        let capture = self.capture.as_ref().assert("always initialized above");

        if self.cursor_visible {
            let mut gfx = self.window.graphics(&self.device, &self.queue);
            let mut frame = self.cursor_graphic.new_frame(&mut gfx);
            frame.draw_shape(
                Shape::filled_circle(Px::new(4), Color::WHITE, Origin::Center)
                    .translate_by(self.cursor.get()),
            );
            drop(frame);
        }

        self.window.prepare(&self.device, &self.queue);

        self.window.render_with(
            &wgpu::RenderPassDescriptor {
                label: None,
                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
                    view: capture.multisample.view(),
                    resolve_target: Some(capture.texture.view()),
                    ops: wgpu::Operations {
                        load: wgpu::LoadOp::Clear(Color::CLEAR_BLACK.into()),
                        store: wgpu::StoreOp::Store,
                    },
                })],
                depth_stencil_attachment: None,
                timestamp_writes: None,
                occlusion_query_set: None,
            },
            &self.device,
            &self.queue,
            self.cursor_visible.then_some(&self.cursor_graphic),
        );
    }

    /// Redraws the contents.
    pub fn refresh(&mut self) -> Result<(), wgpu::BufferAsyncError> {
        self.redraw();

        let capture = self.capture.as_ref().assert("always initialized above");

        capture.map_into::<Format>(&mut self.data, &self.device, &self.queue)?;
        self.data_size = capture.texture.size();

        Ok(())
    }

    /// Sets the cursor position immediately.
    pub fn set_cursor_position(&self, position: Point<Px>) {
        self.cursor.set(position);
    }

    /// Enables or disables drawing of the virtual cursor.
    pub fn set_cursor_visible(&mut self, visible: bool) {
        self.cursor_visible = visible;
    }

    /// Begins recording an animated png.
    pub fn record_animated_png(&mut self, target_fps: u8) -> AnimationRecorder<'_, Format> {
        AnimationRecorder {
            target_fps,
            assembler: Some(FrameAssembler::spawn::<Format>(
                self.device.clone(),
                self.queue.clone(),
            )),
            recorder: self,
        }
    }

    /// Returns a recorder that does not store any rendered frames.
    pub fn simulate_animation(&mut self) -> AnimationRecorder<'_, Format> {
        AnimationRecorder {
            target_fps: 0,
            assembler: None,
            recorder: self,
        }
    }
}

fn copy_buffer_aligned_bytes_per_row(width: u32) -> u32 {
    (width + COPY_BYTES_PER_ROW_ALIGNMENT - 1) / COPY_BYTES_PER_ROW_ALIGNMENT
        * COPY_BYTES_PER_ROW_ALIGNMENT
}

/// An animated PNG recorder.
pub struct AnimationRecorder<'a, Format> {
    recorder: &'a mut VirtualRecorder<Format>,
    target_fps: u8,
    assembler: Option<FrameAssembler>,
}

impl<Format> AnimationRecorder<'_, Format>
where
    Format: CaptureFormat,
{
    /// Animates the cursor to move from its current location to `location`.
    pub fn animate_cursor_to(
        &mut self,
        location: Point<Px>,
        over: Duration,
        easing: impl Easing,
    ) -> Result<(), VirtualRecorderError> {
        self.recorder
            .cursor
            .transition_to(location)
            .over(over)
            .with_easing(easing)
            .launch();
        self.wait_for(over)
    }

    /// Animates pressing and releasing a mouse button at the current cursor
    /// location.
    pub fn animate_mouse_button(
        &mut self,
        button: MouseButton,
        duration: Duration,
    ) -> Result<(), VirtualRecorderError> {
        let _ =
            self.recorder
                .window
                .mouse_input(DeviceId::Virtual(0), ElementState::Pressed, button);

        self.wait_for(duration)?;
        let _ =
            self.recorder
                .window
                .mouse_input(DeviceId::Virtual(0), ElementState::Released, button);
        Ok(())
    }

    /// Simulates a key down and key up event with the given information.
    pub fn animate_keypress(
        &mut self,
        physical_key: PhysicalKey,
        logical_key: Key,
        text: Option<&str>,
        duration: Duration,
    ) -> Result<(), VirtualRecorderError> {
        let text = text.map(SmolStr::new);
        let half_duration = duration / 2;
        let mut event = KeyEvent {
            physical_key,
            logical_key,
            text,
            state: ElementState::Pressed,
            repeat: false,
            location: KeyLocation::Standard,
            modifiers: Modifiers::default(),
        };
        self.recorder
            .window
            .keyboard_input(DeviceId::Virtual(0), event.clone(), true);
        self.wait_for(half_duration)?;
        event.state = ElementState::Released;
        self.recorder
            .window
            .keyboard_input(DeviceId::Virtual(0), event.clone(), true);

        self.wait_for(half_duration)
    }

    /// Animates entering the graphemes from `text` over `duration`.
    pub fn animate_text_input(
        &mut self,
        text: &str,
        duration: Duration,
    ) -> Result<(), VirtualRecorderError> {
        let graphemes = text.graphemes(true).count();
        let delay_per_event =
            Duration::from_nanos(duration.as_nanos().cast::<u64>() / graphemes.cast::<u64>() / 2);
        for grapheme in text.graphemes(true) {
            let grapheme = SmolStr::new(grapheme);
            let mut event = KeyEvent {
                physical_key: PhysicalKey::Unidentified(NativeKeyCode::Xkb(0)),
                logical_key: Key::Character(grapheme.clone()),
                text: Some(SmolStr::new(grapheme)),
                location: KeyLocation::Standard,
                state: ElementState::Pressed,
                repeat: false,
                modifiers: Modifiers::default(),
            };
            let _handled =
                self.recorder
                    .window
                    .keyboard_input(DeviceId::Virtual(0), event.clone(), true);
            self.wait_for(delay_per_event)?;

            event.state = ElementState::Released;
            let _handled = self
                .recorder
                .window
                .keyboard_input(DeviceId::Virtual(0), event, true);
            self.wait_for(delay_per_event)?;
        }
        Ok(())
    }

    /// Waits for `duration`, rendering frames as needed.
    pub fn wait_for(&mut self, duration: Duration) -> Result<(), VirtualRecorderError> {
        self.wait_until(Instant::now() + duration)
    }

    /// Waits until `time`, rendering frames as needed.
    pub fn wait_until(&mut self, time: Instant) -> Result<(), VirtualRecorderError> {
        let Some(assembler) = self.assembler.as_ref() else {
            return Ok(());
        };

        let frame_duration = Duration::from_micros(1_000_000 / u64::from(self.target_fps));
        let mut last_frame = Instant::now();

        loop {
            let now = Instant::now();
            let final_frame = now > time;

            self.recorder
                .window
                .cursor_moved(DeviceId::Virtual(0), self.recorder.cursor.get());

            let next_frame = match self.recorder.window.state.dynamic.redraw_target.get() {
                RedrawTarget::Never => now + frame_duration,
                RedrawTarget::Now => now,
                RedrawTarget::At(instant) => now.min(instant),
            };

            if final_frame || next_frame <= now {
                // Try to reuse an existing capture instead of forcing an
                // allocation.
                if let Ok(capture) = assembler.resuable_captures.try_recv() {
                    self.recorder.capture = Some(capture);
                }
                let elapsed = now.saturating_duration_since(last_frame);
                last_frame = now;
                self.recorder.redraw();
                let capture = self.recorder.capture.take().assert("always present");
                if assembler.sender.send((capture, elapsed)).is_err() {
                    break;
                }
            }

            if final_frame {
                break;
            }

            let render_duration = now.elapsed();
            std::thread::sleep(frame_duration.saturating_sub(render_duration));
        }

        Ok(())
    }

    /// Encodes the currently recorded frames into a new file at `path`.
    ///
    /// If this animation was created from
    /// [`VirtualRecorder::simulate_animation`], this function will do nothing.
    pub fn write_to(self, path: impl AsRef<Path>) -> Result<(), VirtualRecorderError> {
        let Some(frames) = self.assembler.map(FrameAssembler::finish).transpose()? else {
            return Ok(());
        };
        let mut file = std::fs::OpenOptions::new()
            .create(true)
            .truncate(true)
            .write(true)
            .open(path)?;
        let mut encoder = png::Encoder::new(
            &mut file,
            self.recorder.window.size().width.get(),
            self.recorder.window.size().height.get(),
        );
        encoder.set_color(if Format::HAS_ALPHA {
            png::ColorType::Rgba
        } else {
            png::ColorType::Rgb
        });
        encoder.set_adaptive_filter(png::AdaptiveFilterType::Adaptive);
        encoder.set_animated(u32::try_from(frames.len()).assert("too many frames"), 0)?;
        encoder.set_compression(png::Compression::Best);

        let mut current_frame_delay = Duration::ZERO;
        let mut writer = encoder.write_header()?;
        for frame in &frames {
            if current_frame_delay != frame.duration && frames.len() > 1 {
                current_frame_delay = frame.duration;
                // This has a limitation that a single frame can't be longer
                // than ~6.5 seconds, but it ensures frame timing is more
                // accurate.
                writer.set_frame_delay(
                    u16::try_from(current_frame_delay.as_nanos() / 100_000).unwrap_or(u16::MAX),
                    10_000,
                )?;
            }
            writer.write_image_data(&frame.data)?;
        }

        writer.finish()?;

        file.sync_all()?;

        Ok(())
    }
}

struct Frame {
    data: Vec<u8>,
    duration: Duration,
}

/// An error from a [`VirtualRecorder`].
#[derive(Debug)]
pub enum VirtualRecorderError {
    /// No compatible wgpu adapters could be found.
    NoAdapter,
    /// An error occurred requesting a device.
    RequestDevice(wgpu::RequestDeviceError),
    /// The capture texture dimensions are too large to fit in the current host
    /// platform's memory.
    TooLarge,
    /// An error occurred trying to read a buffer.
    MapBuffer(wgpu::BufferAsyncError),
    /// An error occurred encoding a png image.
    PngEncode(png::EncodingError),
}

impl From<png::EncodingError> for VirtualRecorderError {
    fn from(value: png::EncodingError) -> Self {
        Self::PngEncode(value)
    }
}

impl From<wgpu::RequestDeviceError> for VirtualRecorderError {
    fn from(value: wgpu::RequestDeviceError) -> Self {
        Self::RequestDevice(value)
    }
}

impl From<wgpu::BufferAsyncError> for VirtualRecorderError {
    fn from(value: wgpu::BufferAsyncError) -> Self {
        Self::MapBuffer(value)
    }
}

impl From<TryFromIntError> for VirtualRecorderError {
    fn from(_: TryFromIntError) -> Self {
        Self::TooLarge
    }
}

impl From<io::Error> for VirtualRecorderError {
    fn from(value: io::Error) -> Self {
        Self::PngEncode(value.into())
    }
}

impl std::fmt::Display for VirtualRecorderError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            VirtualRecorderError::NoAdapter => {
                f.write_str("no compatible graphics adapters were found")
            }
            VirtualRecorderError::RequestDevice(err) => {
                write!(f, "error requesting graphics device: {err}")
            }
            VirtualRecorderError::TooLarge => {
                f.write_str("the rendered surface is too large for this cpu architecture")
            }
            VirtualRecorderError::MapBuffer(err) => {
                write!(f, "error reading rendered graphics data: {err}")
            }
            VirtualRecorderError::PngEncode(err) => write!(f, "error encoding png: {err}"),
        }
    }
}

impl std::error::Error for VirtualRecorderError {}

/// A unique identifier of an input device.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub enum DeviceId {
    /// A winit-supplied device id.
    Winit(winit::event::DeviceId),
    /// A simulated device.
    Virtual(u64),
}

impl From<winit::event::DeviceId> for DeviceId {
    fn from(value: winit::event::DeviceId) -> Self {
        Self::Winit(value)
    }
}

struct FrameAssembler {
    sender: mpsc::SyncSender<(Box<Capture>, Duration)>,
    result: mpsc::Receiver<Result<Vec<Frame>, VirtualRecorderError>>,
    resuable_captures: mpsc::Receiver<Box<Capture>>,
}

impl FrameAssembler {
    fn spawn<Format>(device: Arc<wgpu::Device>, queue: Arc<wgpu::Queue>) -> Self
    where
        Format: CaptureFormat,
    {
        let (frame_sender, frame_receiver) = mpsc::sync_channel(1000);
        let (finished_frame_sender, finished_frame_receiver) = mpsc::sync_channel(600);
        let (result_sender, result_receiver) = mpsc::sync_channel(1);

        std::thread::spawn(move || {
            Self::assembler_thread::<Format>(
                &frame_receiver,
                &result_sender,
                &finished_frame_sender,
                &device,
                &queue,
            );
        });

        Self {
            sender: frame_sender,
            result: result_receiver,
            resuable_captures: finished_frame_receiver,
        }
    }

    fn finish(self) -> Result<Vec<Frame>, VirtualRecorderError> {
        drop(self.sender);
        self.result.recv().assert("thread panicked")
    }

    fn assembler_thread<Format>(
        frames: &mpsc::Receiver<(Box<Capture>, Duration)>,
        result: &mpsc::SyncSender<Result<Vec<Frame>, VirtualRecorderError>>,
        reusable: &mpsc::SyncSender<Box<Capture>>,
        device: &wgpu::Device,
        queue: &wgpu::Queue,
    ) where
        Format: CaptureFormat,
    {
        let mut assembled = Vec::<Frame>::new();
        let mut buffer = Vec::new();
        while let Ok((capture, elapsed)) = frames.recv() {
            if let Err(err) = capture.map_into::<Format>(&mut buffer, device, queue) {
                let _result = result.send(Err(err.into()));
                return;
            }
            match assembled.last_mut() {
                Some(frame) if frame.data == buffer => {
                    frame.duration += elapsed;
                }
                _ => {
                    assembled.push(Frame {
                        data: std::mem::take(&mut buffer),
                        duration: elapsed,
                    });
                }
            }
            let _result = reusable.try_send(capture);
        }

        let _result = result.send(Ok(assembled));
    }
}

/// Describes a keyboard input targeting a window.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct KeyEvent {
    /// The logical key that is interpretted from the `physical_key`.
    ///
    /// See [`KeyEvent::logical_key`](winit::event::KeyEvent::logical_key) for
    /// more information.
    pub logical_key: Key,
    /// The physical key that caused this event.
    ///
    /// See [`KeyEvent::logical_key`](winit::event::KeyEvent::physical_key) for
    /// more information.
    pub physical_key: PhysicalKey,

    /// The text being input by this event, if any.
    ///
    /// See [`KeyEvent::logical_key`](winit::event::KeyEvent::text) for
    /// more information.
    pub text: Option<SmolStr>,

    /// The physical location of the key being presed.
    ///
    /// See [`KeyEvent::logical_key`](winit::event::KeyEvent::location) for
    /// more information.
    pub location: KeyLocation,

    /// The state of this key for this event.
    ///
    /// See [`KeyEvent::logical_key`](winit::event::KeyEvent::state) for
    /// more information.
    pub state: ElementState,

    /// If true, this event was caused by a key being repeated.
    ///
    /// See [`KeyEvent::logical_key`](winit::event::KeyEvent::logical_key) for
    /// more information.
    pub repeat: bool,

    /// The modifiers state active for this event.
    pub modifiers: Modifiers,
}

impl KeyEvent {
    /// Returns a new key event from a winit key event and modifiers.
    #[must_use]
    pub fn from_winit(event: winit::event::KeyEvent, modifiers: Modifiers) -> Self {
        Self {
            physical_key: event.physical_key,
            logical_key: event.logical_key,
            text: event.text,
            location: event.location,
            state: event.state,
            repeat: event.repeat,
            modifiers,
        }
    }
}
