// SPDX-License-Identifier: MPL-2.0

use crate::{
    prelude::*,
    process::{Gid, Uid},
};

mod ipc_ids;
mod ipc_ns;
pub mod semaphore;

pub use ipc_ids::IpcId;
pub use ipc_ns::IpcNamespace;

/// An IPC key.
///
/// This key is specified by and can be looked up in userspace. Do not confuse it with the
/// identifier [`IpcId`], which is specified by the kernel and returned to userspace upon lookup.
pub type IpcKey = i32;

/// A private IPC key.
///
/// This key cannot be looked up.
const IPC_PRIVATE: IpcKey = 0;

bitflags! {
    pub struct IpcFlags: u32{
        /// Create key if key does not exist
        const IPC_CREAT  = 1 << 9;
        /// Fail if key exists
        const IPC_EXCL  = 1 << 10;
        /// Return error on wait
        const IPC_NOWAIT  = 1 << 11;
        /// Undo the operation on exit
        const SEM_UNDO = 1 << 12;
    }
}

// TODO: Add support for the commented-out commands below
#[expect(non_camel_case_types)]
#[repr(i32)]
#[derive(Clone, Copy, Debug, TryFromInt)]
pub enum IpcControlCmd {
    IPC_RMID = 0,
    // IPC_SET = 1,
    IPC_STAT = 2,

    SEM_GETPID = 11,
    SEM_GETVAL = 12,
    // SEM_GETALL = 13,
    SEM_GETNCNT = 14,
    SEM_GETZCNT = 15,
    SEM_SETVAL = 16,
    // SEM_SETALL = 17,
}

#[derive(Debug)]
pub struct IpcPermission {
    key: IpcKey,
    /// Owner's UID
    uid: Uid,
    /// Owner's GID
    gid: Gid,
    /// Creator's UID
    cuid: Uid,
    /// Creator's GID
    cguid: Gid,
    /// Permission mode
    mode: u16,
}

impl IpcPermission {
    pub fn key(&self) -> IpcKey {
        self.key
    }

    /// Returns owner's UID
    pub fn uid(&self) -> Uid {
        self.uid
    }

    /// Returns owner's GID
    pub fn gid(&self) -> Gid {
        self.gid
    }

    /// Returns creator's UID
    pub fn cuid(&self) -> Uid {
        self.cuid
    }

    /// Returns creator's GID
    pub fn cguid(&self) -> Gid {
        self.cguid
    }

    /// Returns permission mode
    pub fn mode(&self) -> u16 {
        self.mode
    }

    pub(self) fn new_sem_perm(key: IpcKey, uid: Uid, gid: Gid, mode: u16) -> Self {
        Self {
            key,
            uid,
            gid,
            cuid: uid,
            cguid: gid,
            mode,
        }
    }
}
