//! Demonstrates automatic directional navigation with manual navigation overrides.
//!
//! This example shows how to leverage both automatic navigation and manual overrides to create
//! a desired user navigation experience without much boilerplate code. In this example, there are
//! multiple pages of UI Buttons that depict different scenarios in which both automatic and manual
//! navigation are leveraged to produce a desired navigation experience.
//!
//! Manual overrides can be used to define navigation in any situation where automatic
//! navigation fails to create an edge due to lack of proximity. For example, when creating
//! navigation that loops around to an opposite side, manual overrides should be used to define
//! this behavior. If one input is too far away from the others and `AutoNavigationConfig`
//! cannot be tweaked, manual overrides can connect that input to the others. Manual navigation
//! can also be used to override any undesired automatic navigation.
//!
//! The [`AutoDirectionalNavigation`] component is used to create basic, intuitive navigation to UI
//! elements within a page. Manual navigation edges are added to the [`DirectionalNavigationMap`]
//! to create special navigation rules. The [`AutoDirectionalNavigator`] system parameter navigates
//! using manual navigation rules/overrides first and automatic navigation second.

use core::time::Duration;

use bevy::{
    camera::NormalizedRenderTarget,
    input_focus::{
        directional_navigation::{
            AutoNavigationConfig, DirectionalNavigationMap, DirectionalNavigationPlugin,
        },
        FocusCause, InputFocus, InputFocusVisible,
    },
    math::{CompassOctant, Dir2},
    picking::{
        backend::HitData,
        pointer::{Location, PointerId},
    },
    platform::collections::HashSet,
    prelude::*,
    ui::auto_directional_navigation::{AutoDirectionalNavigation, AutoDirectionalNavigator},
};

fn main() {
    App::new()
        // Input focus is not enabled by default, so we need to add the corresponding plugins
        // The navigation system's resources are initialized by the DirectionalNavigationPlugin.
        .add_plugins((DefaultPlugins, DirectionalNavigationPlugin))
        // This resource is canonically used to track whether or not to render a focus indicator
        // It starts as false, but we set it to true here as we would like to see the focus indicator
        .insert_resource(InputFocusVisible(true))
        // Configure auto-navigation behavior
        .insert_resource(AutoNavigationConfig {
            // Require at least 10% overlap in perpendicular axis for cardinal directions
            min_alignment_factor: 0.1,
            // Don't connect nodes more than 200 pixels apart between their closest edges
            max_search_distance: Some(200.0),
            // Prefer nodes that are well-aligned
            prefer_aligned: true,
        })
        .init_resource::<ActionState>()
        // For automatic navigation, UI entities will have the component `AutoDirectionalNavigation`
        // and will be automatically connected by the navigation system.
        // We will also add some new edges that the automatic navigation system
        // cannot create by itself by inserting them into `DirectionalNavigationMap`
        .add_systems(Startup, setup_paged_ui)
        // Input is generally handled during PreUpdate
        .add_systems(PreUpdate, (process_inputs, navigate).chain())
        .add_systems(
            Update,
            (
                highlight_focused_element,
                interact_with_focused_button,
                reset_button_after_interaction,
                update_focus_display
                    .run_if(|input_focus: Res<InputFocus>| input_focus.is_changed()),
                update_key_display,
            ),
        )
        .add_observer(universal_button_click_behavior)
        .run();
}

const PAGE_1_NORMAL_BUTTON: Srgba = bevy::color::palettes::tailwind::BLUE_400;
const PAGE_1_PRESSED_BUTTON: Srgba = bevy::color::palettes::tailwind::BLUE_500;
const PAGE_1_FOCUSED_BORDER: Srgba = bevy::color::palettes::tailwind::BLUE_50;

const PAGE_2_NORMAL_BUTTON: Srgba = bevy::color::palettes::tailwind::RED_400;
const PAGE_2_PRESSED_BUTTON: Srgba = bevy::color::palettes::tailwind::RED_500;
const PAGE_2_FOCUSED_BORDER: Srgba = bevy::color::palettes::tailwind::RED_50;

const PAGE_3_NORMAL_BUTTON: Srgba = bevy::color::palettes::tailwind::GREEN_400;
const PAGE_3_PRESSED_BUTTON: Srgba = bevy::color::palettes::tailwind::GREEN_500;
const PAGE_3_FOCUSED_BORDER: Srgba = bevy::color::palettes::tailwind::GREEN_50;

const NORMAL_BUTTON_COLORS: [Srgba; 3] = [
    PAGE_1_NORMAL_BUTTON,
    PAGE_2_NORMAL_BUTTON,
    PAGE_3_NORMAL_BUTTON,
];
const PRESSED_BUTTON_COLORS: [Srgba; 3] = [
    PAGE_1_PRESSED_BUTTON,
    PAGE_2_PRESSED_BUTTON,
    PAGE_3_PRESSED_BUTTON,
];
const FOCUSED_BORDER_COLORS: [Srgba; 3] = [
    PAGE_1_FOCUSED_BORDER,
    PAGE_2_FOCUSED_BORDER,
    PAGE_3_FOCUSED_BORDER,
];

/// Marker component for the text that displays the currently focused button
#[derive(Component)]
struct FocusDisplay;

/// Marker component for the text that displays the last key pressed
#[derive(Component)]
struct KeyDisplay;

/// Component that stores which page a button is on
#[derive(Component)]
struct Page(usize);

// Observer for button clicks
fn universal_button_click_behavior(
    mut click: On<Pointer<Click>>,
    mut button_query: Query<(&mut BackgroundColor, &Page, &mut ResetTimer)>,
) {
    let button_entity = click.entity;
    if let Ok((mut color, page, mut reset_timer)) = button_query.get_mut(button_entity) {
        color.0 = PRESSED_BUTTON_COLORS[page.0].into();
        reset_timer.0 = Timer::from_seconds(0.3, TimerMode::Once);
        click.propagate(false);
    }
}

#[derive(Component, Default, Deref, DerefMut)]
struct ResetTimer(Timer);

fn reset_button_after_interaction(
    time: Res<Time>,
    mut query: Query<(&mut ResetTimer, &mut BackgroundColor, &Page)>,
) {
    for (mut reset_timer, mut color, page) in query.iter_mut() {
        reset_timer.tick(time.delta());
        if reset_timer.just_finished() {
            color.0 = NORMAL_BUTTON_COLORS[page.0].into();
        }
    }
}

/// Spawn pages of buttons to demonstrate automatic and manual navigation.
///
/// This function creates three pages of buttons. All buttons have automatic navigation
/// enabled by having the [`AutoDirectionalNavigation`] component.
/// Manual navigation is specified with the [`DirectionalNavigationMap`].
/// Page 1 has a simple grid of buttons where transitions between rows is defined using
/// the [`DirectionalNavigationMap`].
/// Page 2 has a cluster of buttons to the top left and a lonely button on the bottom right.
/// Navigation between the cluster and the lonely button is defined using the
/// [`DirectionalNavigationMap`].
/// Page 3 has the same simple grid of buttons as page 1, but automatic navigation has been
/// overridden in the vertical direction with the [`DirectionalNavigationMap`].
fn setup_paged_ui(
    mut commands: Commands,
    mut manual_directional_nav_map: ResMut<DirectionalNavigationMap>,
    mut input_focus: ResMut<InputFocus>,
) {
    commands.spawn(Camera2d);

    // Create a full-screen background node
    let root_node = commands
        .spawn(Node {
            width: percent(100),
            height: percent(100),
            ..default()
        })
        .id();

    // Instructions
    let instructions = commands
        .spawn((
            Text::new(
                "Directional Navigation Overrides Demo\n\n\
                 Use arrow keys or D-pad to navigate.\n\
                 Press Enter or A button to interact.\n\n\
                 Navigation on each page is a combination of \
                 both automatic and manual navigation.",
            ),
            Node {
                position_type: PositionType::Absolute,
                left: px(20),
                top: px(20),
                width: px(280),
                padding: UiRect::all(px(12)),
                border_radius: BorderRadius::all(px(8)),
                ..default()
            },
            BackgroundColor(Color::srgba(0.1, 0.1, 0.1, 0.8)),
        ))
        .id();
    commands.entity(root_node).add_children(&[instructions]);

    // Focus display - shows which button is currently focused
    commands.spawn((
        Text::new("Focused: None"),
        FocusDisplay,
        Node {
            position_type: PositionType::Absolute,
            left: px(20),
            bottom: px(80),
            width: px(280),
            padding: UiRect::all(px(12)),
            border_radius: BorderRadius::all(px(8)),
            ..default()
        },
        BackgroundColor(Color::srgba(0.1, 0.5, 0.1, 0.8)),
        TextFont {
            font_size: FontSize::Px(20.0),
            ..default()
        },
    ));

    // Key display - shows the last key pressed
    commands.spawn((
        Text::new("Last Key: None"),
        KeyDisplay,
        Node {
            position_type: PositionType::Absolute,
            left: px(20),
            bottom: px(20),
            width: px(280),
            padding: UiRect::all(px(12)),
            border_radius: BorderRadius::all(px(8)),
            ..default()
        },
        BackgroundColor(Color::srgba(0.5, 0.1, 0.5, 0.8)),
        TextFont {
            font_size: FontSize::Px(20.0),
            ..default()
        },
    ));

    // Setup the pages with buttons and helper text
    let mut pages_entities = [
        Vec::with_capacity(12),
        Vec::with_capacity(12),
        Vec::with_capacity(12),
    ];
    let mut text_entities = Vec::with_capacity(10);
    for (page_num, page_button_entities) in pages_entities.iter_mut().enumerate() {
        if page_num == 1 {
            // the second page
            setup_buttons_for_triangle_page(
                &mut commands,
                page_num,
                (page_button_entities, &mut text_entities),
            );
        } else {
            // the first and third pages are regular grids
            setup_buttons_for_grid_page(
                &mut commands,
                page_num,
                (page_button_entities, &mut text_entities),
            );
        }

        // Only the first page is visible at setup.
        let visibility = if page_num == 0 {
            Visibility::Visible
        } else {
            Visibility::Hidden
        };
        let page = commands
            .spawn((
                Node {
                    width: percent(100),
                    height: percent(100),
                    ..default()
                },
                visibility,
            ))
            .id();

        commands
            .entity(page)
            .add_children(page_button_entities)
            .add_children(&text_entities);

        text_entities.clear();
    }

    // For Pages 1 and 3, add manual edges within the grid page for navigation between rows.
    let entity_pairs = [
        // the end of the first row should connect to the beginning of the second
        ((0, 2), (1, 0)),
        // the end of the second row should connect to the beginning of the third
        ((1, 2), (2, 0)),
        // the end of the third row should connect to the beginning of the fourth
        ((2, 2), (3, 0)),
    ];
    for (page_num, page_entities) in pages_entities.iter().enumerate() {
        // Skip Page 2; we are only adding these manual edges for the grid pages.
        if page_num == 1 {
            continue;
        }
        for ((entity_a_row, entity_a_col), (entity_b_row, entity_b_col)) in entity_pairs.iter() {
            manual_directional_nav_map.add_symmetrical_edge(
                page_entities[entity_a_row * 3 + entity_a_col],
                page_entities[entity_b_row * 3 + entity_b_col],
                CompassOctant::East,
            );
        }
    }

    // Add manual edges within the triangle page (Page 2) between buttons 3 and 4.
    // The `AutoNavigationConfig` is set to our desired values, but automatic
    // navigation does not connect Button 3 to Button 4, so we have to add
    // this navigation manually.
    manual_directional_nav_map.add_symmetrical_edge(
        pages_entities[1][2],
        pages_entities[1][3],
        CompassOctant::East,
    );
    manual_directional_nav_map.add_symmetrical_edge(
        pages_entities[1][2],
        pages_entities[1][3],
        CompassOctant::South,
    );
    manual_directional_nav_map.add_symmetrical_edge(
        pages_entities[1][2],
        pages_entities[1][3],
        CompassOctant::SouthEast,
    );
    // Add one-way blocking within the first grid page (Page 1) for down nav.
    for btn in &pages_entities[0] {
        manual_directional_nav_map.block_edge(*btn, CompassOctant::South);
        manual_directional_nav_map.block_edge(*btn, CompassOctant::North);
    }

    // For Page 3, we override the navigation North and South to be inverted.
    let mut col_entities = Vec::with_capacity(4);
    for col in 0..=2 {
        for row in 0..=3 {
            col_entities.push(pages_entities[2][row * 3 + col]);
        }
        manual_directional_nav_map.add_looping_edges(&col_entities, CompassOctant::North);
        col_entities.clear();
    }

    // Add manual edges between pages.
    // When navigating east (right) from the last button of page 1,
    // go to the first button of page 2. This edge is symmetrical.
    manual_directional_nav_map.add_symmetrical_edge(
        pages_entities[0][11],
        pages_entities[1][0],
        CompassOctant::East,
    );
    // When navigating south (down) from the last button of page 2,
    // go to the first button of page 3. This edge is NOT symmetrical.
    // This means going north (up) from the first button of page 3 does
    // NOT go to the last button of page 2.
    manual_directional_nav_map.add_edge(
        pages_entities[1][3],
        pages_entities[2][0],
        CompassOctant::South,
    );
    // When navigating west (left) from the first button of page 3,
    // go back to the last button of page 2. This edge is NOT symmetrical.
    manual_directional_nav_map.add_edge(
        pages_entities[2][0],
        pages_entities[1][3],
        CompassOctant::West,
    );
    // When navigating east (right) from the last button of page 1,
    // go to the first button of page 2. This edge is symmetrical.
    manual_directional_nav_map.add_symmetrical_edge(
        pages_entities[2][11],
        pages_entities[0][0],
        CompassOctant::East,
    );

    // Set initial focus
    input_focus.set(pages_entities[0][0], FocusCause::Navigated);
}

/// Creates the buttons and text for a grid page and places the ids into their
/// respective Vecs in `entities`.
fn setup_buttons_for_grid_page(
    commands: &mut Commands,
    page_num: usize,
    entities: (&mut Vec<Entity>, &mut Vec<Entity>),
) {
    let (page_button_entities, text_entities) = entities;

    // Spawn buttons in a grid
    // Auto-navigation will automatically configure navigation within rows.
    let button_positions = [
        // Row 0
        [(450.0, 80.0), (650.0, 80.0), (850.0, 80.0)],
        // Row 1
        [(450.0, 215.0), (650.0, 215.0), (850.0, 215.0)],
        // Row 2
        [(450.0, 350.0), (650.0, 350.0), (850.0, 350.0)],
        // Row 3
        [(450.0, 485.0), (650.0, 485.0), (850.0, 485.0)],
    ];
    for (i, row) in button_positions.iter().enumerate() {
        for (j, (left, top)) in row.iter().enumerate() {
            let button_entity = spawn_auto_nav_button(
                commands,
                format!("Btn {}-{}", i + 1, j + 1),
                left,
                top,
                page_num,
            );
            page_button_entities.push(button_entity);
        }
    }

    // Text describing current page
    let current_page_entity = spawn_small_text_node(
        commands,
        format!("Currently on Page {}", page_num + 1),
        650,
        20,
        Justify::Center,
    );
    text_entities.push(current_page_entity);

    // Text describing direction to go to the previous page, placed left of the top-left button.
    let previous_page = if page_num == 0 { 3 } else { page_num };
    let previous_page_entity = spawn_small_text_node(
        commands,
        format!("Page {} << ", previous_page),
        310,
        120,
        Justify::Right,
    );
    text_entities.push(previous_page_entity);

    // Text describing direction to go to the next page, placed right of the bottom-right button.
    let next_page_entity = spawn_small_text_node(
        commands,
        format!(">> Page {}", (page_num + 1) % 3 + 1),
        1000,
        525,
        Justify::Left,
    );
    text_entities.push(next_page_entity);

    // Texts describing that moving right wraps to the next row.
    let right_1 = spawn_small_text_node(commands, "> Btn 2-1".into(), 1000, 120, Justify::Left);
    let right_2 = spawn_small_text_node(commands, "> Btn 3-1".into(), 1000, 255, Justify::Left);
    let right_3 = spawn_small_text_node(commands, "> Btn 4-1".into(), 1000, 390, Justify::Left);
    let left_1 = spawn_small_text_node(commands, "Btn 1-3 < ".into(), 310, 255, Justify::Right);
    let left_2 = spawn_small_text_node(commands, "Btn 2-3 < ".into(), 310, 390, Justify::Right);
    let left_3 = spawn_small_text_node(commands, "Btn 3-3 < ".into(), 310, 525, Justify::Right);
    text_entities.push(right_1);
    text_entities.push(right_2);
    text_entities.push(right_3);
    text_entities.push(left_1);
    text_entities.push(left_2);
    text_entities.push(left_3);

    let text = match page_num {
        // For the first page, add a notice about vertical navigation being blocked off
        0 => Text::new(
            "Vertical movements disabled on each button, but you can still navigate between rows by going off the left or right sides."
        ),
        // For the third page, add a notice about vertical navigation being inverted in the grid.
        2 => Text::new(
            "Vertical Navigation has been manually overridden to be inverted! \
            ^ moves down, and v (down) moves up.",
        ),
        _ => Text::default()
    };
    let footer_info = commands
        .spawn((
            text,
            Node {
                position_type: PositionType::Absolute,
                left: px(450),
                top: px(600),
                width: px(540),
                padding: UiRect::all(px(12)),
                ..default()
            },
            TextFont {
                font_size: FontSize::Px(20.0),
                ..default()
            },
        ))
        .id();
    text_entities.push(footer_info);
}

/// Creates the buttons and text for the triangle page (page 2) and places the ids into their
/// respective Vecs in `entities`.
fn setup_buttons_for_triangle_page(
    commands: &mut Commands,
    page_num: usize,
    entities: (&mut Vec<Entity>, &mut Vec<Entity>),
) {
    let button_positions = [
        (450.0, 80.0),   // top left
        (700.0, 80.0),   // top right
        (575.0, 215.0),  // middle
        (1050.0, 350.0), // bottom right
    ];
    let (page_button_entities, text_entities) = entities;
    for (i, (left, top)) in button_positions.iter().enumerate() {
        let button_entity =
            spawn_auto_nav_button(commands, format!("Btn {}", i + 1), left, top, page_num);
        page_button_entities.push(button_entity);
    }

    // Text describing current page
    let current_page_entity = spawn_small_text_node(
        commands,
        format!("Currently on Page {}", page_num + 1),
        650,
        20,
        Justify::Center,
    );
    text_entities.push(current_page_entity);

    // Text describing direction to go to the previous page, placed left of the top-left button.
    let previous_page = if page_num == 0 { 3 } else { page_num };
    let previous_page_entity = spawn_small_text_node(
        commands,
        format!("Page {} << ", previous_page),
        310,
        120,
        Justify::Right,
    );
    text_entities.push(previous_page_entity);

    // Direction to navigate from button 3 to button 4, placed below center button
    let below_button_three_entity =
        spawn_small_text_node(commands, "v\nBtn 4".into(), 575, 325, Justify::Center);
    text_entities.push(below_button_three_entity);

    // Direction to navigate from button 3 to button 4, placed right of center button
    let right_of_button_three_entity =
        spawn_small_text_node(commands, "> Btn 4".into(), 735, 255, Justify::Left);
    text_entities.push(right_of_button_three_entity);

    // Direction to navigate from button 4 to button 3, placed above bottom right button
    let below_button_three_entity =
        spawn_small_text_node(commands, "Btn 3\n^".into(), 1050, 300, Justify::Center);
    text_entities.push(below_button_three_entity);

    // Direction to navigate from button 4 to button 3, placed left of bottom right button
    let right_of_button_three_entity =
        spawn_small_text_node(commands, "Btn 3 < ".into(), 910, 390, Justify::Right);
    text_entities.push(right_of_button_three_entity);

    // Direction to go to the next page, placed bottom of the bottom-right button.
    let next_page_entity = spawn_small_text_node(
        commands,
        format!("V\nV\nPage {}", (page_num + 1) % 3 + 1),
        1050,
        460,
        Justify::Center,
    );
    text_entities.push(next_page_entity);
}

fn spawn_auto_nav_button(
    commands: &mut Commands,
    text: String,
    left: &f64,
    top: &f64,
    page_num: usize,
) -> Entity {
    commands
        .spawn((
            Button,
            Node {
                position_type: PositionType::Absolute,
                left: px(*left),
                top: px(*top),
                width: px(140),
                height: px(100),
                border: UiRect::all(px(4)),
                justify_content: JustifyContent::Center,
                align_items: AlignItems::Center,
                border_radius: BorderRadius::all(px(12)),
                ..default()
            },
            Page(page_num),
            BackgroundColor(NORMAL_BUTTON_COLORS[page_num].into()),
            // Just add this component for automatic navigation
            AutoDirectionalNavigation::default(),
            ResetTimer::default(),
            Name::new(text.clone()),
        ))
        .with_child((
            Text::new(text),
            TextLayout {
                justify: Justify::Center,
                ..default()
            },
        ))
        .id()
}

fn spawn_small_text_node(
    commands: &mut Commands,
    text: String,
    left: i32,
    top: i32,
    justify: Justify,
) -> Entity {
    commands
        .spawn((
            Text::new(text),
            Node {
                position_type: PositionType::Absolute,
                left: px(left),
                top: px(top),
                width: px(140),
                padding: UiRect::all(px(12)),
                ..default()
            },
            TextFont {
                font_size: FontSize::Px(20.0),
                ..default()
            },
            TextLayout {
                justify,
                ..default()
            },
        ))
        .id()
}

// Action state and input handling
#[derive(Debug, PartialEq, Eq, Hash)]
enum DirectionalNavigationAction {
    Up,
    Down,
    Left,
    Right,
    Select,
}

impl DirectionalNavigationAction {
    fn variants() -> Vec<Self> {
        vec![
            DirectionalNavigationAction::Up,
            DirectionalNavigationAction::Down,
            DirectionalNavigationAction::Left,
            DirectionalNavigationAction::Right,
            DirectionalNavigationAction::Select,
        ]
    }

    fn keycode(&self) -> KeyCode {
        match self {
            DirectionalNavigationAction::Up => KeyCode::ArrowUp,
            DirectionalNavigationAction::Down => KeyCode::ArrowDown,
            DirectionalNavigationAction::Left => KeyCode::ArrowLeft,
            DirectionalNavigationAction::Right => KeyCode::ArrowRight,
            DirectionalNavigationAction::Select => KeyCode::Enter,
        }
    }

    fn gamepad_button(&self) -> GamepadButton {
        match self {
            DirectionalNavigationAction::Up => GamepadButton::DPadUp,
            DirectionalNavigationAction::Down => GamepadButton::DPadDown,
            DirectionalNavigationAction::Left => GamepadButton::DPadLeft,
            DirectionalNavigationAction::Right => GamepadButton::DPadRight,
            DirectionalNavigationAction::Select => GamepadButton::South,
        }
    }
}

#[derive(Default, Resource)]
struct ActionState {
    pressed_actions: HashSet<DirectionalNavigationAction>,
}

fn process_inputs(
    mut action_state: ResMut<ActionState>,
    keyboard_input: Res<ButtonInput<KeyCode>>,
    gamepad_input: Query<&Gamepad>,
) {
    action_state.pressed_actions.clear();

    for action in DirectionalNavigationAction::variants() {
        if keyboard_input.just_pressed(action.keycode()) {
            action_state.pressed_actions.insert(action);
        }
    }

    for gamepad in gamepad_input.iter() {
        for action in DirectionalNavigationAction::variants() {
            if gamepad.just_pressed(action.gamepad_button()) {
                action_state.pressed_actions.insert(action);
            }
        }
    }
}

fn navigate(
    action_state: Res<ActionState>,
    parent_query: Query<&ChildOf>,
    mut visibility_query: Query<&mut Visibility>,
    mut auto_directional_navigator: AutoDirectionalNavigator,
) {
    let net_east_west = action_state
        .pressed_actions
        .contains(&DirectionalNavigationAction::Right) as i8
        - action_state
            .pressed_actions
            .contains(&DirectionalNavigationAction::Left) as i8;

    let net_north_south = action_state
        .pressed_actions
        .contains(&DirectionalNavigationAction::Up) as i8
        - action_state
            .pressed_actions
            .contains(&DirectionalNavigationAction::Down) as i8;

    // Use Dir2::from_xy to convert input to direction, then convert to CompassOctant
    let maybe_direction = Dir2::from_xy(net_east_west as f32, net_north_south as f32)
        .ok()
        .map(CompassOctant::from);

    // Store the previous focus in case navigation switches pages.
    let previous_focus = auto_directional_navigator.input_focus();
    if let Some(direction) = maybe_direction {
        match auto_directional_navigator.navigate(direction) {
            Ok(new_focus) => {
                // Successfully navigated!

                // If navigation switches between pages, change the visibilities of pages
                if let Ok(current_child_of) = parent_query.get(new_focus)
                    && let Ok(mut current_page_visibility) =
                        visibility_query.get_mut(current_child_of.parent())
                {
                    *current_page_visibility = Visibility::Visible;

                    if let Some(previous_focus_entity) = previous_focus
                        && let Ok(previous_child_of) = parent_query.get(previous_focus_entity)
                        && previous_child_of.parent() != current_child_of.parent()
                        && let Ok(mut previous_page_visibility) =
                            visibility_query.get_mut(previous_child_of.parent())
                    {
                        *previous_page_visibility = Visibility::Hidden;
                    }
                }
            }
            Err(_e) => {
                // Navigation failed (no neighbor in that direction)
            }
        }
    }
}

fn update_focus_display(
    input_focus: Res<InputFocus>,
    button_query: Query<&Name, With<Button>>,
    mut display_query: Query<&mut Text, With<FocusDisplay>>,
) {
    if let Ok(mut text) = display_query.single_mut() {
        if let Some(focused_entity) = input_focus.get() {
            if let Ok(name) = button_query.get(focused_entity) {
                **text = format!("Focused: {}", name);
            } else {
                **text = "Focused: Unknown".to_string();
            }
        } else {
            **text = "Focused: None".to_string();
        }
    }
}

fn update_key_display(
    keyboard_input: Res<ButtonInput<KeyCode>>,
    gamepad_input: Query<&Gamepad>,
    mut display_query: Query<&mut Text, With<KeyDisplay>>,
) {
    if let Ok(mut text) = display_query.single_mut() {
        // Check for keyboard inputs
        for action in DirectionalNavigationAction::variants() {
            if keyboard_input.just_pressed(action.keycode()) {
                let key_name = match action {
                    DirectionalNavigationAction::Up => "Up Arrow",
                    DirectionalNavigationAction::Down => "Down Arrow",
                    DirectionalNavigationAction::Left => "Left Arrow",
                    DirectionalNavigationAction::Right => "Right Arrow",
                    DirectionalNavigationAction::Select => "Enter",
                };
                **text = format!("Last Key: {}", key_name);
                return;
            }
        }

        // Check for gamepad inputs
        for gamepad in gamepad_input.iter() {
            for action in DirectionalNavigationAction::variants() {
                if gamepad.just_pressed(action.gamepad_button()) {
                    let button_name = match action {
                        DirectionalNavigationAction::Up => "D-Pad Up",
                        DirectionalNavigationAction::Down => "D-Pad Down",
                        DirectionalNavigationAction::Left => "D-Pad Left",
                        DirectionalNavigationAction::Right => "D-Pad Right",
                        DirectionalNavigationAction::Select => "A Button",
                    };
                    **text = format!("Last Key: {}", button_name);
                    return;
                }
            }
        }
    }
}

fn highlight_focused_element(
    input_focus: Res<InputFocus>,
    input_focus_visible: Res<InputFocusVisible>,
    mut query: Query<(Entity, &mut BorderColor, &Page)>,
) {
    for (entity, mut border_color, page) in query.iter_mut() {
        if input_focus.get() == Some(entity) && input_focus_visible.0 {
            *border_color = BorderColor::all(FOCUSED_BORDER_COLORS[page.0]);
        } else {
            *border_color = BorderColor::DEFAULT;
        }
    }
}

fn interact_with_focused_button(
    action_state: Res<ActionState>,
    input_focus: Res<InputFocus>,
    mut commands: Commands,
) {
    if action_state
        .pressed_actions
        .contains(&DirectionalNavigationAction::Select)
        && let Some(focused_entity) = input_focus.get()
    {
        commands.trigger(Pointer::new(
            PointerId::Mouse,
            Location {
                target: NormalizedRenderTarget::None {
                    width: 0,
                    height: 0,
                },
                position: Vec2::ZERO,
            },
            Click {
                button: PointerButton::Primary,
                hit: HitData {
                    camera: Entity::PLACEHOLDER,
                    depth: 0.0,
                    position: None,
                    normal: None,
                    extra: None,
                },
                count: 1,
                duration: Duration::from_secs_f32(0.1),
            },
            focused_entity,
        ));
    }
}
