use smithay::reexports::wayland_protocols_wlr::output_power_management::v1::server::{
    zwlr_output_power_manager_v1::{self, ZwlrOutputPowerManagerV1},
    zwlr_output_power_v1::{self, ZwlrOutputPowerV1},
};
use smithay::reexports::wayland_server::{self,
    Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, Weak as WlWeak,
};
use tracing::debug;

const MANAGER_VERSION: u32 = 1;

/// Per-power-control data.
pub struct OutputPowerData;

pub struct OutputPowerManagerState {
    controls: Vec<WlWeak<ZwlrOutputPowerV1>>,
}

impl OutputPowerManagerState {
    pub fn new<D>(display: &DisplayHandle) -> Self
    where
        D: GlobalDispatch<ZwlrOutputPowerManagerV1, ()>,
        D: Dispatch<ZwlrOutputPowerManagerV1, ()>,
        D: Dispatch<ZwlrOutputPowerV1, OutputPowerData>,
        D: OutputPowerHandler,
        D: 'static,
    {
        display.create_global::<D, ZwlrOutputPowerManagerV1, _>(MANAGER_VERSION, ());
        Self {
            controls: Vec::new(),
        }
    }
}

/// Trait for handling output power management requests.
pub trait OutputPowerHandler {
    fn output_power_state(&mut self) -> &mut OutputPowerManagerState;

    /// Called when a client requests changing the output power mode.
    fn set_output_power(&mut self, on: bool);
}

impl<D> GlobalDispatch<ZwlrOutputPowerManagerV1, (), D> for OutputPowerManagerState
where
    D: GlobalDispatch<ZwlrOutputPowerManagerV1, ()>,
    D: Dispatch<ZwlrOutputPowerManagerV1, ()>,
    D: Dispatch<ZwlrOutputPowerV1, OutputPowerData>,
    D: OutputPowerHandler,
    D: 'static,
{
    fn bind(
        _state: &mut D,
        _display: &DisplayHandle,
        _client: &Client,
        manager: New<ZwlrOutputPowerManagerV1>,
        _data: &(),
        data_init: &mut DataInit<'_, D>,
    ) {
        data_init.init(manager, ());
        debug!("Output power manager bound");
    }
}

impl<D> Dispatch<ZwlrOutputPowerManagerV1, (), D> for OutputPowerManagerState
where
    D: Dispatch<ZwlrOutputPowerManagerV1, ()>,
    D: Dispatch<ZwlrOutputPowerV1, OutputPowerData>,
    D: OutputPowerHandler,
    D: 'static,
{
    fn request(
        state: &mut D,
        _client: &Client,
        _manager: &ZwlrOutputPowerManagerV1,
        request: zwlr_output_power_manager_v1::Request,
        _data: &(),
        _display: &DisplayHandle,
        data_init: &mut DataInit<'_, D>,
    ) {
        match request {
            zwlr_output_power_manager_v1::Request::GetOutputPower { id, .. } => {
                let control = data_init.init(id, OutputPowerData);
                state
                    .output_power_state()
                    .controls
                    .push(control.downgrade());
            }
            zwlr_output_power_manager_v1::Request::Destroy => {}
            _ => {}
        }
    }
}

impl<D> Dispatch<ZwlrOutputPowerV1, OutputPowerData, D> for OutputPowerManagerState
where
    D: Dispatch<ZwlrOutputPowerV1, OutputPowerData>,
    D: OutputPowerHandler,
    D: 'static,
{
    fn request(
        state: &mut D,
        _client: &Client,
        _control: &ZwlrOutputPowerV1,
        request: zwlr_output_power_v1::Request,
        _data: &OutputPowerData,
        _display: &DisplayHandle,
        _data_init: &mut DataInit<'_, D>,
    ) {
        match request {
            zwlr_output_power_v1::Request::SetMode { mode } => {
                let on = mode == wayland_server::WEnum::Value(zwlr_output_power_v1::Mode::On);
                state.set_output_power(on);
            }
            zwlr_output_power_v1::Request::Destroy => {}
            _ => {}
        }
    }
}

/// Delegate macro for output power management protocol.
#[macro_export]
macro_rules! delegate_output_power {
    ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
        smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
            smithay::reexports::wayland_protocols_wlr::output_power_management::v1::server::zwlr_output_power_manager_v1::ZwlrOutputPowerManagerV1: ()
        ] => $crate::protocols::output_power::OutputPowerManagerState);

        smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
            smithay::reexports::wayland_protocols_wlr::output_power_management::v1::server::zwlr_output_power_manager_v1::ZwlrOutputPowerManagerV1: ()
        ] => $crate::protocols::output_power::OutputPowerManagerState);

        smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
            smithay::reexports::wayland_protocols_wlr::output_power_management::v1::server::zwlr_output_power_v1::ZwlrOutputPowerV1: $crate::protocols::output_power::OutputPowerData
        ] => $crate::protocols::output_power::OutputPowerManagerState);
    };
}
