use smithay::reexports::wayland_protocols_wlr::foreign_toplevel::v1::server::{
    zwlr_foreign_toplevel_handle_v1::{self, ZwlrForeignToplevelHandleV1},
    zwlr_foreign_toplevel_manager_v1::{self, ZwlrForeignToplevelManagerV1},
};
use smithay::reexports::wayland_server::{
    Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, Weak as WlWeak,
};
use tracing::debug;

const MANAGER_VERSION: u32 = 3;

/// Per-handle data tracking which toplevel this handle represents.
#[derive(Debug)]
pub struct ToplevelHandleData {
    pub app_id: String,
}

/// State for the foreign toplevel management protocol.
pub struct ForeignToplevelState {
    managers: Vec<WlWeak<ZwlrForeignToplevelManagerV1>>,
}

impl ForeignToplevelState {
    pub fn new<D>(display: &DisplayHandle) -> Self
    where
        D: GlobalDispatch<ZwlrForeignToplevelManagerV1, ()>,
        D: Dispatch<ZwlrForeignToplevelManagerV1, ()>,
        D: Dispatch<ZwlrForeignToplevelHandleV1, ToplevelHandleData>,
        D: ForeignToplevelHandler,
        D: 'static,
    {
        display.create_global::<D, ZwlrForeignToplevelManagerV1, _>(MANAGER_VERSION, ());
        Self {
            managers: Vec::new(),
        }
    }
}

/// Trait for handling foreign toplevel requests.
pub trait ForeignToplevelHandler {
    fn foreign_toplevel_state(&mut self) -> &mut ForeignToplevelState;

    /// Called when a client requests to activate a toplevel.
    fn activate_toplevel(&mut self, app_id: &str);

    /// Called when a client requests to close a toplevel.
    fn close_toplevel(&mut self, app_id: &str);

    /// Called when a client requests to fullscreen a toplevel.
    fn fullscreen_toplevel(&mut self, app_id: &str);

    /// Called when a client requests to unfullscreen a toplevel.
    fn unfullscreen_toplevel(&mut self, app_id: &str);
}

impl<D> GlobalDispatch<ZwlrForeignToplevelManagerV1, (), D> for ForeignToplevelState
where
    D: GlobalDispatch<ZwlrForeignToplevelManagerV1, ()>,
    D: Dispatch<ZwlrForeignToplevelManagerV1, ()>,
    D: Dispatch<ZwlrForeignToplevelHandleV1, ToplevelHandleData>,
    D: ForeignToplevelHandler,
    D: 'static,
{
    fn bind(
        state: &mut D,
        _display: &DisplayHandle,
        _client: &Client,
        manager: New<ZwlrForeignToplevelManagerV1>,
        _data: &(),
        data_init: &mut DataInit<'_, D>,
    ) {
        let manager = data_init.init(manager, ());
        debug!("Foreign toplevel manager bound");
        state
            .foreign_toplevel_state()
            .managers
            .push(manager.downgrade());
    }
}

impl<D> Dispatch<ZwlrForeignToplevelManagerV1, (), D> for ForeignToplevelState
where
    D: Dispatch<ZwlrForeignToplevelManagerV1, ()>,
    D: ForeignToplevelHandler,
    D: 'static,
{
    fn request(
        _state: &mut D,
        _client: &Client,
        _manager: &ZwlrForeignToplevelManagerV1,
        request: zwlr_foreign_toplevel_manager_v1::Request,
        _data: &(),
        _display: &DisplayHandle,
        _data_init: &mut DataInit<'_, D>,
    ) {
        match request {
            zwlr_foreign_toplevel_manager_v1::Request::Stop => {
                // Client wants to stop receiving events
            }
            _ => {}
        }
    }
}

impl<D> Dispatch<ZwlrForeignToplevelHandleV1, ToplevelHandleData, D> for ForeignToplevelState
where
    D: Dispatch<ZwlrForeignToplevelHandleV1, ToplevelHandleData>,
    D: ForeignToplevelHandler,
    D: 'static,
{
    fn request(
        state: &mut D,
        _client: &Client,
        _handle: &ZwlrForeignToplevelHandleV1,
        request: zwlr_foreign_toplevel_handle_v1::Request,
        data: &ToplevelHandleData,
        _display: &DisplayHandle,
        _data_init: &mut DataInit<'_, D>,
    ) {
        match request {
            zwlr_foreign_toplevel_handle_v1::Request::SetMaximized => {}
            zwlr_foreign_toplevel_handle_v1::Request::UnsetMaximized => {}
            zwlr_foreign_toplevel_handle_v1::Request::SetMinimized => {}
            zwlr_foreign_toplevel_handle_v1::Request::UnsetMinimized => {}
            zwlr_foreign_toplevel_handle_v1::Request::Activate { .. } => {
                state.activate_toplevel(&data.app_id);
            }
            zwlr_foreign_toplevel_handle_v1::Request::Close => {
                state.close_toplevel(&data.app_id);
            }
            zwlr_foreign_toplevel_handle_v1::Request::SetFullscreen { .. } => {
                state.fullscreen_toplevel(&data.app_id);
            }
            zwlr_foreign_toplevel_handle_v1::Request::UnsetFullscreen => {
                state.unfullscreen_toplevel(&data.app_id);
            }
            zwlr_foreign_toplevel_handle_v1::Request::Destroy => {}
            _ => {}
        }
    }
}

/// Delegate macro for foreign toplevel protocol.
#[macro_export]
macro_rules! delegate_foreign_toplevel {
    ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
        smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
            smithay::reexports::wayland_protocols_wlr::foreign_toplevel::v1::server::zwlr_foreign_toplevel_manager_v1::ZwlrForeignToplevelManagerV1: ()
        ] => $crate::protocols::foreign_toplevel::ForeignToplevelState);

        smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
            smithay::reexports::wayland_protocols_wlr::foreign_toplevel::v1::server::zwlr_foreign_toplevel_manager_v1::ZwlrForeignToplevelManagerV1: ()
        ] => $crate::protocols::foreign_toplevel::ForeignToplevelState);

        smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
            smithay::reexports::wayland_protocols_wlr::foreign_toplevel::v1::server::zwlr_foreign_toplevel_handle_v1::ZwlrForeignToplevelHandleV1: $crate::protocols::foreign_toplevel::ToplevelHandleData
        ] => $crate::protocols::foreign_toplevel::ForeignToplevelState);
    };
}
