//! Handler utilities for the various selection protocols.
//!
//! Wayland originally only had a singe way to deal with the user clipboard with the help of
//! `wl_data_device` protocol. However the demand for *primary* selection as well as writing
//! clipboard managers made it into developing protocol extension for the said cases.
//!
//! The following selection providers are supported:
//!
//! - The [`data_device`](data_device/index.html) module to work with the traditional clipboard selection.
//! - The [`primary_selection`](primary_selection/index.html) module to work with the primary selection.
//! - The [`wlr_data_control`](wlr_data_control/index.html) module to hook data control into
//!   clipboard and primary selection

use std::os::unix::io::OwnedFd;

use crate::input::{Seat, SeatHandler};

pub mod data_device;
pub mod ext_data_control;
pub mod primary_selection;
pub mod wlr_data_control;

mod device;
mod offer;
mod seat_data;
mod source;

pub use source::SelectionSource;

/// Events that are generated by interactions of the clients with the data device.
pub trait SelectionHandler: Sized + SeatHandler {
    /// UserData attached to server-side selections.
    type SelectionUserData: Clone + Send + Sync + 'static;

    /// A client has set the selection
    #[allow(unused_variables)]
    fn new_selection(&mut self, ty: SelectionTarget, source: Option<SelectionSource>, seat: Seat<Self>) {}

    /// A client requested to read the server-set selection.
    ///
    /// * `mime_type` - the requested mime type
    /// * `fd` - the fd to write into
    #[allow(unused_variables)]
    fn send_selection(
        &mut self,
        ty: SelectionTarget,
        mime_type: String,
        fd: OwnedFd,
        seat: Seat<Self>,
        user_data: &Self::SelectionUserData,
    ) {
    }
}

/// The target for the selection request.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SelectionTarget {
    /// The target for the request is primary selection.
    Primary,
    /// The target for the request is clipboard selection.
    Clipboard,
}

pub(crate) mod private {
    /// `selection_dispatch!(self; Enum(foo) => foo.something())`
    /// expands to the equivalent of
    /// ```ignore
    /// match self {
    ///    Enum::DataDevice(foo) => foo.something(),
    ///    Enum::Primary(foo) => foo.something(),
    ///    Enum::WlrDataControl(foo) => foo.something(),
    ///    Enum::ExtDataControl(foo) => foo.something(),
    /// }
    /// ```
    ///
    /// And `selection_dispatch!(self, other; Enum(foo), EnumNext(zoo) => foo.something(zoo))` expands
    /// to the equivalent of
    /// ```ignore
    /// match (self, other) {
    ///    (Enum::DataDevice(foo), EnumNext::DataDevice(zoo))  => foo.something(zoo),
    ///    (Enum::Primary(foo), EnumNext::Primary(zoo))  => foo.something(zoo),
    ///    (Enum::WlrDataControl(foo), EnumNext::WlrDataControl(zoo))  => foo.something(zoo),
    ///    (Enum::ExtDataControl(foo), EnumNext::ExtDataControl(zoo))  => foo.something(zoo),
    ///    _ => unreachable!(),
    /// }
    /// ```
    ///
    /// The result can be converted to another enum by adding `; as AnotherEnum`
    macro_rules! selection_dispatch {
        ($what:ident; $enum:ident ( $($c1:tt)* ) => $x:expr) => {
            match $what {
                $enum::DataDevice($($c1)*) => $x,
                $enum::Primary($($c1)*) => $x,
                $enum::WlrDataControl($($c1)*) => $x,
                $enum::ExtDataControl($($c1)*) => $x,
            }
        };
        ($what:ident$(, $what_next:ident)+; $enum:ident ( $($c1:tt)*) $(, $enum_next:ident ( $($c2:tt)* ) )+ => $x:expr) => {
            match ($what$(, $what_next)*) {
                ($enum::DataDevice($($c1)*)$(, $enum_next::DataDevice($($c2)*))*) => $x,
                ($enum::Primary($($c1)*)$(, $enum_next::Primary($($c2)*))*) => $x,
                ($enum::WlrDataControl($($c1)*)$(, $enum_next::WlrDataControl($($c2)*))*) => $x,
                ($enum::ExtDataControl($($c1)*)$(, $enum_next::ExtDataControl($($c2)*))*) => $x,
                _ => unreachable!(),
            }
        };
    }

    pub(crate) use selection_dispatch;
}
