//! wlr-gamma-control-unstable-v1 protocol implementation.
//!
//! Allows privileged clients (like `wlsunset`) to set per-output gamma tables.

use smithay::reexports::wayland_protocols_wlr::gamma_control::v1::server::{
    zwlr_gamma_control_manager_v1::{self, ZwlrGammaControlManagerV1},
    zwlr_gamma_control_v1::{self, ZwlrGammaControlV1},
};
use smithay::reexports::wayland_server::{
    Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New,
};
use std::os::unix::io::FromRawFd;
use tracing::debug;

const MANAGER_VERSION: u32 = 1;

pub struct GammaControlData;

pub struct GammaControlManagerState;

impl GammaControlManagerState {
    pub fn new<D>(display: &DisplayHandle) -> Self
    where
        D: GlobalDispatch<ZwlrGammaControlManagerV1, ()>
            + Dispatch<ZwlrGammaControlManagerV1, ()>
            + Dispatch<ZwlrGammaControlV1, GammaControlData>
            + 'static,
    {
        display.create_global::<D, ZwlrGammaControlManagerV1, _>(MANAGER_VERSION, ());
        Self
    }
}

impl<D> GlobalDispatch<ZwlrGammaControlManagerV1, (), D> for GammaControlManagerState
where
    D: GlobalDispatch<ZwlrGammaControlManagerV1, ()>
        + Dispatch<ZwlrGammaControlManagerV1, ()>
        + Dispatch<ZwlrGammaControlV1, GammaControlData>
        + 'static,
{
    fn bind(
        _state: &mut D,
        _display: &DisplayHandle,
        _client: &Client,
        resource: New<ZwlrGammaControlManagerV1>,
        _data: &(),
        data_init: &mut DataInit<'_, D>,
    ) {
        data_init.init(resource, ());
        debug!("wlr-gamma-control-manager bound");
    }
}

impl<D> Dispatch<ZwlrGammaControlManagerV1, (), D> for GammaControlManagerState
where
    D: Dispatch<ZwlrGammaControlManagerV1, ()>
        + Dispatch<ZwlrGammaControlV1, GammaControlData>
        + 'static,
{
    fn request(
        _state: &mut D,
        _client: &Client,
        _resource: &ZwlrGammaControlManagerV1,
        request: zwlr_gamma_control_manager_v1::Request,
        _data: &(),
        _display: &DisplayHandle,
        data_init: &mut DataInit<'_, D>,
    ) {
        match request {
            zwlr_gamma_control_manager_v1::Request::GetGammaControl { id, .. } => {
                let control = data_init.init(id, GammaControlData);
                // Send the gamma table size (256 entries per channel is standard)
                control.gamma_size(256);
            }
            zwlr_gamma_control_manager_v1::Request::Destroy => {}
            _ => {}
        }
    }
}

impl<D> Dispatch<ZwlrGammaControlV1, GammaControlData, D> for GammaControlManagerState
where
    D: Dispatch<ZwlrGammaControlV1, GammaControlData> + 'static,
{
    fn request(
        _state: &mut D,
        _client: &Client,
        _resource: &ZwlrGammaControlV1,
        request: zwlr_gamma_control_v1::Request,
        _data: &GammaControlData,
        _display: &DisplayHandle,
        _data_init: &mut DataInit<'_, D>,
    ) {
        match request {
            zwlr_gamma_control_v1::Request::SetGamma { fd } => {
                // Read gamma table from fd: 3 channels * 256 entries * 2 bytes = 1536 bytes
                use std::io::Read;
                use std::os::unix::io::IntoRawFd;

                let raw_fd = fd.into_raw_fd();
                let mut file = unsafe { std::fs::File::from_raw_fd(raw_fd) };
                let mut buf = vec![0u8; 256 * 3 * 2];
                if let Err(e) = file.read_exact(&mut buf) {
                    debug!("Failed to read gamma table: {e}");
                    return;
                }

                // Parse into [u16] for R, G, B channels
                let _red: Vec<u16> = buf[..512]
                    .chunks_exact(2)
                    .map(|c| u16::from_ne_bytes([c[0], c[1]]))
                    .collect();
                let _green: Vec<u16> = buf[512..1024]
                    .chunks_exact(2)
                    .map(|c| u16::from_ne_bytes([c[0], c[1]]))
                    .collect();
                let _blue: Vec<u16> = buf[1024..1536]
                    .chunks_exact(2)
                    .map(|c| u16::from_ne_bytes([c[0], c[1]]))
                    .collect();

                // The gamma table is now parsed; applying to DRM requires access to
                // the DRM connector's GAMMA_LUT property. This compositor uses the
                // night_light module for color temperature adjustments which handles
                // gamma via shader uniforms instead of DRM LUTs.
                debug!("Gamma table received (256 entries per channel)");
            }
            zwlr_gamma_control_v1::Request::Destroy => {}
            _ => {}
        }
    }
}

#[macro_export]
macro_rules! delegate_gamma_control {
    ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
        smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
            smithay::reexports::wayland_protocols_wlr::gamma_control::v1::server::zwlr_gamma_control_manager_v1::ZwlrGammaControlManagerV1: ()
        ] => $crate::protocols::gamma_control::GammaControlManagerState);

        smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
            smithay::reexports::wayland_protocols_wlr::gamma_control::v1::server::zwlr_gamma_control_manager_v1::ZwlrGammaControlManagerV1: ()
        ] => $crate::protocols::gamma_control::GammaControlManagerState);

        smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
            smithay::reexports::wayland_protocols_wlr::gamma_control::v1::server::zwlr_gamma_control_v1::ZwlrGammaControlV1: $crate::protocols::gamma_control::GammaControlData
        ] => $crate::protocols::gamma_control::GammaControlManagerState);
    };
}
