use super::CrashContext;
use crate::{
    minidump_cpu::RawContextCPU, minidump_format::format, thread_info::copy_u32_registers,
};
use libc::{
    REG_CSGSFS, REG_EFL, REG_R10, REG_R11, REG_R12, REG_R13, REG_R14, REG_R15, REG_R8, REG_R9,
    REG_RAX, REG_RBP, REG_RBX, REG_RCX, REG_RDI, REG_RDX, REG_RIP, REG_RSI, REG_RSP,
};
use scroll::Pwrite;

impl CrashContext {
    pub fn get_instruction_pointer(&self) -> usize {
        self.inner.context.uc_mcontext.gregs[REG_RIP as usize] as usize
    }

    pub fn get_stack_pointer(&self) -> usize {
        self.inner.context.uc_mcontext.gregs[REG_RSP as usize] as usize
    }

    pub fn fill_cpu_context(&self, out: &mut RawContextCPU) {
        out.context_flags = format::ContextFlagsAmd64::CONTEXT_AMD64_FULL.bits();

        {
            let gregs = &self.inner.context.uc_mcontext.gregs;
            out.cs = (gregs[REG_CSGSFS as usize] & 0xffff) as u16;

            out.fs = ((gregs[REG_CSGSFS as usize] >> 32) & 0xffff) as u16;
            out.gs = ((gregs[REG_CSGSFS as usize] >> 16) & 0xffff) as u16;

            out.eflags = gregs[REG_EFL as usize] as u32;

            out.rax = gregs[REG_RAX as usize] as u64;
            out.rcx = gregs[REG_RCX as usize] as u64;
            out.rdx = gregs[REG_RDX as usize] as u64;
            out.rbx = gregs[REG_RBX as usize] as u64;

            out.rsp = gregs[REG_RSP as usize] as u64;
            out.rbp = gregs[REG_RBP as usize] as u64;
            out.rsi = gregs[REG_RSI as usize] as u64;
            out.rdi = gregs[REG_RDI as usize] as u64;
            out.r8 = gregs[REG_R8 as usize] as u64;
            out.r9 = gregs[REG_R9 as usize] as u64;
            out.r10 = gregs[REG_R10 as usize] as u64;
            out.r11 = gregs[REG_R11 as usize] as u64;
            out.r12 = gregs[REG_R12 as usize] as u64;
            out.r13 = gregs[REG_R13 as usize] as u64;
            out.r14 = gregs[REG_R14 as usize] as u64;
            out.r15 = gregs[REG_R15 as usize] as u64;

            out.rip = gregs[REG_RIP as usize] as u64;
        }

        {
            let fs = &self.inner.float_state;

            let mut float_save = format::XMM_SAVE_AREA32 {
                control_word: fs.cwd,
                status_word: fs.swd,
                tag_word: fs.ftw as u8,
                error_opcode: fs.fop,
                error_offset: fs.rip as u32,
                data_offset: fs.rdp as u32,
                error_selector: 0, // We don't have this.
                data_selector: 0,  // We don't have this.
                mx_csr: fs.mxcsr,
                mx_csr_mask: fs.mxcr_mask,
                ..Default::default()
            };

            copy_u32_registers(&mut float_save.float_registers, &fs.st_space);
            copy_u32_registers(&mut float_save.xmm_registers, &fs.xmm_space);

            out.float_save
                .pwrite_with(float_save, 0, scroll::Endian::Little)
                .expect("this is impossible");
        }
    }
}
