use std::time::UNIX_EPOCH;

use smithay::reexports::wayland_protocols_wlr::screencopy::v1::server::zwlr_screencopy_frame_v1::{
    Flags, Request, ZwlrScreencopyFrameV1,
};
use smithay::reexports::wayland_server::protocol::wl_buffer::WlBuffer;
use smithay::reexports::wayland_server::{Client, DataInit, Dispatch, DisplayHandle};
use smithay::utils::{Physical, Rectangle};

use crate::protocols::screencopy::{ScreencopyHandler, ScreencopyManagerState};

pub struct ScreencopyFrameState {
    pub rect: Rectangle<i32, Physical>,
    pub _overlay_cursor: bool,
}

impl<D> Dispatch<ZwlrScreencopyFrameV1, ScreencopyFrameState, D> for ScreencopyManagerState
where
    D: Dispatch<ZwlrScreencopyFrameV1, ScreencopyFrameState>,
    D: ScreencopyHandler,
    D: 'static,
{
    fn request(
        state: &mut D,
        _client: &Client,
        frame: &ZwlrScreencopyFrameV1,
        request: Request,
        data: &ScreencopyFrameState,
        _display: &DisplayHandle,
        _data_init: &mut DataInit<'_, D>,
    ) {
        let (buffer, send_damage) = match request {
            Request::Copy { buffer } => (buffer, false),
            Request::CopyWithDamage { buffer } => (buffer, true),
            Request::Destroy => return,
            _ => unreachable!(),
        };

        state.frame(Screencopy {
            send_damage,
            buffer,
            frame: frame.clone(),
            region: data.rect,
            submitted: false,
        });
    }
}

pub struct Screencopy {
    region: Rectangle<i32, Physical>,
    frame: ZwlrScreencopyFrameV1,
    send_damage: bool,
    buffer: WlBuffer,
    submitted: bool,
}

impl Drop for Screencopy {
    fn drop(&mut self) {
        if !self.submitted {
            self.frame.failed();
        }
    }
}

impl Screencopy {
    pub fn buffer(&self) -> &WlBuffer {
        &self.buffer
    }

    pub fn region(&self) -> Rectangle<i32, Physical> {
        self.region
    }

    pub fn damage(&mut self, damage: &[Rectangle<i32, Physical>]) {
        if !self.send_damage {
            return;
        }

        for Rectangle { loc, size } in damage {
            self.frame.damage(loc.x as u32, loc.y as u32, size.w as u32, size.h as u32);
        }
    }

    pub fn submit(mut self) {
        self.frame.flags(Flags::empty());

        let now = UNIX_EPOCH.elapsed().unwrap();
        let secs = now.as_secs();
        self.frame.ready((secs >> 32) as u32, secs as u32, now.subsec_nanos());

        self.submitted = true;
    }
}
