// `EventSource` is macro-generated; the lint can't be allowed on the enum itself.
#![allow(clippy::exhaustive_enums)]
use std::cell::Cell;
use std::collections::VecDeque;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, mpsc};
use std::time::{Duration, Instant};
use std::{iter, mem, slice};

use bitflags::bitflags;
use orbclient::{
    ButtonEvent, EventOption, FocusEvent, HoverEvent, KeyEvent, MouseEvent, MouseRelativeEvent,
    MoveEvent, QuitEvent, ResizeEvent, ScrollEvent, TextInputEvent,
};
use redox_event::{EventFlags, EventQueue, UserData};
use smol_str::SmolStr;
use winit_core::application::ApplicationHandler;
use winit_core::cursor::{CustomCursor, CustomCursorSource};
use winit_core::error::{EventLoopError, NotSupportedError, RequestError};
use winit_core::event::{self, Modifiers, StartCause};
use winit_core::event_loop::pump_events::PumpStatus;
use winit_core::event_loop::{
    ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents, EventLoopProvider,
    EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider,
    OwnedDisplayHandle as CoreOwnedDisplayHandle,
};
use winit_core::keyboard::{
    Key, KeyCode, KeyLocation, ModifiersKeys, ModifiersState, NamedKey, NativeKey, NativeKeyCode,
    PhysicalKey,
};
use winit_core::window::{Theme, Window as CoreWindow, WindowId};

use crate::window::Window;
use crate::{RedoxSocket, TimeSocket, WindowProperties};

/// timeout from redox_syscall
const EVENT_TIMEOUT_ID: usize = usize::MAX - 2;

fn convert_scancode(scancode: u8) -> (PhysicalKey, Option<NamedKey>) {
    // Key constants from https://docs.rs/orbclient/latest/orbclient/event/index.html
    let (key_code, named_key_opt) = match scancode {
        orbclient::K_A => (KeyCode::KeyA, None),
        orbclient::K_B => (KeyCode::KeyB, None),
        orbclient::K_C => (KeyCode::KeyC, None),
        orbclient::K_D => (KeyCode::KeyD, None),
        orbclient::K_E => (KeyCode::KeyE, None),
        orbclient::K_F => (KeyCode::KeyF, None),
        orbclient::K_G => (KeyCode::KeyG, None),
        orbclient::K_H => (KeyCode::KeyH, None),
        orbclient::K_I => (KeyCode::KeyI, None),
        orbclient::K_J => (KeyCode::KeyJ, None),
        orbclient::K_K => (KeyCode::KeyK, None),
        orbclient::K_L => (KeyCode::KeyL, None),
        orbclient::K_M => (KeyCode::KeyM, None),
        orbclient::K_N => (KeyCode::KeyN, None),
        orbclient::K_O => (KeyCode::KeyO, None),
        orbclient::K_P => (KeyCode::KeyP, None),
        orbclient::K_Q => (KeyCode::KeyQ, None),
        orbclient::K_R => (KeyCode::KeyR, None),
        orbclient::K_S => (KeyCode::KeyS, None),
        orbclient::K_T => (KeyCode::KeyT, None),
        orbclient::K_U => (KeyCode::KeyU, None),
        orbclient::K_V => (KeyCode::KeyV, None),
        orbclient::K_W => (KeyCode::KeyW, None),
        orbclient::K_X => (KeyCode::KeyX, None),
        orbclient::K_Y => (KeyCode::KeyY, None),
        orbclient::K_Z => (KeyCode::KeyZ, None),
        orbclient::K_0 => (KeyCode::Digit0, None),
        orbclient::K_1 => (KeyCode::Digit1, None),
        orbclient::K_2 => (KeyCode::Digit2, None),
        orbclient::K_3 => (KeyCode::Digit3, None),
        orbclient::K_4 => (KeyCode::Digit4, None),
        orbclient::K_5 => (KeyCode::Digit5, None),
        orbclient::K_6 => (KeyCode::Digit6, None),
        orbclient::K_7 => (KeyCode::Digit7, None),
        orbclient::K_8 => (KeyCode::Digit8, None),
        orbclient::K_9 => (KeyCode::Digit9, None),

        orbclient::K_ALT => (KeyCode::AltLeft, Some(NamedKey::Alt)),
        orbclient::K_ALT_GR => (KeyCode::AltRight, Some(NamedKey::AltGraph)),
        orbclient::K_BACKSLASH => (KeyCode::Backslash, None),
        orbclient::K_BKSP => (KeyCode::Backspace, Some(NamedKey::Backspace)),
        orbclient::K_BRACE_CLOSE => (KeyCode::BracketRight, None),
        orbclient::K_BRACE_OPEN => (KeyCode::BracketLeft, None),
        orbclient::K_CAPS => (KeyCode::CapsLock, Some(NamedKey::CapsLock)),
        orbclient::K_COMMA => (KeyCode::Comma, None),
        orbclient::K_CTRL => (KeyCode::ControlLeft, Some(NamedKey::Control)),
        orbclient::K_DEL => (KeyCode::Delete, Some(NamedKey::Delete)),
        orbclient::K_DOWN => (KeyCode::ArrowDown, Some(NamedKey::ArrowDown)),
        orbclient::K_END => (KeyCode::End, Some(NamedKey::End)),
        orbclient::K_ENTER => (KeyCode::Enter, Some(NamedKey::Enter)),
        orbclient::K_EQUALS => (KeyCode::Equal, None),
        orbclient::K_ESC => (KeyCode::Escape, Some(NamedKey::Escape)),
        orbclient::K_F1 => (KeyCode::F1, Some(NamedKey::F1)),
        orbclient::K_F2 => (KeyCode::F2, Some(NamedKey::F2)),
        orbclient::K_F3 => (KeyCode::F3, Some(NamedKey::F3)),
        orbclient::K_F4 => (KeyCode::F4, Some(NamedKey::F4)),
        orbclient::K_F5 => (KeyCode::F5, Some(NamedKey::F5)),
        orbclient::K_F6 => (KeyCode::F6, Some(NamedKey::F6)),
        orbclient::K_F7 => (KeyCode::F7, Some(NamedKey::F7)),
        orbclient::K_F8 => (KeyCode::F8, Some(NamedKey::F8)),
        orbclient::K_F9 => (KeyCode::F9, Some(NamedKey::F9)),
        orbclient::K_F10 => (KeyCode::F10, Some(NamedKey::F10)),
        orbclient::K_F11 => (KeyCode::F11, Some(NamedKey::F11)),
        orbclient::K_F12 => (KeyCode::F12, Some(NamedKey::F12)),
        orbclient::K_HOME => (KeyCode::Home, Some(NamedKey::Home)),
        orbclient::K_LEFT => (KeyCode::ArrowLeft, Some(NamedKey::ArrowLeft)),
        orbclient::K_LEFT_SHIFT => (KeyCode::ShiftLeft, Some(NamedKey::Shift)),
        orbclient::K_MINUS => (KeyCode::Minus, None),

        orbclient::K_NUM_0 => (KeyCode::Numpad0, None),
        orbclient::K_NUM_1 => (KeyCode::Numpad1, None),
        orbclient::K_NUM_2 => (KeyCode::Numpad2, None),
        orbclient::K_NUM_3 => (KeyCode::Numpad3, None),
        orbclient::K_NUM_4 => (KeyCode::Numpad4, None),
        orbclient::K_NUM_5 => (KeyCode::Numpad5, None),
        orbclient::K_NUM_6 => (KeyCode::Numpad6, None),
        orbclient::K_NUM_7 => (KeyCode::Numpad7, None),
        orbclient::K_NUM_8 => (KeyCode::Numpad8, None),
        orbclient::K_NUM_9 => (KeyCode::Numpad9, None),
        orbclient::K_NUM_ASTERISK => (KeyCode::NumpadMultiply, None),
        orbclient::K_NUM_ENTER => (KeyCode::NumpadEnter, Some(NamedKey::Enter)),
        orbclient::K_NUM_MINUS => (KeyCode::NumpadSubtract, None),
        orbclient::K_NUM_PLUS => (KeyCode::NumpadAdd, None),
        orbclient::K_NUM_SLASH => (KeyCode::NumpadDivide, None),
        orbclient::K_NUM_PERIOD => (KeyCode::NumpadDecimal, None),

        orbclient::K_PERIOD => (KeyCode::Period, None),
        orbclient::K_PGDN => (KeyCode::PageDown, Some(NamedKey::PageDown)),
        orbclient::K_PGUP => (KeyCode::PageUp, Some(NamedKey::PageUp)),
        orbclient::K_QUOTE => (KeyCode::Quote, None),
        orbclient::K_RIGHT => (KeyCode::ArrowRight, Some(NamedKey::ArrowRight)),
        orbclient::K_RIGHT_SHIFT => (KeyCode::ShiftRight, Some(NamedKey::Shift)),
        orbclient::K_RIGHT_SUPER => (KeyCode::MetaRight, Some(NamedKey::Meta)),
        orbclient::K_SEMICOLON => (KeyCode::Semicolon, None),
        orbclient::K_SLASH => (KeyCode::Slash, None),
        orbclient::K_SPACE => (KeyCode::Space, None),
        orbclient::K_SUPER => (KeyCode::MetaLeft, Some(NamedKey::Meta)),
        orbclient::K_TAB => (KeyCode::Tab, Some(NamedKey::Tab)),
        orbclient::K_TICK => (KeyCode::Backquote, None),
        orbclient::K_UP => (KeyCode::ArrowUp, Some(NamedKey::ArrowUp)),
        orbclient::K_VOLUME_DOWN => (KeyCode::AudioVolumeDown, Some(NamedKey::AudioVolumeDown)),
        orbclient::K_VOLUME_TOGGLE => (KeyCode::AudioVolumeMute, Some(NamedKey::AudioVolumeMute)),
        orbclient::K_VOLUME_UP => (KeyCode::AudioVolumeUp, Some(NamedKey::AudioVolumeUp)),

        orbclient::K_INS => (KeyCode::Insert, Some(NamedKey::Insert)),
        orbclient::K_PRTSC => (KeyCode::PrintScreen, Some(NamedKey::PrintScreen)),
        orbclient::K_NUM => (KeyCode::NumLock, Some(NamedKey::NumLock)),
        orbclient::K_SCROLL => (KeyCode::ScrollLock, Some(NamedKey::ScrollLock)),
        orbclient::K_APP => (KeyCode::ContextMenu, Some(NamedKey::ContextMenu)),

        orbclient::K_MEDIA_FAST_FORWARD => {
            (KeyCode::MediaFastForward, Some(NamedKey::MediaFastForward))
        },
        orbclient::K_MEDIA_REWIND => (KeyCode::MediaRewind, Some(NamedKey::MediaRewind)),
        orbclient::K_MEDIA_STOP => (KeyCode::MediaStop, Some(NamedKey::MediaStop)),

        orbclient::K_POWER => (KeyCode::Power, Some(NamedKey::Power)),

        _ => return (PhysicalKey::Unidentified(NativeKeyCode::Unidentified), None),
    };
    (PhysicalKey::Code(key_code), named_key_opt)
}

pub fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
    convert_scancode(scancode.try_into().unwrap_or_default()).0
}

pub fn physicalkey_to_scancode(_physical_key: PhysicalKey) -> Option<u32> {
    // TODO
    None
}

fn element_state(pressed: bool) -> event::ElementState {
    if pressed { event::ElementState::Pressed } else { event::ElementState::Released }
}

bitflags! {
    #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
    struct KeyboardModifierState: u8 {
        const LSHIFT = 1 << 0;
        const RSHIFT = 1 << 1;
        const LCTRL = 1 << 2;
        const RCTRL = 1 << 3;
        const LALT = 1 << 4;
        const RALT = 1 << 5;
        const LMETA = 1 << 6;
        const RMETA = 1 << 7;
    }
}

bitflags! {
    #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
    struct MouseButtonState: u8 {
        const LEFT = 1 << 0;
        const MIDDLE = 1 << 1;
        const RIGHT = 1 << 2;
    }
}

#[derive(Default, Debug)]
struct EventState {
    keyboard: KeyboardModifierState,
    mouse: MouseButtonState,
    mouse_pos: (i32, i32),
    resize_opt: Option<(u32, u32)>,
    text_input_event: Option<TextInputEvent>,
}

impl EventState {
    fn character_all_modifiers(&self, character: char) -> char {
        // Modify character if Ctrl is pressed
        #[allow(clippy::collapsible_if)]
        if self.keyboard.contains(KeyboardModifierState::LCTRL)
            || self.keyboard.contains(KeyboardModifierState::RCTRL)
        {
            if character.is_ascii_lowercase() {
                return ((character as u8 - b'a') + 1) as char;
            }
            // TODO: more control key variants?
        }

        // Return character as-is if no special handling required
        character
    }

    fn key(&mut self, key: PhysicalKey, pressed: bool) {
        let code = match key {
            PhysicalKey::Code(code) => code,
            _ => return,
        };

        match code {
            KeyCode::ShiftLeft => self.keyboard.set(KeyboardModifierState::LSHIFT, pressed),
            KeyCode::ShiftRight => self.keyboard.set(KeyboardModifierState::RSHIFT, pressed),
            KeyCode::ControlLeft => self.keyboard.set(KeyboardModifierState::LCTRL, pressed),
            KeyCode::ControlRight => self.keyboard.set(KeyboardModifierState::RCTRL, pressed),
            KeyCode::AltLeft => self.keyboard.set(KeyboardModifierState::LALT, pressed),
            KeyCode::AltRight => self.keyboard.set(KeyboardModifierState::RALT, pressed),
            KeyCode::MetaLeft => self.keyboard.set(KeyboardModifierState::LMETA, pressed),
            KeyCode::MetaRight => self.keyboard.set(KeyboardModifierState::RMETA, pressed),
            _ => (),
        }
    }

    fn mouse(
        &mut self,
        left: bool,
        middle: bool,
        right: bool,
    ) -> Option<(event::MouseButton, event::ElementState)> {
        if self.mouse.contains(MouseButtonState::LEFT) != left {
            self.mouse.set(MouseButtonState::LEFT, left);
            return Some((event::MouseButton::Left, element_state(left)));
        }

        if self.mouse.contains(MouseButtonState::MIDDLE) != middle {
            self.mouse.set(MouseButtonState::MIDDLE, middle);
            return Some((event::MouseButton::Middle, element_state(middle)));
        }

        if self.mouse.contains(MouseButtonState::RIGHT) != right {
            self.mouse.set(MouseButtonState::RIGHT, right);
            return Some((event::MouseButton::Right, element_state(right)));
        }

        None
    }

    fn modifiers(&self) -> Modifiers {
        let mut state = ModifiersState::empty();
        let mut pressed_mods = ModifiersKeys::empty();

        if self.keyboard.intersects(KeyboardModifierState::LSHIFT | KeyboardModifierState::RSHIFT) {
            state |= ModifiersState::SHIFT;
        }

        pressed_mods
            .set(ModifiersKeys::LSHIFT, self.keyboard.contains(KeyboardModifierState::LSHIFT));
        pressed_mods
            .set(ModifiersKeys::RSHIFT, self.keyboard.contains(KeyboardModifierState::RSHIFT));

        if self.keyboard.intersects(KeyboardModifierState::LCTRL | KeyboardModifierState::RCTRL) {
            state |= ModifiersState::CONTROL;
        }

        pressed_mods
            .set(ModifiersKeys::LCONTROL, self.keyboard.contains(KeyboardModifierState::LCTRL));
        pressed_mods
            .set(ModifiersKeys::RCONTROL, self.keyboard.contains(KeyboardModifierState::RCTRL));

        if self.keyboard.intersects(KeyboardModifierState::LALT | KeyboardModifierState::RALT) {
            state |= ModifiersState::ALT;
        }

        pressed_mods.set(ModifiersKeys::LALT, self.keyboard.contains(KeyboardModifierState::LALT));
        pressed_mods.set(ModifiersKeys::RALT, self.keyboard.contains(KeyboardModifierState::RALT));

        if self.keyboard.intersects(KeyboardModifierState::LMETA | KeyboardModifierState::RMETA) {
            state |= ModifiersState::META
        }

        pressed_mods
            .set(ModifiersKeys::LMETA, self.keyboard.contains(KeyboardModifierState::LMETA));
        pressed_mods
            .set(ModifiersKeys::RMETA, self.keyboard.contains(KeyboardModifierState::RMETA));

        Modifiers::new(state, pressed_mods)
    }
}

#[derive(Debug)]
pub struct EventLoop {
    /// Has `run` or `run_on_demand` been called or a call to `pump_events` that starts the loop
    loop_running: bool,
    windows: Vec<(Arc<RedoxSocket>, EventState)>,
    window_target: ActiveEventLoop,
    user_events_receiver: mpsc::Receiver<()>,
}

impl EventLoop {
    pub fn new(_: &PlatformSpecificEventLoopAttributes) -> Result<Self, EventLoopError> {
        static EVENT_LOOP_CREATED: AtomicBool = AtomicBool::new(false);
        if EVENT_LOOP_CREATED.swap(true, Ordering::Relaxed) {
            // For better cross-platformness.
            return Err(EventLoopError::RecreationAttempt);
        }

        // NOTE: Create a channel which can hold only one event to automatically _squash_ user
        // events.
        let (user_events_sender, user_events_receiver) = mpsc::sync_channel(1);

        let event_socket =
            Arc::new(EventQueue::new().map_err(|error| os_error!(format!("{error}")))?);

        let wake_socket = TimeSocket::open().map_err(|error| os_error!(format!("{error}")))?;

        event_socket
            .subscribe(wake_socket.0.fd(), EventSource::Time, EventFlags::READ)
            .map_err(|error| os_error!(format!("{error}")))?;

        Ok(Self {
            loop_running: false,
            windows: Vec::new(),
            window_target: ActiveEventLoop {
                control_flow: Cell::new(ControlFlow::default()),
                exit: Cell::new(false),
                creates: Mutex::new(VecDeque::new()),
                redraws: Arc::new(Mutex::new(VecDeque::new())),
                destroys: Arc::new(Mutex::new(VecDeque::new())),
                event_socket,
                event_loop_proxy: Arc::new(EventLoopProxy { wake_socket, user_events_sender }),
            },
            user_events_receiver,
        })
    }

    fn process_event<A: ApplicationHandler>(
        window_id: WindowId,
        event_option: EventOption,
        event_state: &mut EventState,
        window_target: &ActiveEventLoop,
        app: &mut A,
    ) {
        let text_input_event = event_state.text_input_event.take();
        if text_input_event.is_some() && !matches!(event_option, EventOption::Key(_)) {
            tracing::warn!("got TextInput event without following Key event");
        }

        match event_option {
            EventOption::Key(KeyEvent { character: _, scancode, pressed }) => {
                // Convert scancode
                let (physical_key, named_key_opt) = convert_scancode(scancode);

                // Get previous modifiers and update modifiers based on physical key
                let modifiers_before = event_state.keyboard;
                event_state.key(physical_key, pressed);

                // Default to unidentified key with no text
                let mut logical_key = Key::Unidentified(NativeKey::Unidentified);
                let mut key_without_modifiers = logical_key.clone();
                let mut text = None;
                let mut text_with_all_modifiers = None;

                // Set key and text based on character
                let character = text_input_event.map_or('\0', |event| event.character);
                if character != '\0' {
                    let mut tmp = [0u8; 4];
                    let character_str = character.encode_utf8(&mut tmp);
                    // The key with Shift and Caps Lock applied (but not Ctrl)
                    logical_key = Key::Character(character_str.into());
                    // The key without Shift or Caps Lock applied
                    key_without_modifiers =
                        Key::Character(SmolStr::from_iter(character.to_lowercase()));
                    if pressed {
                        // The key with Shift and Caps Lock applied (but not Ctrl)
                        text = Some(character_str.into());
                        // The key with Shift, Caps Lock, and Ctrl applied
                        let character_all_modifiers =
                            event_state.character_all_modifiers(character);
                        text_with_all_modifiers =
                            Some(character_all_modifiers.encode_utf8(&mut tmp).into())
                    }
                };

                // Override key if a named key was found (this is to allow Enter to replace '\n')
                if let Some(named_key) = named_key_opt {
                    logical_key = Key::Named(named_key);
                    key_without_modifiers = logical_key.clone();
                }

                let event = event::WindowEvent::KeyboardInput {
                    device_id: None,
                    event: event::KeyEvent {
                        logical_key,
                        physical_key,
                        location: KeyLocation::Standard,
                        state: element_state(pressed),
                        repeat: false,
                        text,
                        key_without_modifiers,
                        text_with_all_modifiers,
                    },
                    is_synthetic: false,
                };

                app.window_event(window_target, window_id, event);

                // If the state of the modifiers has changed, send the event.
                if modifiers_before != event_state.keyboard {
                    app.window_event(
                        window_target,
                        window_id,
                        event::WindowEvent::ModifiersChanged(event_state.modifiers()),
                    );
                }
            },
            EventOption::TextInput(event) => {
                event_state.text_input_event = Some(event);
            },
            EventOption::Mouse(MouseEvent { x, y }) => {
                event_state.mouse_pos = (x, y);
                app.window_event(window_target, window_id, event::WindowEvent::PointerMoved {
                    device_id: None,
                    primary: true,
                    position: event_state.mouse_pos.into(),
                    source: event::PointerSource::Mouse,
                });
            },
            EventOption::MouseRelative(MouseRelativeEvent { dx, dy }) => {
                app.device_event(window_target, None, event::DeviceEvent::PointerMotion {
                    delta: (dx as f64, dy as f64),
                });
            },
            EventOption::Button(ButtonEvent { left, middle, right }) => {
                while let Some((button, state)) = event_state.mouse(left, middle, right) {
                    app.window_event(window_target, window_id, event::WindowEvent::PointerButton {
                        device_id: None,
                        primary: true,
                        state,
                        position: event_state.mouse_pos.into(),
                        button: button.into(),
                        is_macos_activation_click: false,
                    });
                }
            },
            EventOption::Scroll(ScrollEvent { x, y }) => {
                app.window_event(window_target, window_id, event::WindowEvent::MouseWheel {
                    device_id: None,
                    delta: event::MouseScrollDelta::LineDelta(x as f32, y as f32),
                    phase: event::TouchPhase::Moved,
                });
            },
            EventOption::Quit(QuitEvent {}) => {
                app.window_event(window_target, window_id, event::WindowEvent::CloseRequested);
            },
            EventOption::Focus(FocusEvent { focused }) => {
                app.window_event(window_target, window_id, event::WindowEvent::Focused(focused));
            },
            EventOption::Move(MoveEvent { x, y }) => {
                app.window_event(
                    window_target,
                    window_id,
                    event::WindowEvent::Moved((x, y).into()),
                );
            },
            EventOption::Resize(ResizeEvent { width, height }) => {
                app.window_event(
                    window_target,
                    window_id,
                    event::WindowEvent::SurfaceResized((width, height).into()),
                );

                // Acknowledge resize after event loop.
                event_state.resize_opt = Some((width, height));
            },
            // TODO: Screen, Clipboard, Drop
            EventOption::Hover(HoverEvent { entered }) => {
                let event = if entered {
                    event::WindowEvent::PointerEntered {
                        device_id: None,
                        primary: true,
                        position: dpi::PhysicalPosition::default(),
                        kind: event::PointerKind::Mouse,
                    }
                } else {
                    event::WindowEvent::PointerLeft {
                        device_id: None,
                        primary: true,
                        position: None,
                        kind: event::PointerKind::Mouse,
                    }
                };

                app.window_event(window_target, window_id, event);
            },
            other => {
                tracing::warn!("unhandled event: {:?}", other);
            },
        }
    }

    pub fn run_app_on_demand<A: ApplicationHandler>(
        &mut self,
        mut app: A,
    ) -> Result<(), EventLoopError> {
        self.window_target.exit.set(false);
        let res = loop {
            match self.pump_app_events(None, &mut app) {
                PumpStatus::Exit(0) => {
                    break Ok(());
                },
                PumpStatus::Exit(code) => {
                    break Err(EventLoopError::ExitFailure(code));
                },
                _ => {
                    continue;
                },
            }
        };

        drop(app);

        // Handle window destroys that happened when dropping the app.
        while let Some(destroy_id) = {
            let mut destroys = self.window_target.destroys.lock().unwrap();
            destroys.pop_front()
        } {
            self.windows
                .retain(|(window, _event_state)| WindowId::from_raw(window.fd()) != destroy_id);
        }

        res
    }

    fn single_iteration<A: ApplicationHandler>(&mut self, app: &mut A, cause: StartCause) {
        // TODO: Unindent
        {
            app.new_events(&self.window_target, cause);

            if cause == StartCause::Init {
                // NB: For consistency all platforms must call `can_create_surfaces`
                app.can_create_surfaces(&self.window_target);
            }

            // Handle window creates.
            while let Some(window) = {
                let mut creates = self.window_target.creates.lock().unwrap();
                creates.pop_front()
            } {
                let window_id = WindowId::from_raw(window.fd());

                let mut buf: [u8; 4096] = [0; 4096];
                let path = window.fpath(&mut buf).expect("failed to read properties");
                let properties = WindowProperties::new(path);

                self.windows.push((window, EventState::default()));

                // Send resize event on create to indicate first size.
                let event = event::WindowEvent::SurfaceResized((properties.w, properties.h).into());
                app.window_event(&self.window_target, window_id, event);

                // Send moved event on create to indicate first position.
                let event = event::WindowEvent::Moved((properties.x, properties.y).into());
                app.window_event(&self.window_target, window_id, event);
            }

            // Handle window destroys.
            while let Some(destroy_id) = {
                let mut destroys = self.window_target.destroys.lock().unwrap();
                destroys.pop_front()
            } {
                app.window_event(&self.window_target, destroy_id, event::WindowEvent::Destroyed);
                self.windows
                    .retain(|(window, _event_state)| WindowId::from_raw(window.fd()) != destroy_id);
            }

            // Handle window events.
            let mut i = 0;
            // While loop is used here because the same window may be processed more than once.
            while let Some((window, event_state)) = self.windows.get_mut(i) {
                let window_id = WindowId::from_raw(window.fd());

                let mut event_buf = [0u8; 16 * mem::size_of::<orbclient::Event>()];
                let count = libredox::call::read(window.fd(), &mut event_buf)
                    .expect("failed to read window events");
                // Safety: orbclient::Event is a packed struct designed to be transferred over a
                // socket.
                let events = unsafe {
                    slice::from_raw_parts(
                        event_buf.as_ptr() as *const orbclient::Event,
                        count / mem::size_of::<orbclient::Event>(),
                    )
                };

                for orbital_event in events {
                    Self::process_event(
                        window_id,
                        orbital_event.to_option(),
                        event_state,
                        &self.window_target,
                        app,
                    );
                }

                if count == event_buf.len() {
                    // If event buf was full, process same window again to ensure all events are
                    // drained.
                    continue;
                }

                // Acknowledge the latest resize event.
                if let Some((w, h)) = event_state.resize_opt.take() {
                    window
                        .write(format!("S,{w},{h}").as_bytes())
                        .expect("failed to acknowledge resize");

                    // Require redraw after resize.
                    let mut redraws = self.window_target.redraws.lock().unwrap();
                    if !redraws.contains(&window_id) {
                        redraws.push_back(window_id);
                    }
                }

                // Move to next window.
                i += 1;
            }

            while self.user_events_receiver.try_recv().is_ok() {
                app.proxy_wake_up(&self.window_target);
            }

            // To avoid deadlocks the redraws lock is not held during event processing.
            while let Some(window_id) = {
                let mut redraws = self.window_target.redraws.lock().unwrap();
                redraws.pop_front()
            } {
                app.window_event(
                    &self.window_target,
                    window_id,
                    event::WindowEvent::RedrawRequested,
                );
            }

            app.about_to_wait(&self.window_target);
        }
    }

    pub fn pump_app_events<A: ApplicationHandler>(
        &mut self,
        timeout: Option<Duration>,
        mut app: A,
    ) -> PumpStatus {
        if !self.loop_running {
            self.loop_running = true;

            // Run the initial loop iteration.
            self.single_iteration(&mut app, StartCause::Init);
        }

        if self.window_target.exit.get() {
            self.loop_running = false;
            // TODO: other exit codes
            return PumpStatus::Exit(0);
        }

        let start = Instant::now();
        let timeout = {
            let requested_resume = match self.window_target.control_flow() {
                ControlFlow::Poll => Some(Duration::ZERO),
                ControlFlow::Wait => None,
                ControlFlow::WaitUntil(instant) => Some(instant.saturating_duration_since(start)),
            };
            min_timeout(timeout, requested_resume)
        };

        if let Some(timeout) = timeout {
            self.window_target
                .event_socket
                .subscribe(
                    EVENT_TIMEOUT_ID,
                    UserData::from_user_data(timeout.as_millis() as usize),
                    EventFlags::READ,
                )
                .expect("failed to register EVENT_TIMEOUT_ID")
        }

        // Wait for event if needed.
        let event = loop {
            match self.window_target.event_socket.next_event() {
                Ok(event) => break event,
                Err(err) if err.is_interrupt() => continue,
                Err(err) => {
                    panic!("failed to read event: {err}");
                },
            }
        };

        if timeout.is_some() && event.fd == EVENT_TIMEOUT_ID {
            // NB: EVENT_TIMEOUT_ID is not a regular event ID.
            // Here nothing is happened yet.
            return PumpStatus::Continue;
        }

        // Normal window event or spurious timeout.
        let cause = match self.window_target.control_flow() {
            ControlFlow::Poll => StartCause::Poll,
            ControlFlow::Wait => StartCause::WaitCancelled { start, requested_resume: None },
            ControlFlow::WaitUntil(deadline) => {
                if Instant::now() < deadline {
                    StartCause::WaitCancelled { start, requested_resume: Some(deadline) }
                } else {
                    StartCause::ResumeTimeReached { start, requested_resume: deadline }
                }
            },
        };

        // Do actual event processing
        self.single_iteration(&mut app, cause);

        PumpStatus::Continue
    }

    pub fn window_target(&self) -> &dyn RootActiveEventLoop {
        &self.window_target
    }
}

impl EventLoopProvider for EventLoop {
    fn run_app<A: ApplicationHandler + 'static>(
        mut self,
        mut app: A,
    ) -> Result<(), EventLoopError> {
        let result = self.run_app_on_demand(&mut app);
        // SAFETY: unsure that the state is dropped before the exit from the event loop.
        drop(app);
        result
    }

    fn create_proxy(&self) -> CoreEventLoopProxy {
        self.window_target().create_proxy()
    }

    fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
        self.window_target().owned_display_handle()
    }

    fn listen_device_events(&self, allowed: DeviceEvents) {
        self.window_target().listen_device_events(allowed);
    }

    fn set_control_flow(&self, control_flow: ControlFlow) {
        self.window_target().set_control_flow(control_flow);
    }

    fn create_custom_cursor(
        &self,
        custom_cursor: CustomCursorSource,
    ) -> Result<CustomCursor, RequestError> {
        self.window_target().create_custom_cursor(custom_cursor)
    }
}

#[derive(Debug)]
pub struct EventLoopProxy {
    user_events_sender: mpsc::SyncSender<()>,
    pub(super) wake_socket: TimeSocket,
}

impl EventLoopProxyProvider for EventLoopProxy {
    fn wake_up(&self) {
        // When we fail to send the event it means that we haven't woken up to read the previous
        // event.
        if self.user_events_sender.try_send(()).is_ok() {
            self.wake_socket.wake().unwrap();
        }
    }
}

impl Unpin for EventLoopProxy {}

redox_event::user_data! {
    pub enum EventSource {
        Orbital,
        Time,
    }
}

#[derive(Debug)]
pub struct ActiveEventLoop {
    control_flow: Cell<ControlFlow>,
    exit: Cell<bool>,
    pub(super) creates: Mutex<VecDeque<Arc<RedoxSocket>>>,
    pub(super) redraws: Arc<Mutex<VecDeque<WindowId>>>,
    pub(super) destroys: Arc<Mutex<VecDeque<WindowId>>>,
    pub(super) event_socket: Arc<EventQueue<EventSource>>,
    pub(super) event_loop_proxy: Arc<EventLoopProxy>,
}

impl RootActiveEventLoop for ActiveEventLoop {
    fn create_proxy(&self) -> CoreEventLoopProxy {
        CoreEventLoopProxy::new(self.event_loop_proxy.clone())
    }

    fn create_window(
        &self,
        window_attributes: winit_core::window::WindowAttributes,
    ) -> Result<Box<dyn CoreWindow>, RequestError> {
        Ok(Box::new(Window::new(self, window_attributes)?))
    }

    fn create_custom_cursor(&self, _: CustomCursorSource) -> Result<CustomCursor, RequestError> {
        Err(NotSupportedError::new("create_custom_cursor is not supported").into())
    }

    fn available_monitors(&self) -> Box<dyn Iterator<Item = winit_core::monitor::MonitorHandle>> {
        Box::new(iter::empty())
    }

    fn system_theme(&self) -> Option<Theme> {
        None
    }

    fn primary_monitor(&self) -> Option<winit_core::monitor::MonitorHandle> {
        None
    }

    fn listen_device_events(&self, _allowed: DeviceEvents) {}

    fn set_control_flow(&self, control_flow: ControlFlow) {
        self.control_flow.set(control_flow)
    }

    fn control_flow(&self) -> ControlFlow {
        self.control_flow.get()
    }

    fn exit(&self) {
        self.exit.set(true);
    }

    fn exiting(&self) -> bool {
        self.exit.get()
    }

    fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
        CoreOwnedDisplayHandle::new(Arc::new(OwnedDisplayHandle))
    }

    fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
        self
    }
}

impl rwh_06::HasDisplayHandle for ActiveEventLoop {
    fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
        let raw = rwh_06::RawDisplayHandle::Orbital(rwh_06::OrbitalDisplayHandle::new());
        unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
    }
}

#[derive(Clone)]
pub(crate) struct OwnedDisplayHandle;

impl rwh_06::HasDisplayHandle for OwnedDisplayHandle {
    fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
        let raw = rwh_06::RawDisplayHandle::Orbital(rwh_06::OrbitalDisplayHandle::new());
        unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
    }
}

#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct PlatformSpecificEventLoopAttributes {}

/// Returns the minimum `Option<Duration>`, taking into account that `None`
/// equates to an infinite timeout, not a zero timeout (so can't just use
/// `Option::min`)
fn min_timeout(a: Option<Duration>, b: Option<Duration>) -> Option<Duration> {
    a.map_or(b, |a_timeout| b.map_or(Some(a_timeout), |b_timeout| Some(a_timeout.min(b_timeout))))
}
