use core::{
    fmt,
    sync::atomic::{AtomicU32, AtomicUsize, Ordering},
    task::{Context, Poll},
};

use abi_lib::SyscallRegister;
use alloc::{
    collections::{btree_map, BTreeMap},
    format,
    string::String,
    sync::{Arc, Weak},
    vec::Vec,
};
use kernel_arch::{
    task::{TaskContext, UserContextInfo},
    KernelTableManagerImpl,
};
use libk_mm::{phys::GlobalPhysicalAllocator, process::ProcessAddressSpace};
use libk_util::{
    event::{BoolEvent, OneTimeEvent},
    sync::{
        spin_rwlock::{IrqSafeRwLock, IrqSafeRwLockReadGuard, IrqSafeRwLockWriteGuard},
        IrqSafeSpinlock,
    },
};
use yggdrasil_abi::{
    error::Error,
    option::OptionValue,
    process::{
        options::ProcessOptionVariant, ExitCode, ProcessGroupId, ProcessId, Signal, ThreadEvent,
        ThreadSpawnOptions, WaitFlags,
    },
};

use crate::{
    fs::sysfs::{
        self,
        attribute::{StringAttribute, StringAttributeOps},
        object::KObject,
    },
    task::{
        futex::UserspaceMutex, thread::Thread, types::AllocateProcessId, TaskContextImpl, ThreadId,
    },
    vfs::{FileReadiness, FileSet, IoContext, NodeRef},
};

use super::types::TlsImage;

pub trait ForkFrame = kernel_arch::task::ForkFrame<KernelTableManagerImpl, GlobalPhysicalAllocator>;

pub struct ProcessIo {
    ioctx: Option<IoContext>,
    /// Process' set of files
    pub files: FileSet,
}

pub struct ProcessManager {
    processes: IrqSafeRwLock<BTreeMap<ProcessId, Arc<Process>>>,
}

/// Describes information about a program's image in memory
#[derive(Clone)]
pub struct ProcessImage {
    pub load_base: usize,
    pub ip_offset: usize,
    /// Entry point address
    pub entry: usize,
    /// TLS master copy, if any
    pub tls_image: Option<TlsImage>,
}

pub struct ProcessInner {
    session_id: ProcessId,
    group_id: ProcessGroupId,

    session_terminal: Option<NodeRef>,
    threads: Vec<Arc<Thread>>,
    children: BTreeMap<ProcessId, Weak<Process>>,
    mutexes: BTreeMap<usize, Arc<UserspaceMutex>>,
    space: Option<Arc<ProcessAddressSpace>>,
    image: Option<ProcessImage>,
}

/// Describes a process within the system
pub struct Process {
    pub name: String,
    pub id: ProcessId,

    #[allow(unused)]
    parent: Option<Weak<Process>>,

    inner: IrqSafeRwLock<ProcessInner>,
    signal_entry: AtomicUsize,

    pub(crate) exit: OneTimeEvent<ExitCode>,
    pub(crate) child_exit_notify: BoolEvent,

    sysfs_node: IrqSafeRwLock<Option<Arc<KObject<Weak<Process>>>>>,

    /// Process I/O information
    pub io: IrqSafeSpinlock<ProcessIo>,
}

pub struct ProcessCreateInfo<S: Into<String>> {
    pub name: S,
    pub group_id: ProcessGroupId,
    pub space: Arc<ProcessAddressSpace>,
    pub image: Option<ProcessImage>,
    // Main thread
    pub context: TaskContextImpl,
}

impl Process {
    pub fn spawn<S: Into<String>>(
        parent: Option<&Arc<Self>>,
        info: ProcessCreateInfo<S>,
    ) -> (Arc<Self>, Arc<Thread>) {
        let name = info.name.into();
        let id = ProcessId::new();

        let process = Arc::new(Self {
            name: name.clone(),
            id,
            parent: parent.map(Arc::downgrade),
            inner: IrqSafeRwLock::new(ProcessInner::new(
                id,
                info.group_id,
                Some(info.space.clone()),
                info.image,
            )),
            signal_entry: AtomicUsize::new(0),
            child_exit_notify: BoolEvent::new(),
            exit: OneTimeEvent::new(),
            sysfs_node: IrqSafeRwLock::new(None),
            io: IrqSafeSpinlock::new(ProcessIo::new()),
        });

        let sysfs_node = add_sysfs_node(&process);
        *process.sysfs_node.write() = Some(sysfs_node.clone());

        // Add a child if parent specified
        if let Some(parent) = parent {
            parent.inner.write().register_child(&process);
        }

        // Create "main" thread
        let thread = Thread::new_uthread(process.id, Some(name), info.space, info.context);
        thread.add_sysfs_node(&sysfs_node);
        process.inner.write().register_thread(thread.clone());

        MANAGER.register_process(process.clone());

        (process, thread)
    }

    // /// Creates a new process with given main thread
    // pub fn new_with_main<S: Into<String>>(
    //     name: S,
    //     group_id: ProcessGroupId,
    //     parent: Option<Weak<Self>>,
    //     space: Arc<ProcessAddressSpace>,
    //     context: TaskContextImpl,
    //     image: Option<ProcessImage>,
    // ) -> (Arc<Self>, Arc<Thread>) {
    //     let name = name.into();
    //     let id = ProcessId::new();

    //     let process = Arc::new(Self {
    //         name: name.clone(),
    //         id,
    //         parent,

    //         signal_entry: AtomicUsize::new(0),
    //         inner: IrqSafeRwLock::new(ProcessInner::new(id, group_id, Some(space.clone()), image)),

    //         exit: OneTimeEvent::new(),
    //         io: IrqSafeSpinlock::new(ProcessIo::new()),
    //     });

    //     // Create "main" thread
    //     let thread = Thread::new_uthread(process.id, Some(name), space, context);
    //     process.inner.write().register_thread(thread.clone());

    //     MANAGER.register_process(process.clone());

    //     (process, thread)
    // }

    pub fn create_group() -> ProcessGroupId {
        static ID: AtomicU32 = AtomicU32::new(1);
        let id = ID.fetch_add(1, Ordering::AcqRel);

        unsafe { ProcessGroupId::from_raw(id) }
    }

    /// Spawns a new child thread within the process
    pub fn spawn_thread(self: &Arc<Self>, options: &ThreadSpawnOptions) -> Result<ThreadId, Error> {
        log::debug!("Spawn thread in {} with options: {:#x?}", self.id, options);

        let mut inner = self.inner.write();

        let space = inner.space.clone().unwrap();

        // let sp = (options.stack_top - 4) as *mut usize;
        let sp = TaskContextImpl::align_stack_for_entry(options.stack_top) as *mut usize;
        let sp = unsafe { Thread::setup_stack_header(&space, sp, options.argument)? };

        let (address_space, asid) = space.as_address_with_asid();

        let info = UserContextInfo {
            entry: options.entry as _,
            argument: options.argument,
            address_space,
            asid,
            stack_pointer: sp.addr(),
            single_step: false,
            thread_pointer: 0,
        };
        let context = TaskContextImpl::user(info)?;
        let thread = Thread::new_uthread(self.id, None, space.clone(), context);
        let id = thread.id;

        if let Some(sysfs_node) = self.sysfs_node.read().as_ref() {
            thread.add_sysfs_node(sysfs_node);
        }
        inner.register_thread(thread.clone());

        thread.enqueue();

        Ok(id)
    }

    unsafe fn fork_inner<F: ForkFrame<Context = TaskContextImpl>>(
        self: &Arc<Self>,
        _frame: &F,
    ) -> Result<ProcessId, Error> {
        todo!()
        // let src_inner = self.inner.read();
        // let new_space = src_inner.space.as_ref().unwrap().fork()?;
        // let new_context = frame.fork(new_space.as_address_with_asid())?;

        // let (new_process, new_main) = Self::new_with_main(
        //     &self.name,
        //     self.group_id(),
        //     Some(Arc::downgrade(self)),
        //     Arc::new(new_space),
        //     new_context,
        //     src_inner.image.clone(),
        // );

        // {
        //     let mut dst_io = new_process.io.lock();
        //     let src_io = self.io.lock();

        //     dst_io.fork_from(&src_io)?;
        // }

        // new_process.inherit(self)?;

        // log::info!("Process::fork -> {:?}", new_process.id);
        // new_main.enqueue();

        // Ok(new_process.id)
    }

    /// Performs a "fork" operation on the process, creating an identical copy of it, cloning
    /// the register state from provided [ForkFrame].
    ///
    /// # Safety
    ///
    /// Unsafe: frame must be a valid frame to be forked, the function is not yet stable and does
    /// not yet properly fork all the necessary context details.
    pub unsafe fn raw_fork<F: ForkFrame<Context = TaskContextImpl>>(frame: &mut F) {
        let src_thread = Thread::current();
        let src_process = src_thread.process();

        let value = src_process.fork_inner(frame).into_syscall_register();

        frame.set_return_value(value as _);
    }

    /// Replaces the process address space with a new one, loaded from the specified program
    pub fn exec(&self, _program: &str, _argv: Vec<String>, _envp: Vec<String>) -> Result<!, Error> {
        // XXX
        todo!()
        // if self.inner.read().thread_count() != 1 {
        //     todo!();
        // }

        // let (context, image) = proc::exec::load_into(self, program, argv, envp)?;
        // let mut inner = self.inner.write();
        // let main = &inner.threads()[0];

        // let old_context = unsafe { main.replace_context(context) };
        // let new_context = main.context.as_ptr();
        // inner.set_image(Some(image));

        // drop(inner);

        // // TODO old context is leaked
        // unsafe { (*new_context).switch(&old_context) }
        // unreachable!()
    }

    pub fn get_option(
        &self,
        option: ProcessOptionVariant,
        buffer: &mut [u8],
    ) -> Result<usize, Error> {
        use yggdrasil_abi::process::options;
        match option {
            ProcessOptionVariant::Name => options::Name::store(&self.name.as_str(), buffer),
            ProcessOptionVariant::Directory => {
                let io = self.io.lock();
                let path = io.ioctx().cwd_path();
                options::Directory::store(&path.as_str(), buffer)
            }
            ProcessOptionVariant::SignalEntry => {
                options::SignalEntry::store(&self.signal_entry(), buffer)
            }
            ProcessOptionVariant::MainThread => {
                let id = self.inner.read().threads[0].id;
                let abi =
                    unsafe { yggdrasil_abi::process::ThreadId::from_raw(id.as_user() as u32) };
                options::MainThread::store(&abi, buffer)
            }
        }
    }

    pub fn set_option(&self, option: ProcessOptionVariant, buffer: &[u8]) -> Result<(), Error> {
        use yggdrasil_abi::process::options;

        match option {
            ProcessOptionVariant::Name => Err(Error::ReadOnly),
            ProcessOptionVariant::Directory => {
                let value = options::Directory::load(buffer)?;
                let mut io = self.io.lock();
                io.ioctx_mut().set_cwd(value)
            }
            ProcessOptionVariant::SignalEntry => {
                let value = options::SignalEntry::load(buffer)?;
                self.set_signal_entry(value);
                Ok(())
            }
            ProcessOptionVariant::MainThread => Err(Error::ReadOnly),
        }
    }

    /// Returns the address space of the process
    pub fn space(&self) -> Arc<ProcessAddressSpace> {
        self.inner.read().space.clone().unwrap()
    }

    pub fn map_image<T, F: FnOnce(&ProcessImage) -> T>(&self, f: F) -> Option<T> {
        self.inner.read().image.as_ref().map(f)
    }

    pub fn image_base(&self) -> Option<usize> {
        self.map_image(|image| image.load_base)
    }

    pub fn strong_child(&self, id: ProcessId) -> Option<Arc<Self>> {
        self.inner.read().strong_child(id)
    }

    pub fn as_single_thread(&self) -> Option<Arc<Thread>> {
        let inner = self.inner.read();
        if inner.threads.len() != 1 {
            return None;
        }
        Some(inner.threads[0].clone())
    }

    /// Returns the process group ID of the process
    pub fn group_id(&self) -> ProcessGroupId {
        self.inner.read().group_id
    }

    /// Returns the process session ID of the process
    pub fn session_id(&self) -> ProcessId {
        self.inner.read().session_id
    }

    /// Changes the process's group ID
    pub fn set_group_id(&self, id: ProcessGroupId) {
        self.inner.write().group_id = id;
    }

    /// Changes the process's session ID
    pub fn set_session_id(&self, id: ProcessId) {
        self.inner.write().session_id = id;
    }

    // Resources

    /// Returns the current session terminal of the process, if set
    pub fn session_terminal(&self) -> Option<NodeRef> {
        self.inner.read().session_terminal.clone()
    }

    /// Changes the current session terminal of the process
    pub fn set_session_terminal(&self, node: NodeRef) {
        self.inner.write().session_terminal = Some(node);
    }

    /// Resets the current session terminal of the process
    pub fn clear_session_terminal(&self) -> Option<NodeRef> {
        self.inner.write().session_terminal.take()
    }

    /// Inherits the process information from the `parent`
    pub fn inherit(&self, parent: &Self) -> Result<(), Error> {
        let mut our_inner = self.inner.write();
        let their_inner = parent.inner.read();

        our_inner.session_id = their_inner.session_id;
        our_inner.group_id = their_inner.group_id;
        our_inner.session_terminal = their_inner.session_terminal.clone();

        Ok(())
    }

    // State

    /// Returns the [ExitCode] of the process, if it has exited
    pub fn get_exit_status(&self) -> Option<ExitCode> {
        self.exit.try_read_copy()
    }

    /// Returns `true` if the process has exited
    pub fn has_exited(&self) -> bool {
        self.exit.is_signalled()
    }

    pub fn wait_for_child(&self, id: ProcessId, flags: WaitFlags) -> Result<ExitCode, Error> {
        // TODO ESRCH analog
        let child = self
            .inner
            .read()
            .strong_child(id)
            .ok_or(Error::DoesNotExist)?;

        if let Some(status) = child.get_exit_status() {
            log::debug!("Child {id} exited with status {status:?} (didn't block)");
            self.inner.write().remove_child(id);
            return Ok(status);
        }

        if flags.contains(WaitFlags::NON_BLOCKING) {
            return Err(Error::WouldBlock);
        }

        block! {
            loop {
                if let Some(status) = child.get_exit_status() {
                    log::debug!("Child {id} exited with status {status:?}", );
                    self.inner.write().remove_child(id);
                    break status;
                }

                self.child_exit_notify.wait_reset().await;
            }
        }
    }

    fn poll_any_child_exit(
        &self,
    ) -> Option<(Arc<Process>, IrqSafeRwLockWriteGuard<'_, ProcessInner>)> {
        let read = self.inner.read();
        if let Some(child) = read.any_exited_child() {
            let write = IrqSafeRwLockReadGuard::upgrade(read);
            Some((child, write))
        } else {
            None
        }
    }

    pub fn wait_for_any_child(&self, flags: WaitFlags) -> Result<(ProcessId, ExitCode), Error> {
        if let Some((child, mut guard)) = self.poll_any_child_exit() {
            let id = child.id;
            // unwrap ok: ProcessInner tells the child already exited
            let status = child.get_exit_status().unwrap();
            log::info!("Child {id} exited with status {status:?} (didn't block)");
            guard.remove_child(id);
            return Ok((id, status));
        }

        if flags.contains(WaitFlags::NON_BLOCKING) {
            return Err(Error::WouldBlock);
        }

        block! {
            loop {
                if let Some((child, mut guard)) = self.poll_any_child_exit() {
                    let id = child.id;
                    // unwrap ok: ProcessInner tells the child already exited
                    let status = child.get_exit_status().unwrap();
                    log::info!("Child {id} exited with status {status:?}", );
                    guard.remove_child(id);
                    break (id, status);
                }

                self.child_exit_notify.wait_reset().await;
            }
        }
    }

    pub async fn wait_for_exit(&self) -> ExitCode {
        self.exit.wait_copy().await
    }

    /// Cleans up process resources
    fn cleanup(&self, mut inner: IrqSafeRwLockWriteGuard<ProcessInner>) {
        self.remove_sysfs_node();
        self.io.lock().handle_exit();
        inner.threads.clear();
        if let Some(space) = inner.space.take() {
            if let Err(err) = space.clear() {
                log::error!("Address space cleanup error: {err:?}");
            }
        }
    }

    fn remove_sysfs_node(&self) {
        if let Some(node) = self.sysfs_node.write().take() {
            node.unlink();
        }
    }

    /// Updates the signal entry point
    pub fn set_signal_entry(&self, entry: usize) -> usize {
        self.signal_entry.swap(entry, Ordering::Release)
    }

    /// Retrieves current signal entry of the process
    pub fn signal_entry(&self) -> usize {
        self.signal_entry.load(Ordering::Acquire)
    }

    /// Raises a signal for the specified process
    ///
    /// If sender is Some(id) - signal was sent by some thread in some (possibly this) process.
    /// If sender is None - signal was caused by some fault or some other non-deliberate cause.
    pub fn raise_signal(self: &Arc<Self>, sender: Option<ThreadId>, signal: Signal) {
        // If signal was raised by a thread within the process, consider the signal a
        // "synchronous-like" and deliver it to the same thread that issued it.

        let destination = {
            let inner = self.inner.read();
            if let Some(thread) = sender.and_then(|id| inner.threads.iter().find(|t| t.id == id)) {
                // Behave like a "synchronous" signal, because a thread within a process asked to
                // do something with the process itself. Make the requesting thread handle the
                // signal.
                thread.clone()
            } else {
                // Asynchronous signal not caused by any thread within the process, basically,
                // any thread can handle it, but for determinism's sake, let it be the main one
                inner.threads[0].clone()
            }
        };

        destination.raise_signal(signal);
    }

    /// Raises a signal for the specified process group
    pub fn signal_group(sender: Option<ThreadId>, group_id: ProcessGroupId, signal: Signal) {
        MANAGER.for_each(|_, proc| {
            let inner = proc.inner.read();
            if !proc.has_exited() && inner.group_id == group_id {
                log::debug!(
                    "Deliver group ({}) signal to {} ({:?}): {:?}",
                    group_id,
                    proc.id,
                    proc.name,
                    signal
                );
                drop(inner);
                proc.raise_signal(sender, signal);
            }
        });
    }

    /// Returns a [UserspaceMutex] associated with the `address`. If one does not exist, will
    /// create it.
    pub fn get_or_insert_mutex(&self, address: usize) -> Result<Arc<UserspaceMutex>, Error> {
        let space = self.space();
        let mut inner = self.inner.write();
        match inner.mutexes.entry(address) {
            btree_map::Entry::Vacant(slot) => {
                #[allow(clippy::arc_with_non_send_sync)]
                let mutex = Arc::new(UserspaceMutex::new(&space, address)?);
                slot.insert(mutex.clone());
                Ok(mutex)
            }
            btree_map::Entry::Occupied(slot) => Ok(slot.get().clone()),
        }
    }

    // Process list

    /// Returns the process with given [ProcessId], if it exists
    pub fn get(id: ProcessId) -> Option<Arc<Self>> {
        MANAGER.get(id)
    }

    pub fn handle_thread_exit(&self, thread: ThreadId, code: ExitCode) {
        log::debug!("Thread {} of process {}: {:?}", thread, self.id, code);
        let mut inner = self.inner.write();

        assert!(inner.remove_thread(thread));

        let last_thread = inner.threads.is_empty();

        if last_thread {
            log::debug!("Last thread of {} exited", self.id);
            self.cleanup(inner);
            self.exit.signal(code);
            if let Some(parent) = self.parent.as_ref().and_then(Weak::upgrade) {
                log::debug!("{}: notify parent ({}) of exit", self.id, parent.id);
                // Notify parent of child exit
                parent.child_exit_notify.signal_saturating();
            }
        }
    }

    pub async fn wait_for_thread(&self, thread: ThreadId) -> Result<ThreadEvent, Error> {
        let thread = Thread::get(thread).ok_or(Error::ProcessNotFound)?;

        // Check that the process is the thread's tracer or parent
        if !thread.is_child_of(self.id) && !thread.is_tracee_of(self.id) {
            // Cannot wait for events from unrelated threads
            log::warn!("{:?} is not tracer nor parent of {:?}", self.id, thread.id);
            return Err(Error::PermissionDenied);
        }
        let with_trace = thread.is_tracee_of(self.id);

        Ok(thread.events.wait(with_trace).await)
    }

    /// Terminates all threads with the exclusion of `except`. This may be useful when a thread
    /// wants the whole process to terminate: the asking thread itself must be exempt from this
    /// termination, as it's the one doing the cleanup.
    pub async fn terminate_others(&self, except: ThreadId) {
        let mut inner = self.inner.write();

        for thread in inner.threads.iter() {
            if thread.id == except {
                continue;
            }

            log::debug!("Terminate thread {}", thread.id);
            thread.terminate();
            thread.events.exit.wait().await;
            log::debug!("{} died", thread.id);
        }

        inner.retain_thread(except);
    }
}

impl fmt::Debug for Process {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Debug::fmt(&self.id, f)
    }
}

impl Drop for Process {
    fn drop(&mut self) {
        log::debug!("Drop Process {}", self.id);
    }
}

impl FileReadiness for Process {
    fn poll_read(&self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
        self.exit.poll(cx).map(Ok)
    }
}

impl ProcessInner {
    pub fn new(
        id: ProcessId,
        group_id: ProcessGroupId,
        space: Option<Arc<ProcessAddressSpace>>,
        image: Option<ProcessImage>,
    ) -> Self {
        Self {
            session_id: id,
            group_id,
            session_terminal: None,
            threads: Vec::new(),
            children: BTreeMap::new(),

            mutexes: BTreeMap::new(),
            image,
            space: space.clone(),
        }
    }

    pub fn register_thread(&mut self, thread: Arc<Thread>) {
        self.threads.push(thread);
    }

    pub fn register_child(&mut self, child: &Arc<Process>) {
        self.children.insert(child.id, Arc::downgrade(child));
    }

    pub fn strong_child(&self, id: ProcessId) -> Option<Arc<Process>> {
        self.children.get(&id).and_then(Weak::upgrade)
    }

    pub fn any_exited_child(&self) -> Option<Arc<Process>> {
        // TODO not really efficient, bleh
        self.children
            .values()
            .filter_map(Weak::upgrade)
            .find(|child| child.has_exited())
    }

    pub fn remove_child(&mut self, id: ProcessId) -> bool {
        self.children.remove(&id).is_some()
    }

    pub fn remove_thread(&mut self, id: ThreadId) -> bool {
        let n = self.threads.len();
        self.threads.retain(|t| t.id != id);
        n != self.threads.len()
    }

    pub fn retain_thread(&mut self, except: ThreadId) {
        self.threads.retain(|t| t.id == except)
    }
}

static MANAGER: ProcessManager = ProcessManager {
    processes: IrqSafeRwLock::new(BTreeMap::new()),
};

impl ProcessManager {
    fn register_process(&self, process: Arc<Process>) {
        self.processes.write().insert(process.id, process);
    }

    fn get(&self, id: ProcessId) -> Option<Arc<Process>> {
        self.processes.read().get(&id).cloned()
    }

    fn for_each<F: Fn(ProcessId, &Arc<Process>)>(&self, f: F) {
        for (k, v) in self.processes.read().iter() {
            f(*k, v);
        }
    }
}

impl ProcessIo {
    pub fn new() -> Self {
        Self {
            ioctx: None,
            files: FileSet::new(),
        }
    }

    pub fn handle_exit(&mut self) {
        self.files.close_all();
    }

    pub fn fork_from(&mut self, src: &Self) -> Result<(), Error> {
        let new_ioctx = IoContext::inherit(src.ioctx());
        self.set_ioctx(new_ioctx);

        for (old_fd, old_file) in src.files.iter() {
            let new_file = old_file.send()?;
            self.files.set_file(*old_fd, new_file)?;
        }

        Ok(())
    }

    /// Returns the associated [IoContext] reference
    pub fn ioctx_mut(&mut self) -> &mut IoContext {
        self.ioctx.as_mut().unwrap()
    }

    /// Returns the associated [IoContext] reference
    pub fn ioctx(&self) -> &IoContext {
        self.ioctx.as_ref().unwrap()
    }

    /// Changes the [IoContext] of this struct
    pub fn set_ioctx(&mut self, ioctx: IoContext) {
        self.ioctx = Some(ioctx);
    }
}

fn add_sysfs_node(process: &Arc<Process>) -> Arc<KObject<Weak<Process>>> {
    struct Name;
    struct Parent;

    impl StringAttributeOps for Name {
        type Data = Weak<Process>;
        const NAME: &'static str = "name";

        fn read(state: &Self::Data) -> Result<String, Error> {
            let process = state.upgrade().ok_or(Error::ProcessNotFound)?;
            Ok(process.name.clone())
        }
    }

    impl StringAttributeOps for Parent {
        type Data = Weak<Process>;
        const NAME: &'static str = "parent";

        fn read(state: &Self::Data) -> Result<String, Error> {
            let process = state.upgrade().ok_or(Error::ProcessNotFound)?;
            if let Some(parent) = process.parent.as_ref().and_then(Weak::upgrade) {
                Ok(format!("{}", parent.id))
            } else {
                Ok("0".into())
            }
        }
    }

    let name = format!("{}", process.id);
    let process = Arc::downgrade(process);
    let object = KObject::new(process);

    object.add_attribute(StringAttribute::from(Name)).ok();
    object.add_attribute(StringAttribute::from(Parent)).ok();

    if let Some(proc) = sysfs::proc() {
        proc.add_object(name, object.clone()).ok();
    }
    object
}
