use {
    crate::{
        async_engine::SpawnedFuture,
        client::{error::LookupError, objects::Objects},
        criteria::{
            CritDestroyListener, CritMatcherId,
            clm::{CL_CHANGED_DESTROYED, CL_CHANGED_NEW, ClMatcherChange},
        },
        ifs::{
            wl_display::WlDisplay,
            wl_registry::WlRegistry,
            wl_surface::{WlSurface, commit_timeline::CommitTimelines},
            xdg_activation_token_v1::ActivationToken,
        },
        leaks::Tracker,
        object::{Interface, Object, ObjectId, WL_DISPLAY_ID},
        security_context_acceptor::AcceptorMetadata,
        state::State,
        utils::{
            asyncevent::AsyncEvent,
            buffd::{MsgFormatter, MsgParser, MsgParserError, OutBufferSwapchain},
            copyhashmap::{CopyHashMap, Locked},
            errorfmt::ErrorFmt,
            numcell::NumCell,
            pending_serial::PendingSerial,
            pid_info::{PidInfo, get_pid_info, get_socket_creds},
            pidfd_send_signal::pidfd_send_signal,
            static_text::StaticText,
        },
        wire::WlRegistryId,
    },
    ahash::AHashMap,
    std::{
        cell::{Cell, RefCell},
        collections::VecDeque,
        error::Error,
        fmt::{Debug, Display, Formatter},
        mem,
        ops::DerefMut,
        rc::{Rc, Weak},
    },
    uapi::{OwnedFd, c},
};
pub use {
    error::{ClientError, ParserError},
    objects::MIN_SERVER_ID,
};

mod error;
mod objects;
mod tasks;

bitflags! {
    ClientCaps: u32;
        CAP_DATA_CONTROL_MANAGER     = 1 << 0,
        CAP_VIRTUAL_KEYBOARD_MANAGER = 1 << 1,
        CAP_FOREIGN_TOPLEVEL_LIST    = 1 << 2,
        CAP_IDLE_NOTIFIER            = 1 << 3,
        CAP_SESSION_LOCK_MANAGER     = 1 << 4,
        CAP_JAY_COMPOSITOR           = 1 << 5,
        CAP_LAYER_SHELL              = 1 << 6,
        CAP_SCREENCOPY_MANAGER       = 1 << 7,
        CAP_SEAT_MANAGER             = 1 << 8,
        CAP_DRM_LEASE                = 1 << 9,
        CAP_INPUT_METHOD             = 1 << 10,
        CAP_WORKSPACE                = 1 << 11,
        CAP_FOREIGN_TOPLEVEL_MANAGER           = 1 << 12,
        CAP_HEAD_MANAGER                       = 1 << 13,
        CAP_GAMMA_CONTROL_MANAGER              = 1 << 14,
        CAP_VIRTUAL_POINTER_MANAGER            = 1 << 15,
        CAP_FOREIGN_TOPLEVEL_GEOMETRY_TRACKING = 1 << 16,
}

impl StaticText for ClientCapsEnum {
    fn text(&self) -> &'static str {
        match self {
            ClientCapsEnum::CAP_DATA_CONTROL_MANAGER => "data-control",
            ClientCapsEnum::CAP_VIRTUAL_KEYBOARD_MANAGER => "virtual-keyboard",
            ClientCapsEnum::CAP_FOREIGN_TOPLEVEL_LIST => "foreign-toplevel-list",
            ClientCapsEnum::CAP_IDLE_NOTIFIER => "idle-notifier",
            ClientCapsEnum::CAP_SESSION_LOCK_MANAGER => "session-lock",
            ClientCapsEnum::CAP_JAY_COMPOSITOR => "jay-compositor",
            ClientCapsEnum::CAP_LAYER_SHELL => "layer-shell",
            ClientCapsEnum::CAP_SCREENCOPY_MANAGER => "screencopy",
            ClientCapsEnum::CAP_SEAT_MANAGER => "seat-manager",
            ClientCapsEnum::CAP_DRM_LEASE => "drm-lease",
            ClientCapsEnum::CAP_INPUT_METHOD => "input-method",
            ClientCapsEnum::CAP_WORKSPACE => "workspace-manager",
            ClientCapsEnum::CAP_FOREIGN_TOPLEVEL_MANAGER => "foreign-toplevel-manager",
            ClientCapsEnum::CAP_HEAD_MANAGER => "head-manager",
            ClientCapsEnum::CAP_GAMMA_CONTROL_MANAGER => "gamma-control-manager",
            ClientCapsEnum::CAP_VIRTUAL_POINTER_MANAGER => "virtual-pointer",
            ClientCapsEnum::CAP_FOREIGN_TOPLEVEL_GEOMETRY_TRACKING => {
                "foreign-toplevel-geometry-tracking"
            }
        }
    }
}

pub const CAPS_DEFAULT: ClientCaps = ClientCaps(CAP_LAYER_SHELL.0 | CAP_DRM_LEASE.0);
pub const CAPS_DEFAULT_SANDBOXED: ClientCaps = ClientCaps(CAP_DRM_LEASE.0);

#[derive(Debug, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
pub struct ClientId(u64);

impl ClientId {
    pub fn raw(self) -> u64 {
        self.0
    }

    pub fn from_raw(val: u64) -> Self {
        Self(val)
    }
}

impl Display for ClientId {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        Display::fmt(&self.0, f)
    }
}

pub struct Clients {
    next_client_id: NumCell<u64>,
    pub clients: RefCell<AHashMap<ClientId, ClientHolder>>,
    shutdown_clients: RefCell<AHashMap<ClientId, ClientHolder>>,
}

impl Clients {
    pub fn new() -> Self {
        Self {
            next_client_id: NumCell::new(1),
            clients: Default::default(),
            shutdown_clients: Default::default(),
        }
    }

    pub fn clear(&self) {
        mem::take(self.clients.borrow_mut().deref_mut());
        mem::take(self.shutdown_clients.borrow_mut().deref_mut());
    }

    pub fn id(&self) -> ClientId {
        ClientId(self.next_client_id.fetch_add(1))
    }

    pub fn get(&self, id: ClientId) -> Result<Rc<Client>, ClientError> {
        let clients = self.clients.borrow();
        match clients.get(&id) {
            Some(c) => Ok(c.data.clone()),
            _ => Err(ClientError::ClientDoesNotExist(id)),
        }
    }

    pub fn spawn(
        &self,
        id: ClientId,
        global: &Rc<State>,
        socket: Rc<OwnedFd>,
        bounding_caps: ClientCaps,
        set_bounding_caps_for_children: bool,
        acceptor: &Rc<AcceptorMetadata>,
    ) -> Result<(), ClientError> {
        let Some((uid, pid)) = get_socket_creds(&socket) else {
            return Ok(());
        };
        self.spawn2(
            id,
            global,
            socket,
            uid,
            pid,
            bounding_caps,
            set_bounding_caps_for_children,
            false,
            acceptor,
        )?;
        Ok(())
    }

    pub fn spawn2(
        &self,
        id: ClientId,
        global: &Rc<State>,
        socket: Rc<OwnedFd>,
        uid: c::uid_t,
        pid: c::pid_t,
        bounding_caps: ClientCaps,
        set_bounding_caps_for_children: bool,
        is_xwayland: bool,
        acceptor: &Rc<AcceptorMetadata>,
    ) -> Result<Rc<Client>, ClientError> {
        let effective_caps = match acceptor.sandboxed {
            true => CAPS_DEFAULT_SANDBOXED,
            false => CAPS_DEFAULT,
        };
        let data = Rc::new_cyclic(|slf| Client {
            id,
            state: global.clone(),
            checking_queue_size: Cell::new(false),
            socket,
            objects: Objects::new(),
            swapchain: Default::default(),
            flush_request: Default::default(),
            shutdown: Default::default(),
            tracker: Default::default(),
            is_xwayland,
            effective_caps: Cell::new(effective_caps & bounding_caps),
            bounding_caps_for_children: Cell::new(bounding_caps),
            last_enter_serial: Default::default(),
            pid_info: get_pid_info(uid, pid),
            serials: Default::default(),
            symmetric_delete: Cell::new(false),
            last_xwayland_serial: Cell::new(0),
            surfaces_by_xwayland_serial: Default::default(),
            activation_tokens: Default::default(),
            commit_timelines: Rc::new(CommitTimelines::new(
                &global.wait_for_syncobj,
                &global.ring,
                &global.eng,
                slf,
            )),
            wire_scale: Default::default(),
            focus_stealing_serial: Default::default(),
            changed_properties: Default::default(),
            destroyed: Default::default(),
            acceptor: acceptor.clone(),
        });
        track!(data, data);
        global.update_capabilities(&data, bounding_caps, set_bounding_caps_for_children);
        if acceptor.secure || is_xwayland {
            data.effective_caps.set(ClientCaps::all());
        }
        let display = Rc::new(WlDisplay::new(&data));
        track!(data, display);
        data.objects.display.set(Some(display.clone()));
        data.objects.add_client_object(display).expect("");
        let client = ClientHolder {
            _handler: global.eng.spawn("client", tasks::client(data.clone())),
            data: data.clone(),
        };
        log::info!(
            "Client {} connected, pid: {}, uid: {}, fd: {}, comm: {:?}, caps: {:?}",
            id,
            pid,
            uid,
            client.data.socket.raw(),
            data.pid_info.comm,
            data.effective_caps.get(),
        );
        client.data.property_changed(CL_CHANGED_NEW);
        self.clients.borrow_mut().insert(client.data.id, client);
        Ok(data)
    }

    pub fn kill(&self, client: ClientId) {
        log::info!("Removing client {}", client.0);
        if self.clients.borrow_mut().remove(&client).is_none() {
            self.shutdown_clients.borrow_mut().remove(&client);
        }
    }

    pub fn shutdown(&self, client_id: ClientId) {
        if let Some(client) = self.clients.borrow_mut().remove(&client_id) {
            log::info!("Shutting down client {}", client.data.id.0);
            client.data.shutdown.trigger();
            client.data.flush_request.trigger();
            self.shutdown_clients.borrow_mut().insert(client_id, client);
        }
    }

    pub fn broadcast<B>(&self, required_caps: ClientCaps, xwayland_only: bool, mut f: B)
    where
        B: FnMut(&Rc<Client>),
    {
        let clients = self.clients.borrow();
        for client in clients.values() {
            if client.data.effective_caps.get().contains(required_caps)
                && (!xwayland_only || client.data.is_xwayland)
            {
                f(&client.data);
            }
        }
    }
}

impl Drop for Clients {
    fn drop(&mut self) {
        let _clients1 = mem::take(&mut *self.clients.borrow_mut());
        let _clients2 = mem::take(&mut *self.shutdown_clients.borrow_mut());
    }
}

pub struct ClientHolder {
    pub data: Rc<Client>,
    _handler: SpawnedFuture<()>,
}

impl Drop for ClientHolder {
    fn drop(&mut self) {
        self.data.objects.destroy();
        self.data.flush_request.clear();
        self.data.shutdown.clear();
        self.data.surfaces_by_xwayland_serial.clear();
        self.data.remove_activation_tokens();
        self.data.commit_timelines.clear();
        self.data.property_changed(CL_CHANGED_DESTROYED);
        if self.data.is_xwayland
            && let Some(pidfd) = self.data.state.xwayland.pidfd.get()
            && let Err(e) = pidfd_send_signal(&pidfd, c::SIGKILL)
        {
            log::error!("Could not kill Xwayland: {}", ErrorFmt(e));
        }
    }
}

pub trait EventFormatter: Debug {
    fn format(self, fmt: &mut MsgFormatter<'_>);
    fn id(&self) -> ObjectId;
    fn interface(&self) -> Interface;
}

pub trait RequestParser<'a>: Debug + Sized {
    type Generic<'b>: RequestParser<'b>;
    const ID: u32;
    fn parse(parser: &mut MsgParser<'_, 'a>) -> Result<Self, MsgParserError>;
}

pub struct Client {
    pub id: ClientId,
    pub state: Rc<State>,
    checking_queue_size: Cell<bool>,
    socket: Rc<OwnedFd>,
    pub objects: Objects,
    swapchain: Rc<RefCell<OutBufferSwapchain>>,
    flush_request: AsyncEvent,
    shutdown: AsyncEvent,
    pub tracker: Tracker<Client>,
    pub is_xwayland: bool,
    pub effective_caps: Cell<ClientCaps>,
    pub bounding_caps_for_children: Cell<ClientCaps>,
    pub last_enter_serial: Cell<Option<u64>>,
    pub pid_info: PidInfo,
    pub serials: RefCell<VecDeque<SerialRange>>,
    pub symmetric_delete: Cell<bool>,
    pub last_xwayland_serial: Cell<u64>,
    pub surfaces_by_xwayland_serial: CopyHashMap<u64, Rc<WlSurface>>,
    pub activation_tokens: RefCell<VecDeque<ActivationToken>>,
    pub commit_timelines: Rc<CommitTimelines>,
    pub wire_scale: Cell<Option<i32>>,
    pub focus_stealing_serial: Cell<Option<u64>>,
    pub changed_properties: Cell<ClMatcherChange>,
    pub destroyed: CopyHashMap<CritMatcherId, Weak<dyn CritDestroyListener<Rc<Self>>>>,
    pub acceptor: Rc<AcceptorMetadata>,
}

pub const NUM_CACHED_SERIAL_RANGES: usize = 64;

pub struct SerialRange {
    pub lo: u64,
    pub hi: u64,
}

impl Client {
    pub fn invalid_request(&self, obj: &dyn Object, request: u32) {
        log::error!(
            "Client {} sent an invalid request {} on object {} of type {}",
            self.id.0,
            request,
            obj.id(),
            obj.interface().name(),
        );
        match self.display() {
            Ok(d) => {
                d.send_invalid_request(obj, request);
                self.state.clients.shutdown(self.id);
            }
            Err(e) => {
                log::error!(
                    "Could not retrieve display of client {}: {}",
                    self.id,
                    ErrorFmt(e),
                );
                self.state.clients.kill(self.id);
            }
        }
    }

    pub fn map_serial(&self, serial: u32) -> Option<u64> {
        let serials = self.serials.borrow_mut();
        let latest = serials.back()?;
        let mut serial = ((latest.hi >> 32) << 32) | (serial as u64);
        if serial > latest.hi {
            serial = serial.checked_sub(1 << 32)?;
        }
        for range in serials.iter().rev() {
            if serial > range.hi {
                return None;
            }
            if serial >= range.lo {
                return Some(serial);
            }
        }
        if serials.len() == NUM_CACHED_SERIAL_RANGES {
            return Some(serial);
        }
        None
    }

    pub fn next_serial(&self) -> u64 {
        self.state.next_serial(Some(self))
    }

    pub fn pending_serial(&self) -> PendingSerial<'_> {
        PendingSerial::new(self)
    }

    pub fn new_id<T: From<ObjectId>>(&self) -> Result<T, ClientError> {
        self.objects.id(self)
    }

    pub fn display(&self) -> Result<Rc<WlDisplay>, ClientError> {
        match self.objects.display.get() {
            Some(d) => Ok(d),
            _ => Err(ClientError::NotADisplay(WL_DISPLAY_ID)),
        }
    }

    pub fn parse<'a, R: RequestParser<'a>>(
        &self,
        obj: &impl Object,
        mut parser: MsgParser<'_, 'a>,
    ) -> Result<R, MsgParserError> {
        let res = R::parse(&mut parser)?;
        log::trace!(
            "Client {} -> {}@{}.{:?}",
            self.id,
            obj.interface().name(),
            obj.id(),
            res
        );
        Ok(res)
    }

    pub fn error(&self, message: impl Error) {
        let msg = ErrorFmt(message).to_string();
        log::error!("Client {}: A fatal error occurred: {}", self.id.0, msg,);
        match self.display() {
            Ok(d) => {
                d.send_implementation_error(msg);
                self.state.clients.shutdown(self.id);
            }
            Err(e) => {
                log::error!(
                    "Could not retrieve display of client {}: {}",
                    self.id,
                    ErrorFmt(e),
                );
                self.state.clients.kill(self.id);
            }
        }
    }

    pub fn protocol_error(&self, obj: &dyn Object, code: u32, message: &str) {
        if let Ok(d) = self.display() {
            d.send_error(obj.id(), code, message);
        }
        self.state.clients.shutdown(self.id);
    }

    pub fn event<T: EventFormatter>(self: &Rc<Self>, event: T) {
        if log::log_enabled!(log::Level::Trace) {
            self.log_event(&event);
        }
        let mut fds = vec![];
        let mut swapchain = self.swapchain.borrow_mut();
        let mut fmt = MsgFormatter::new(&mut swapchain.cur, &mut fds);
        event.format(&mut fmt);
        fmt.write_len();
        if swapchain.cur.is_full() {
            swapchain.commit();
            if swapchain.exceeds_limit() {
                if !self.checking_queue_size.replace(true) {
                    self.state.slow_clients.push(self.clone());
                }
            }
        }
        self.flush_request.trigger();
    }

    // pub fn flush(&self) {
    //     self.flush_request.trigger();
    // }

    pub async fn check_queue_size(&self) {
        if self.swapchain.borrow_mut().exceeds_limit() {
            self.state.eng.yield_now().await;
            if self.swapchain.borrow_mut().exceeds_limit() {
                log::error!("Client {} is too slow at fetching events", self.id.0);
                self.state.clients.kill(self.id);
                return;
            }
        }
        self.checking_queue_size.set(false);
    }

    pub fn lock_registries(&self) -> Locked<'_, WlRegistryId, Rc<WlRegistry>> {
        self.objects.registries()
    }

    pub fn log_event<T: EventFormatter>(&self, event: &T) {
        log::trace!(
            "Client {} <= {}@{}.{:?}",
            self.id,
            event.interface().name(),
            event.id(),
            event,
        );
    }

    pub fn add_client_obj<T: WaylandObject>(&self, obj: &Rc<T>) -> Result<(), ClientError> {
        self.add_obj(obj, true)
    }

    pub fn add_server_obj<T: WaylandObject>(&self, obj: &Rc<T>) {
        self.add_obj(obj, false).expect("add_server_obj failed")
    }

    fn add_obj<T: WaylandObject>(&self, obj: &Rc<T>, client: bool) -> Result<(), ClientError> {
        if client {
            self.objects.add_client_object(obj.clone())?;
        } else {
            self.objects.add_server_object(obj.clone());
        }
        obj.clone().add(self);
        Ok(())
    }

    pub fn remove_obj<T: WaylandObject>(self: &Rc<Self>, obj: &T) -> Result<(), ClientError> {
        obj.remove(self);
        self.objects.remove_obj(self, obj.id())
    }

    pub fn lookup<Id: WaylandObjectLookup>(&self, id: Id) -> Result<Rc<Id::Object>, ClientError> {
        match Id::lookup(self, id) {
            Some(t) => Ok(t),
            _ => Err(ClientError::LookupError(LookupError {
                interface: Id::INTERFACE,
                id: id.into(),
            })),
        }
    }

    fn remove_activation_tokens(&self) {
        for token in &*self.activation_tokens.borrow() {
            self.state.activation_tokens.remove(token);
        }
    }

    pub fn property_changed(self: &Rc<Self>, change: ClMatcherChange) {
        let props = self.changed_properties.get();
        self.changed_properties.set(props | change);
        if props.is_none() && change.is_some() {
            self.state.cl_matcher_manager.changed(self);
        }
    }
}

pub trait WaylandObject: Object {
    fn add(self: Rc<Self>, client: &Client) {
        let _ = client;
    }
    fn remove(&self, client: &Client) {
        let _ = client;
    }
}

pub trait WaylandObjectLookup: Copy + Into<ObjectId> {
    type Object;
    const INTERFACE: Interface;

    fn lookup(client: &Client, id: Self) -> Option<Rc<Self::Object>>;
}
