use core::pin::Pin;

use super::{CPUStatus, GDTEntry};

#[derive(Debug, Clone)]
pub enum UserTLS {
    /// TODO: This is not used yet.
    #[allow(dead_code)]
    TLS64(u64),
    TLS32 {
        base: u64,
        desc: GDTEntry,
    },
}

impl UserTLS {
    /// # Return
    /// Returns the TLS descriptor and the index of the TLS segment.
    pub fn new32(base: u32, limit: u32, is_limit_in_pages: bool) -> (Self, u32) {
        let flags = if is_limit_in_pages { 0xc } else { 0x4 };

        (
            Self::TLS32 {
                base: base as u64,
                desc: GDTEntry::new(base, limit, 0xf2, flags),
            },
            7,
        )
    }

    pub fn load(&self, cpu_status: Pin<&mut CPUStatus>) {
        match self {
            Self::TLS64(base) => {
                const IA32_KERNEL_GS_BASE: u32 = 0xc0000102;
                super::wrmsr(IA32_KERNEL_GS_BASE, *base);
            }
            Self::TLS32 { base, desc } => {
                unsafe {
                    // SAFETY: We don't move the CPUStatus object.
                    let cpu_mut = cpu_status.get_unchecked_mut();
                    cpu_mut.set_tls32(*desc);
                }

                const IA32_KERNEL_GS_BASE: u32 = 0xc0000102;
                super::wrmsr(IA32_KERNEL_GS_BASE, *base);
            }
        }
    }
}

pub unsafe fn load_interrupt_stack(cpu_status: Pin<&mut CPUStatus>, stack: u64) {
    // SAFETY: We don't move the CPUStatus object.
    cpu_status.get_unchecked_mut().set_rsp0(stack);
}
