use super::*;

/// The single thing that currently holds input focus.
///
/// Every path that wants to focus something — a pointer/touch press, an
/// activation request, the task switcher, unminimize, an exclusive
/// layer-shell surface — names one of these and routes through
/// [`Windows::set_focus`](crate::windows::Windows::set_focus). Keyboard
/// focus and the xdg `Activated` state are both derived from it, so
/// "focused" and "has the keyboard" cannot drift apart.
#[derive(Clone, Debug)]
pub enum FocusTarget {
    Layout(Weak<RefCell<Window>>),
    Layer(WlSurface, Option<String>),
    X11(WlSurface),
}

impl FocusTarget {
    /// Resolve to the keyboard surface and app id, or `None` if the target
    /// no longer exists (its window was dropped).
    pub fn resolve(&self) -> Option<(WlSurface, Option<String>)> {
        match self {
            Self::Layout(weak) => weak.upgrade().map(|w| {
                let w = w.borrow();
                (w.surface.surface().clone(), w.app_id.clone())
            }),
            Self::Layer(surface, app_id) => Some((surface.clone(), app_id.clone())),
            Self::X11(surface) => Some((surface.clone(), None)),
        }
    }
}

impl Windows {
    /// Per-frame focus resolver. Returns the keyboard surface (and its app
    /// id), reconciling the xdg `Activated` state along the way so "focused"
    /// and "has the keyboard" never drift apart.
    ///
    /// Two notions feed it: the *baseline* is the active layout's window
    /// ([`Layouts::focus`], updated by layout operations and by clicks on a
    /// toplevel) and is what carries `Activated`; the *overlay* is a focused
    /// layer-shell or X11 surface ([`Self::focus_target`]) that takes the
    /// keyboard over the baseline without de-activating it.
    pub fn focus(&mut self) -> Option<(WlSurface, Option<String>)> {
        if let View::Lock(window) = &self.view {
            return window.as_ref().map(|w| (w.surface().clone(), w.app_id.clone()));
        }

        let baseline = match self.layouts.focus.as_ref().map(Weak::upgrade) {
            Some(Some(window)) => {
                window.borrow_mut().urgent = false;
                Some(window)
            },
            Some(None) => {
                let primary = self.layouts.pending_active().primary().cloned();
                if let Some(w) = &primary {
                    w.borrow_mut().urgent = false;
                }
                self.layouts.focus = primary.as_ref().map(Rc::downgrade);
                primary
            },
            None => None,
        };

        let active_toplevel = baseline.as_ref().map(|w| w.borrow().surface.clone());
        if self.activated.as_ref().map(|s| s.surface())
            != active_toplevel.as_ref().map(|s| s.surface())
        {
            if let Some(activated) = self.activated.take() {
                activated.set_state(|state| {
                    state.states.unset(State::Activated);
                });
            }
            if let Some(surface) = &active_toplevel {
                surface.set_state(|state| {
                    state.states.set(State::Activated);
                });
            }
            self.activated = active_toplevel;
        }

        // A focused layer/X11 overlay takes the keyboard over the baseline,
        // mirroring the old `layers.focus.or(baseline)` precedence. A stale
        // overlay (its surface gone) falls through to the baseline.
        if let Some(t @ (FocusTarget::Layer(..) | FocusTarget::X11(_))) = &self.focus_target {
            if self.is_focusable(t) {
                return t.resolve();
            }
        }

        baseline.map(|w| {
            let w = w.borrow();
            (w.surface.surface().clone(), w.app_id.clone())
        })
    }

    fn is_focusable(&self, target: &FocusTarget) -> bool {
        match target {
            FocusTarget::Layout(weak) => match weak.upgrade() {
                Some(w) => {
                    let w = w.borrow();
                    if w.minimized {
                        return false;
                    }
                    if self.canvas.is_mobile() {
                        return self.layouts.position(w.surface.surface()).map(|p| p.index)
                            == self.layouts.active_index();
                    }
                    true
                },
                None => false,
            },
            FocusTarget::Layer(surface, _) => {
                self.layers.iter().any(|l| l.surface.surface() == surface)
            },
            FocusTarget::X11(surface) => self.x11_windows.entries().iter().any(|e| {
                !e.minimized && e.surface.wl_surface().as_ref() == Some(surface)
            }),
        }
    }

    /// The single entry point for changing focus. Brings the target to the
    /// front (z-order + desktop stacking), refreshes the MRU and updates the
    /// baseline/overlay focus so keyboard focus and activation follow on the
    /// next [`Self::focus`]. `None` clears focus entirely (tap on the
    /// background).
    pub fn set_focus(&mut self, target: Option<FocusTarget>) {
        match &target {
            Some(FocusTarget::Layout(weak)) => {
                if let Some(w) = weak.upgrade() {
                    let surface = w.borrow().surface.surface().clone();
                    self.mru_touch(surface.id());
                    self.z_promote(&surface);
                    if !self.canvas.is_mobile() {
                        if let Some(id) = self.layouts.layout_id_for(&surface) {
                            self.layouts.raise_to_top(id);
                        }
                    }
                    self.layouts.focus = Some(weak.clone());
                }
            },
            Some(FocusTarget::X11(surface)) => {
                self.mru_touch(surface.id());
                self.z_promote(surface);
            },
            Some(FocusTarget::Layer(..)) => {},
            None => self.layouts.focus = None,
        }
        self.focus_target = target;
        self.dirty = true;
    }
}
