use core::{
    cell::Cell,
    future::poll_fn,
    mem::size_of,
    ops::Deref,
    sync::atomic::{AtomicU32, AtomicU64, Ordering},
    task::Poll,
};

use alloc::{
    collections::BTreeMap,
    format,
    string::String,
    sync::{Arc, Weak},
};
use crossbeam_queue::SegQueue;
use futures_util::task::ArcWake;
use kernel_arch::{
    task::{Scheduler, TaskContext, TaskFrame},
    CpuImpl,
};
use libk_mm::{process::ProcessAddressSpace, PageFaultKind};
use libk_util::{
    event::BoolEvent,
    ring::LossyRingQueue,
    sync::{spin_rwlock::IrqSafeRwLock, IrqGuard, IrqSafeSpinlock, IrqSafeSpinlockGuard},
};
use yggdrasil_abi::{
    arch::SavedFrame,
    debug::{self, DebugControl, TraceEvent, TraceEventPayload, TraceFlags},
    error::Error,
    option::{OptionValue, RequestValue},
    process::{
        thread::{ThreadOptionVariant, ThreadSignalStack},
        ExitCode, ProcessId, Signal, SignalEntryData, ThreadEvent,
    },
};

use crate::{
    fs::sysfs::{
        attribute::{StringAttribute, StringAttributeOps},
        object::KObject,
    },
    task::{
        mem::ForeignPointer,
        sched::CpuQueue,
        types::{ThreadAffinity, ThreadId, ThreadState},
        TaskContextImpl,
    },
    time::monotonic_time,
};

use super::process::Process;

// type BreakpointType = <ArchitectureImpl as Architecture>::BreakpointType;
// const BREAKPOINT_VALUE: BreakpointType = ArchitectureImpl::BREAKPOINT_VALUE;

/// Provides details about how the thread is scheduled onto CPUs
pub struct ThreadSchedulingInfo {
    /// Current state
    pub state: ThreadState,

    /// Is the thread present in any queue
    pub in_queue: bool,
    /// Queue into which the thread was last put
    pub queue: Option<&'static CpuQueue>,
}

pub struct ThreadTracingInfo {
    pub tracer: Option<ProcessId>,
    pub single_step: bool,
    pub state: SavedFrame,
}

pub struct ThreadInfo {
    pub signal_stack: ThreadSignalStack,
}

pub struct ThreadEvents {
    pub exit: BoolEvent,
    pub trace: LossyRingQueue<TraceEvent>,
}

/// Describes a single thread within the system
pub struct Thread {
    /// Unique thread ID
    pub id: ThreadId,
    /// Thread name
    pub name: IrqSafeRwLock<String>,
    /// Scheduling information
    pub sched: IrqSafeSpinlock<ThreadSchedulingInfo>,
    /// Low-level context details
    pub context: Cell<TaskContextImpl>,
    process: Option<ProcessId>,
    space: Option<Arc<ProcessAddressSpace>>,
    debug: IrqSafeSpinlock<ThreadTracingInfo>,
    trace_flags: AtomicU32,
    syscall_trace_seq: AtomicU64,

    // inner: IrqSafeSpinlock<ThreadInner>,
    info: IrqSafeRwLock<ThreadInfo>,
    signal_queue: SegQueue<Signal>,
    sysfs_node: IrqSafeRwLock<Option<Arc<KObject<Weak<Thread>>>>>,

    pub events: ThreadEvents,
    pub kill: BoolEvent,

    /// CPU scheduling affinity mask
    pub affinity: ThreadAffinity,
}

// Prevents thread from getting scheduled during some critical section
#[allow(unused)]
pub struct SuspendGuard<'a>(IrqSafeSpinlockGuard<'a, ThreadSchedulingInfo>);

/// Wrapper which guarantees the thread referred to is the current one on the current CPU
#[repr(C)]
pub struct CurrentThread(Arc<Thread>, IrqGuard);

struct GlobalThreadList {
    data: BTreeMap<ThreadId, Arc<Thread>>,
}

static THREADS: IrqSafeRwLock<GlobalThreadList> = IrqSafeRwLock::new(GlobalThreadList::new());

// TODO this is ugly?
#[no_mangle]
unsafe extern "C" fn __arch_drop_thread(thread_ptr: *const Thread) {
    let thread = Arc::from_raw(thread_ptr);
    Thread::remove_from_list(thread.id);
    Arc::decrement_strong_count(thread_ptr);
}

impl Thread {
    fn new(
        id: ThreadId,
        name: Option<String>,
        process: Option<ProcessId>,
        space: Option<Arc<ProcessAddressSpace>>,
        context: TaskContextImpl,
    ) -> Arc<Self> {
        let thread = Arc::new(Self {
            id,
            name: IrqSafeRwLock::new(name.unwrap_or_default()),
            sched: IrqSafeSpinlock::new(ThreadSchedulingInfo {
                state: ThreadState::Suspended,
                in_queue: false,
                queue: None,
            }),
            debug: IrqSafeSpinlock::new(ThreadTracingInfo::default()),
            context: Cell::new(context),
            process,
            space,
            trace_flags: AtomicU32::new(0),
            syscall_trace_seq: AtomicU64::new(1),

            info: IrqSafeRwLock::new(ThreadInfo {
                signal_stack: ThreadSignalStack::default(),
            }),
            signal_queue: SegQueue::new(),
            events: ThreadEvents::new(),
            kill: BoolEvent::new(),
            sysfs_node: IrqSafeRwLock::new(None),

            affinity: ThreadAffinity::any_cpu(),
        });

        THREADS.write().insert(thread.clone());

        thread
    }

    /// Constructs a new kernel-space thread
    pub fn new_kthread<N: Into<String>>(name: N, context: TaskContextImpl) -> Arc<Self> {
        Self::new(
            ThreadId::next_kernel(),
            Some(name.into()),
            None,
            None,
            context,
        )
    }

    /// Constructs a new user-space thread
    pub fn new_uthread(
        parent: ProcessId,
        name: Option<String>,
        space: Arc<ProcessAddressSpace>,
        context: TaskContextImpl,
    ) -> Arc<Self> {
        Self::new(
            ThreadId::next_user(),
            name,
            Some(parent),
            Some(space),
            context,
        )
    }

    pub fn get_option(
        &self,
        option: ThreadOptionVariant,
        buffer: &mut [u8],
    ) -> Result<usize, Error> {
        use yggdrasil_abi::process::thread as options;

        match option {
            ThreadOptionVariant::Name => {
                let name = self.name.read();
                options::Name::store(&name.as_str(), buffer)
            }
            ThreadOptionVariant::SignalStack => {
                options::SignalStack::store(&self.signal_stack(), buffer)
            }
            // There're better ways to do this, don't ask the kernel
            ThreadOptionVariant::ThreadPointer => Err(Error::InvalidOperation),
        }
    }

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

        match option {
            ThreadOptionVariant::Name => {
                let value = options::Name::load(buffer)?;
                self.set_name(value);
                Ok(())
            }
            ThreadOptionVariant::SignalStack => {
                let value = options::SignalStack::load(buffer)?;
                if value.base.checked_add(value.size).is_none() {
                    return Err(Error::InvalidArgument);
                }
                self.set_signal_stack(value);
                Ok(())
            }
            ThreadOptionVariant::ThreadPointer => {
                let value = options::ThreadPointer::load(buffer)?;
                self.set_thread_pointer(value);
                Ok(())
            }
        }
    }

    /// Sets up arguments on the task's stack, if needed by the ABI.
    ///
    /// # Safety
    ///
    /// `sp` must be a valid writable pointer to the task's stack, growing downward.
    #[allow(unused)]
    pub unsafe fn setup_stack_header(
        space: &ProcessAddressSpace,
        mut sp: *mut usize,
        argument: usize,
    ) -> Result<*mut usize, Error> {
        #[cfg(any(target_arch = "x86", rust_analyzer))]
        {
            // Argument
            sp = sp.sub(1);
            sp.write_foreign_volatile(space, argument);
            // Return address
            sp = sp.sub(1);
            sp.write_foreign_volatile(space, 0);
        }
        Ok(sp)
    }

    /// Replaces the current context of the thread with a new one.
    ///
    /// TODO: how do I drop the old one within exec(), lol
    ///
    /// # Safety
    ///
    /// Unsafe: directly sets the thread's context.
    pub unsafe fn replace_context(&self, context: TaskContextImpl) -> TaskContextImpl {
        self.context.replace(context)
    }

    pub fn set_name(&self, name: impl Into<String>) {
        *self.name.write() = name.into();
    }

    pub fn set_thread_pointer(&self, tp: usize) {
        unsafe { (*self.context.as_ptr()).set_thread_pointer(tp) };
    }

    // Get/Set
    pub fn is_user(&self) -> bool {
        self.id.is_user()
    }

    /// Updates the thread affinity to run on a specific set of CPUs
    pub fn set_affinity(&self, affinity: u64) {
        self.affinity.set(affinity);
    }

    /// Updates the thread signal stack information
    pub fn set_signal_stack(&self, stack: ThreadSignalStack) -> ThreadSignalStack {
        let mut info = self.info.write();
        core::mem::replace(&mut info.signal_stack, stack)
    }

    /// Returns the currently set signal stack
    pub fn signal_stack(&self) -> ThreadSignalStack {
        self.info.read().signal_stack
    }

    /// Returns the thread address space (usually provided by its parent process). If none exists,
    /// panics.
    pub fn address_space(&self) -> &ProcessAddressSpace {
        self.space.as_ref().unwrap()
    }

    /// Returns the thread's parent process ID, panics if there's none
    pub fn process_id(&self) -> ProcessId {
        self.process.unwrap()
    }

    /// Returns `true` if the thread is a child of given process
    pub fn is_child_of(&self, pid: ProcessId) -> bool {
        self.process == Some(pid)
    }

    /// Returns the thread's parent process reference
    pub fn try_get_process(&self) -> Option<Arc<Process>> {
        self.process.and_then(Process::get)
    }

    /// Returns the thread's parent process reference, panics if there's none
    pub fn process(&self) -> Arc<Process> {
        Process::get(self.process.unwrap()).unwrap()
    }

    /// Removes the thread from the thread list
    pub fn remove_from_list(id: ThreadId) {
        THREADS
            .write()
            .remove(id)
            .expect("Thread was not in the thread list?");
    }

    /// Updates the thread's terminated status and wakes up any other threads waiting for it to
    /// exit
    pub fn set_terminated(&self) {
        self.events.exit.signal_saturating();
    }

    pub fn trace_event<F: TaskFrame>(&self, suspend: bool, frame: &F, payload: TraceEventPayload) {
        let mut debug = self.debug.lock();
        if debug.tracer.is_some() {
            let timestamp = monotonic_time();
            debug.store_state(frame);
            self.events.trace.write(TraceEvent {
                suspend,
                timestamp,
                payload,
            });
        }
    }

    // Signals

    /// Pushes a signal to the thread's signal queue
    pub fn raise_signal(&self, signal: Signal) -> bool {
        let is_traced = self.debug.lock().tracer.is_some();

        if is_traced && signal != Signal::Debug && signal != Signal::Killed {
            log::info!("{}: caught traced signal {signal:?}", self.id);
            // TODO
            // self.trace_event(true, );
            true
        } else {
            log::info!("{}: raise signal {signal:?}", self.id);
            self.signal_queue.push(signal);
            self.enqueue();
            false
        }
    }

    // Scheduling

    /// Changes thread state to "Ready" and inserts it into given `queue`, if it's not yet in one
    pub fn enqueue_to(&self, queue: &'static CpuQueue) {
        let mut sched = self.sched.lock();

        if sched.state == ThreadState::Terminated {
            return;
        }

        match sched.state {
            ThreadState::Running | ThreadState::Ready => {
                assert!(sched.in_queue);
            }
            ThreadState::Suspended => {
                sched.state = ThreadState::Ready;

                if !sched.in_queue {
                    assert!(sched.queue.is_none());

                    sched.in_queue = true;
                    sched.queue = Some(queue);

                    queue.push(self.id);
                }
            }
            ThreadState::Terminated => panic!("Cannot enqueue a terminated thread"),
        }
    }

    /// Changes thread state to `state` and removes it from its queue. If the thread is currently
    /// running on local CPU, will yield control to the next thread
    pub fn dequeue(&self, state: ThreadState) {
        let mut sched = self.sched.lock();

        debug_assert_ne!(state, ThreadState::Running);
        debug_assert_ne!(state, ThreadState::Ready);

        let old_state = sched.state;
        sched.state = state;

        if let Some(queue) = sched.queue {
            match (queue.is_local(), old_state) {
                (true, ThreadState::Running) => unsafe {
                    debug_assert!(sched.in_queue);
                    drop(sched);
                    queue.yield_cpu();
                },
                (false, ThreadState::Running) => {
                    debug_assert!(sched.in_queue);
                }
                (_, ThreadState::Ready) => {
                    debug_assert!(sched.in_queue);
                }
                (_, ThreadState::Suspended | ThreadState::Terminated) => {
                    todo!()
                }
            }
        } else {
            assert!(!sched.in_queue);
            assert_ne!(old_state, ThreadState::Running);
            assert_ne!(old_state, ThreadState::Ready);
        }
    }

    /// Inserts the thread as "Ready" into the best queue (based on affinity and queue load)
    pub fn enqueue(&self) {
        let queue = CpuQueue::for_affinity_mask(self.affinity.get());
        self.enqueue_to(queue);
    }

    /// Requests thread termination. This function only asks the thread to terminate, which will
    /// happen the next time it's seen by the scheduler.
    pub fn terminate(self: &Arc<Self>) {
        // Will not abort the execution: called from another thread.
        //
        // Instead of dequeueing the thread, like done with a synchronous exit (when the thread
        // exits on its own), instead, the thread needs to be **enqueued**, because we need to wake
        // it up in order to make it die itself.
        self.kill.signal_saturating();
        self.enqueue();
    }

    fn cleanup(&self) {
        // Can detach debugger now
        {
            let mut debug = self.debug.lock();
            debug.tracer = None;
            debug.single_step = false;
            self.trace_flags.store(0, Ordering::Release);
        }

        self.drop_sysfs_node();
    }

    /// Returns the current thread on the CPU.
    ///
    /// # Panics
    ///
    /// Will panic if no current thread is present. For try-style getter, see
    /// [Thread::get_current].
    #[inline]
    pub fn current() -> CurrentThread {
        Self::get_current().unwrap()
    }

    /// Returns the current thread on the CPU, if any is present
    #[inline]
    pub fn get_current() -> Option<CurrentThread> {
        // IrqGuard is held throughout
        let cpu = CpuImpl::<CpuQueue>::local();
        let thread = cpu.current_thread_id().and_then(Self::get);

        thread.map(|t| CurrentThread(t, cpu.into_guard()))
    }

    /// Returns a thread for given `id`, if such exists
    #[inline]
    pub fn get(id: ThreadId) -> Option<Arc<Thread>> {
        THREADS.read().get(id).cloned()
    }

    /// # Safety
    ///
    /// Precondition: the caller must ensure the thread is, in fact, a current one, for example,
    /// by preventing interrupts by holding an [IrqGuard].
    pub unsafe fn upgrade(weak: &Weak<Self>) -> Option<CurrentThread> {
        let guard = IrqGuard::acquire();
        let strong = weak.upgrade()?;
        Some(CurrentThread(strong, guard))
    }

    // Tracing/debugging

    /// Returns `true` if the thread is a tracee of given process
    pub fn is_tracee_of(&self, pid: ProcessId) -> bool {
        self.debug.lock().tracer == Some(pid)
    }

    pub fn attach_trace(&self, tracer: ProcessId) -> Result<(), Error> {
        let mut debug = self.debug.lock();
        if debug.tracer.is_some() {
            log::warn!("{:?}: tracer already attached", self.id);
            return Err(Error::AlreadyExists);
        }
        debug.tracer = Some(tracer);
        debug.single_step = true;
        log::info!("{:?}: tracer ({:?}) attached", self.id, tracer);
        Ok(())
    }

    pub fn set_trace_flags(&self, flags: TraceFlags) {
        let debug = self.debug.lock();
        let value = if debug.tracer.is_some() {
            flags.bits()
        } else {
            0
        };
        self.trace_flags.store(value, Ordering::Release);
    }

    pub fn trace_flags(&self) -> TraceFlags {
        TraceFlags::new(self.trace_flags.load(Ordering::Acquire))
    }

    pub fn resume(&self, single_step: bool) {
        {
            let mut debug = self.debug.lock();

            debug.single_step = single_step;
        }

        self.enqueue();
    }

    pub fn hold_suspended(&self) -> Result<SuspendGuard<'_>, Error> {
        let sched = self.sched.lock();
        if sched.state == ThreadState::Running {
            return Err(Error::InvalidOperation);
        }
        Ok(SuspendGuard(sched))
    }

    pub fn debug_control(
        &self,
        option: u32,
        buffer: &mut [u8],
        len: usize,
    ) -> Result<usize, Error> {
        // Deserialize request
        let input = &buffer[..len];
        let option = DebugControl::try_from(option)?;

        match option {
            // TODO runtime attach
            DebugControl::Attach => todo!(),
            // TODO
            DebugControl::Detach => todo!(),
            DebugControl::Resume => {
                let single_step = debug::Resume::load_request(input)?;
                self.resume(single_step);
                debug::Resume::store_response(&(), buffer)
            }
            DebugControl::SetTraceFlags => {
                let flags = debug::SetTraceFlags::load_request(buffer)?;
                self.set_trace_flags(flags);
                debug::SetTraceFlags::store_response(&(), buffer)
            }
            DebugControl::GetTraceFlags => {
                debug::GetTraceFlags::store_response(&self.trace_flags(), buffer)
            }
            DebugControl::GetRegisters => {
                let _guard = self.hold_suspended()?;
                let debug = self.debug.lock();
                debug::GetRegisters::store_response(&debug.state, buffer)
            }
            DebugControl::SetRegisters => {
                let _guard = self.hold_suspended()?;
                todo!()
            }
            DebugControl::ReadMemory => {
                let address = debug::ReadMemory::load_request(buffer)?;
                let pointer = core::ptr::with_exposed_provenance::<usize>(address);
                let _guard = self.hold_suspended()?;
                let space = self.address_space();
                let word = unsafe { pointer.try_read_foreign_volatile(space)? };
                debug::ReadMemory::store_response(&word, buffer)
            }
            DebugControl::WriteMemory => {
                todo!();
            }
        }
    }

    // sysfs operations

    pub(super) fn add_sysfs_node(self: &Arc<Self>, parent: &Arc<KObject<Weak<Process>>>) {
        let ThreadId::User(id) = self.id else {
            return;
        };

        struct Name;

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

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

        let object = KObject::new(Arc::downgrade(self));

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

        parent.add_object(format!("{id}"), object.clone()).ok();
        *self.sysfs_node.write() = Some(object);
    }

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

impl GlobalThreadList {
    pub const fn new() -> Self {
        Self {
            data: BTreeMap::new(),
        }
    }

    #[inline]
    pub fn get(&self, id: ThreadId) -> Option<&Arc<Thread>> {
        self.data.get(&id)
    }

    pub fn insert(&mut self, thread: Arc<Thread>) {
        let id = thread.id;
        debug_assert!(!self.data.contains_key(&id));
        self.data.insert(id, thread);
    }

    pub fn remove(&mut self, id: ThreadId) -> Option<Arc<Thread>> {
        self.data.remove(&id)
    }
}

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

unsafe impl Sync for Thread {}

impl ArcWake for Thread {
    fn wake_by_ref(arc_self: &Arc<Self>) {
        arc_self.clone().enqueue()
    }
}

impl CurrentThread {
    pub fn downgrade(self) -> Weak<Thread> {
        Arc::downgrade(&self.0)
    }

    pub fn raise_signal(&self, signal: Signal) {
        if self.0.raise_signal(signal) {
            // Signal was intercepted and the thread will need to be suspended
            log::info!("{}: suspending: signal intercepted by tracer", self.id);
            self.suspend().ok();
        }
    }

    // TODO point data
    pub fn trace_point<F: TaskFrame, P: FnOnce() -> TraceEventPayload>(
        &self,
        filter: TraceFlags,
        frame: &F,
        producer: P,
    ) {
        if self.trace_flags().contains(filter) {
            self.trace_event(true, frame, producer());
            self.suspend().ok();
        }
    }

    pub fn trace_syscall_entry<F: TaskFrame>(&self, frame: &F) -> u64 {
        let seq = self.syscall_trace_seq.fetch_add(1, Ordering::Relaxed);
        self.trace_point(TraceFlags::SYSCALL_ENTRY, frame, || {
            TraceEventPayload::SyscallEntry(seq)
        });
        seq
    }

    pub fn trace_syscall_exit<F: TaskFrame>(&self, frame: &F, seq: u64) {
        self.trace_point(TraceFlags::SYSCALL_EXIT, frame, || {
            TraceEventPayload::SyscallExit(seq)
        });
    }

    /// Terminate the current thread
    pub fn exit(&self, code: ExitCode) -> ! {
        self.cleanup();

        if let Some(process) = self.try_get_process() {
            process.handle_thread_exit(self.id, code);
        }
        self.dequeue(ThreadState::Terminated);
        unreachable!()
    }

    /// Terminate the parent process of the thread, including all other threads and the current
    /// thread itself
    pub fn exit_process(&self, code: ExitCode) -> ! {
        let _guard = IrqGuard::acquire();

        let process = self.process();

        let p = process.clone();

        block! {
            p.terminate_others(self.id).await;
        }
        .unwrap();

        self.exit(code)
    }

    /// Suspends a thread from further execution until it is awoken
    pub fn suspend(&self) -> Result<(), Error> {
        self.dequeue(ThreadState::Suspended);

        if !self.signal_queue.is_empty() {
            return Err(Error::Interrupted);
        }

        Ok(())
    }

    pub fn handle_single_step<F: TaskFrame>(&self, frame: &mut F) -> bool {
        {
            let debug = self.debug.lock();
            if !debug.single_step {
                frame.set_single_step(false);
                return true;
            }
        }

        self.trace_event(true, frame, TraceEventPayload::SingleStep);

        match self.suspend() {
            Ok(_) | Err(Error::Interrupted) => true,
            Err(err) => panic!("TODO: handle error in single-step suspend: {err:?}"),
        }
    }

    pub fn handle_breakpoint<F: TaskFrame>(&self, _frame: &mut F) -> bool {
        false
        // let mut debug = self.debug.lock();
        // let ip = frame.user_ip();

        // if let Some(value) = debug.breakpoints.remove(&ip) {
        //     let space = self.address_space();

        //     // Restore original code
        //     let pointer = ip as *mut BreakpointType;
        //     unsafe { pointer.write_foreign_volatile(space, value) };

        //     log::debug!(
        //         "Thread {} ({:?}) hit a breakpoint @ {:#x}, step={}",
        //         self.id,
        //         *self.name.read(),
        //         ip,
        //         debug.single_step
        //     );

        //     // TODO handle cases when no debugger is attached (clear breakpoint and resume)
        //     frame.set_single_step(true);

        //     let frame = frame.store();
        //     debug.restore_breakpoint = Some(ip);
        //     debug.saved_frame = Some(frame.clone());

        //     let debugger = debug.debugger.as_ref().unwrap();
        //     debugger.send(&DebugFrame::HitBreakpoint { frame }).ok();

        //     drop(debug);

        //     self.suspend().unwrap();

        //     true
        // } else {
        //     false
        // }
    }

    pub fn handle_page_fault(&self, address: usize, kind: PageFaultKind) -> Result<(), Error> {
        self.address_space().handle_fault(address, kind)
    }

    /// Sets up a return frame to handle a pending signal, if any is present in the task's queue.
    ///
    /// # Safety
    ///
    /// This function is only meant to be called right before returning from an userspace
    /// exception handler.
    pub unsafe fn handle_pending_signals<F: TaskFrame>(&self, frame: &mut F) {
        if !self.id.is_user() {
            return;
        }

        if let Some(signal) = self.signal_queue.pop() {
            log::debug!(
                "{} ({:?}): handle signal {signal:?}",
                self.id,
                *self.name.read()
            );

            if signal == Signal::Debug {
                // Debugger attached in runtime
                todo!()
            }

            let ip = self.process().signal_entry();
            let signal_stack = self.signal_stack();
            let sp = signal_stack.base + signal_stack.size;

            // Check if ip and sp are legible for signal entry
            if ip == 0 || signal_stack.base == 0 || signal_stack.size < 4096 {
                log::warn!("No legible signal handler for {}", self.id);
                self.exit_process(ExitCode::BySignal(Ok(signal)));
            }

            // // TODO check if really in a syscall, lol
            // let syscall_return = -(u32::from(Error::Interrupted) as isize);
            // frame.set_return_value(syscall_return as u64);

            // Setup signal frame
            // FIXME use write_foreign_*** for this
            let usp = ((sp - size_of::<SignalEntryData>()) & !0xF)
                - TaskContextImpl::SIGNAL_STACK_EXTRA_ALIGN;
            let frame_ptr = usp as *mut SignalEntryData;

            let saved_frame = frame.store();
            frame_ptr.write_foreign_volatile(
                self.address_space(),
                SignalEntryData {
                    signal,
                    frame: saved_frame,
                },
            );

            // Setup return to signal handler
            log::debug!(
                "Signal entry @ pc={:#x}, sp={:#x} (top = {:#x})",
                ip,
                usp,
                sp,
            );

            frame.set_user_sp(usp);
            frame.set_user_ip(ip);

            // Pass the frame pointer as an argument to signal handler entry
            frame.set_argument(usp as _);
        }
    }
}

impl Deref for CurrentThread {
    type Target = Arc<Thread>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

// impl ThreadTracingInfo {
//     fn set_breakpoint_inner(
//         &mut self,
//         space: &ProcessAddressSpace,
//         address: usize,
//     ) -> Result<(), Error> {
//         match self.breakpoints.entry(address) {
//             btree_map::Entry::Vacant(vacant) => {
//                 let pointer = address as *mut BreakpointType;
//
//                 // Read old code from the address space at that location
//                 let original =
//                     unsafe { (pointer as *const BreakpointType).try_read_foreign_volatile(space) }?;
//
//                 unsafe { pointer.write_foreign_volatile(space, BREAKPOINT_VALUE) };
//
//                 vacant.insert(original);
//                 Ok(())
//             }
//             // No need, breakpoint already present
//             btree_map::Entry::Occupied(_) => Err(Error::AlreadyExists),
//         }
//     }
// }

impl ThreadTracingInfo {
    pub fn store_state<F: TaskFrame>(&mut self, frame: &F) {
        self.state = frame.store();
    }
}

impl Default for ThreadTracingInfo {
    fn default() -> Self {
        Self {
            single_step: false,
            tracer: None,
            state: Default::default(),
        }
    }
}

impl ThreadEvents {
    pub fn new() -> Self {
        Self {
            exit: BoolEvent::new(),
            trace: LossyRingQueue::with_capacity(8),
        }
    }

    pub async fn wait(&self, with_trace: bool) -> ThreadEvent {
        poll_fn(|cx| {
            if with_trace {
                if let Poll::Ready(mut lock) = self.trace.poll_lock(cx) {
                    let event = unsafe { lock.read_single_unchecked() };
                    return Poll::Ready(ThreadEvent::Trace(event));
                }
            }

            if self.exit.poll(cx).is_ready() {
                Poll::Ready(ThreadEvent::Exited)
            } else {
                Poll::Pending
            }
        })
        .await
    }
}
