use bitflags::bitflags;
use num_derive::FromPrimitive;

pub const PAGE_SIZE: usize = 0x1000;

#[repr(C, align(4096))]
#[derive(Clone, Debug)]
pub struct VMCSRegion {
    pub revision_id: u32,
    pub abort_indicator: u32,
    data: [u8; PAGE_SIZE - 8],
}

// (Intel Manual: 25.11.2 VMREAD, VMWRITE, and Encodings of VMCS Fields)
#[derive(FromPrimitive)]
enum VmcsAccessType {
    FULL = 0,
    HIGH = 1,
}

#[derive(FromPrimitive)]
enum VmcsType {
    CONTROL = 0,
    VMEXIT = 1,
    GUEST = 2,
    HOST = 3,
}

#[derive(FromPrimitive)]
enum VmcsWidth {
    BIT16 = 0,
    BIT64 = 1,
    BIT32 = 2,
    NATURAL = 3,
}

#[derive(FromPrimitive)]
#[allow(non_camel_case_types)]
// (Intel Manual: APPENDIX B FIELD ENCODING IN VMCS)
pub enum VmcsFields {
    // [CONTROL] fields
    // 16-bit control fields
    CTRL_VIRT_PROC_ID = encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT16, 0) as isize,
    CTRL_POSTED_INTR_N_VECTOR =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT16, 1) as isize,
    CTRL_EPTP_INDEX = encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT16, 2) as isize,
    // 64-bit control fields
    CTRL_IO_BITMAP_A_ADDR = encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT64, 0) as isize,
    CTRL_IO_BITMAP_B_ADDR = encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT64, 1) as isize,
    CTRL_MSR_BITMAP_ADDR = encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT64, 2) as isize, // control whether RDMSR or WRMSR cause VM exit
    CTRL_VMEXIT_MSR_STORE_ADDR =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT64, 3) as isize,
    CTRL_VMEXIT_MSR_LOAD_ADDR =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT64, 4) as isize,
    CTRL_VMENTRY_MSR_LOAD_ADDR =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT64, 5) as isize,
    CTRL_EXECUTIVE_VMCS_PTR =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT64, 6) as isize,
    CTRL_PML_ADDR = encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT64, 7) as isize,
    CTRL_TSC_ADDR = encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT64, 8) as isize,
    CTRL_VIRT_APIC_ADDR = encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT64, 9) as isize,
    CTRL_APIC_ACCESS_ADDR =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT64, 10) as isize,
    CTRL_POSTED_INTR_DESC_ADDR =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT64, 11) as isize,
    CTRL_VMFUNC_CTRL = encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT64, 12) as isize,
    CTRL_EPTP_PTR = encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT64, 13) as isize,
    CTRL_EOI_EXIT_BITMAP_0 =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT64, 14) as isize,
    CTRL_EOI_EXIT_BITMAP_1 =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT64, 15) as isize,
    CTRL_EOI_EXIT_BITMAP_2 =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT64, 16) as isize,
    CTRL_EOI_EXIT_BITMAP_3 =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT64, 17) as isize,
    CTRL_EPT_LIST_ADDR = encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT64, 18) as isize,
    CTRL_VMREAD_BITMAP_ADDR =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT64, 19) as isize,
    CTRL_VMWRITE_BITMAP_ADDR =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT64, 20) as isize,
    CTRL_VIRT_EXECPT_INFO_ADDR =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT64, 21) as isize,
    CTRL_XSS_EXITING_BITMAP =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT64, 22) as isize,
    CTRL_ENCLS_EXITING_BITMAP =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT64, 23) as isize,
    CTRL_TSC_MULTIPLIER = encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT64, 25) as isize,
    // 32-bit control fields
    CTRL_PIN_BASED_VM_EXEC_CTRLS =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT32, 0) as isize, // control async event handling (i.e. interrupts)
    CTRL_PRIMARY_PROCESSOR_VM_EXEC_CTRLS =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT32, 1) as isize, // control sync event handling (i.e. instruction exits)
    CTRL_EXPECTION_BITMAP = encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT32, 2) as isize, // bitmap to control exceptions that cause a VM exit
    CTRL_PAGE_FAULT_ERR_CODE_MASK =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT32, 3) as isize,
    CTRL_PAGE_FAULT_ERR_CODE_MATCH =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT32, 4) as isize,
    CTRL_CR3_TARGET_COUNT = encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT32, 5) as isize,
    CTRL_PRIMARY_VM_EXIT_CTRLS =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT32, 6) as isize,
    CTRL_VM_EXIT_MSR_STORE_COUNT =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT32, 7) as isize,
    CTRL_VM_EXIT_MSR_LOAD_COUNT =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT32, 8) as isize,
    CTRL_VM_ENTRY_CTRLS = encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT32, 9) as isize,
    CTRL_VM_ENTRY_MSR_LOAD_COUNT =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT32, 10) as isize,
    CTRL_VM_ENTRY_INTR_INFO_FIELD =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT32, 11) as isize,
    CTRL_VM_ENTRY_EXCEPTION_ERR_CODE =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT32, 12) as isize,
    CTRL_VM_ENTRY_INSTR_LEN =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT32, 13) as isize,
    CTRL_TPR_THRESHOLD = encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT32, 14) as isize,
    CTRL_SECONDARY_PROCESSOR_VM_EXEC_CTRLS =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT32, 15) as isize,
    CTRL_PLE_GAP = encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT32, 16) as isize,
    CTRL_PLE_WINDOW = encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::BIT32, 17) as isize,
    // natural control fields
    CTRL_CR0_GUEST_HOST_MASK =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::NATURAL, 0) as isize, // control executions of insts that access cr0
    CTRL_CR4_GUEST_HOST_MASK =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::NATURAL, 1) as isize,
    CTRL_CR0_READ_SHADOW =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::NATURAL, 2) as isize, // control executions of insts that access cr0
    CTRL_CR4_READ_SHADOW =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::NATURAL, 3) as isize,
    CTRL_CR3_TARGET_VALUE_0 =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::NATURAL, 4) as isize,
    CTRL_CR3_TARGET_VALUE_1 =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::NATURAL, 5) as isize,
    CTRL_CR3_TARGET_VALUE_2 =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::NATURAL, 6) as isize,
    CTRL_CR3_TARGET_VALUE_3 =
        encode_vmcs_field_full(VmcsType::CONTROL, VmcsWidth::NATURAL, 7) as isize,

    // [VMEXIT] fields read-only
    // No 16-bit vmexit fields
    // 64-bit vmexit fields
    VMEXIT_GUEST_PHY_ADDR = encode_vmcs_field_full(VmcsType::VMEXIT, VmcsWidth::BIT64, 0) as isize,
    // 32-bit vmexit fields
    VMEXIT_INSTR_ERR = encode_vmcs_field_full(VmcsType::VMEXIT, VmcsWidth::BIT32, 0) as isize,
    VMEXIT_EXIT_REASON = encode_vmcs_field_full(VmcsType::VMEXIT, VmcsWidth::BIT32, 1) as isize,
    VMEXIT_INT_INFO = encode_vmcs_field_full(VmcsType::VMEXIT, VmcsWidth::BIT32, 2) as isize,
    VMEXIT_INT_ERR_CODE = encode_vmcs_field_full(VmcsType::VMEXIT, VmcsWidth::BIT32, 3) as isize,
    VMEXIT_IDT_VECTOR_INFO = encode_vmcs_field_full(VmcsType::VMEXIT, VmcsWidth::BIT32, 4) as isize,
    VMEXIT_IDT_VECTOR_ERR_CODE =
        encode_vmcs_field_full(VmcsType::VMEXIT, VmcsWidth::BIT32, 5) as isize,
    VMEXIT_INSTR_LEN = encode_vmcs_field_full(VmcsType::VMEXIT, VmcsWidth::BIT32, 6) as isize,
    VMEXIT_INSTR_INFO = encode_vmcs_field_full(VmcsType::VMEXIT, VmcsWidth::BIT32, 7) as isize,
    // natural vmexit fields
    VMEXIT_QUALIFICATION = encode_vmcs_field_full(VmcsType::VMEXIT, VmcsWidth::NATURAL, 0) as isize,
    VMEXIT_IO_RCX = encode_vmcs_field_full(VmcsType::VMEXIT, VmcsWidth::NATURAL, 1) as isize,
    VMEXIT_IO_RSX = encode_vmcs_field_full(VmcsType::VMEXIT, VmcsWidth::NATURAL, 2) as isize,
    VMEXIT_IO_RDI = encode_vmcs_field_full(VmcsType::VMEXIT, VmcsWidth::NATURAL, 3) as isize,
    VMEXIT_IO_RIP = encode_vmcs_field_full(VmcsType::VMEXIT, VmcsWidth::NATURAL, 4) as isize,
    VMEXIT_GUEST_LINEAR_ADDR =
        encode_vmcs_field_full(VmcsType::VMEXIT, VmcsWidth::NATURAL, 5) as isize,

    // [GUEST] fields
    // 16-bit guest fields
    GUEST_ES_SELECTOR = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT16, 0) as isize,
    GUEST_CS_SELECTOR = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT16, 1) as isize,
    GUEST_SS_SELECTOR = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT16, 2) as isize,
    GUEST_DS_SELECTOR = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT16, 3) as isize,
    GUEST_FS_SELECTOR = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT16, 4) as isize,
    GUEST_GS_SELECTOR = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT16, 5) as isize,
    GUEST_LDTR_SELECTOR = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT16, 6) as isize,
    GUEST_TR_SELECTOR = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT16, 7) as isize,
    GUEST_INTR_STATUS = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT16, 8) as isize,
    GUEST_PML_INDEX = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT16, 9) as isize,
    // 64-bit guest fields
    GUEST_VMCS_LINK_PTR = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT64, 0) as isize,
    GUEST_DEBUGCTL = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT64, 1) as isize,
    GUEST_PAT = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT64, 2) as isize,
    GUEST_EFER = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT64, 3) as isize,
    GUEST_PERF_GLOBAL_CTRL = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT64, 4) as isize,
    GUEST_PDPTE0 = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT64, 5) as isize,
    GUEST_PDPTE1 = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT64, 6) as isize,
    GUEST_PDPTE2 = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT64, 7) as isize,
    GUEST_PDPTE3 = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT64, 8) as isize,
    // 32-bit guest fields
    GUEST_ES_LIMIT = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT32, 0) as isize,
    GUEST_CS_LIMIT = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT32, 1) as isize,
    GUEST_SS_LIMIT = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT32, 2) as isize,
    GUEST_DS_LIMIT = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT32, 3) as isize,
    GUEST_FS_LIMIT = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT32, 4) as isize,
    GUEST_GS_LIMIT = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT32, 5) as isize,
    GUEST_LDTR_LIMIT = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT32, 6) as isize,
    GUEST_TR_LIMIT = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT32, 7) as isize,
    GUEST_GDTR_LIMIT = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT32, 8) as isize,
    GUEST_IDTR_LIMIT = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT32, 9) as isize,
    GUEST_ES_ACCESS_RIGHTS = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT32, 10) as isize,
    GUEST_CS_ACCESS_RIGHTS = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT32, 11) as isize,
    GUEST_SS_ACCESS_RIGHTS = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT32, 12) as isize,
    GUEST_DS_ACCESS_RIGHTS = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT32, 13) as isize,
    GUEST_FS_ACCESS_RIGHTS = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT32, 14) as isize,
    GUEST_GS_ACCESS_RIGHTS = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT32, 15) as isize,
    GUEST_LDTR_ACCESS_RIGHTS =
        encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT32, 16) as isize,
    GUEST_TR_ACCESS_RIGHTS = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT32, 17) as isize,
    GUEST_INTERRUPTIBILITY_STATE =
        encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT32, 18) as isize,
    GUEST_ACTIVITY_STATE = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT32, 19) as isize,
    GUEST_SMBASE = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT32, 20) as isize,
    GUEST_SYSENTER_CS = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::BIT32, 21) as isize,
    GUEST_VMX_PREEMPT_TIMER_VALUE = 0x482E_isize,
    // natural guest fields
    GUEST_CR0 = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::NATURAL, 0) as isize,
    GUEST_CR3 = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::NATURAL, 1) as isize,
    GUEST_CR4 = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::NATURAL, 2) as isize,
    GUEST_ES_BASE = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::NATURAL, 3) as isize,
    GUEST_CS_BASE = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::NATURAL, 4) as isize,
    GUEST_SS_BASE = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::NATURAL, 5) as isize,
    GUEST_DS_BASE = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::NATURAL, 6) as isize,
    GUEST_FS_BASE = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::NATURAL, 7) as isize,
    GUEST_GS_BASE = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::NATURAL, 8) as isize,
    GUEST_LDTR_BASE = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::NATURAL, 9) as isize,
    GUEST_TR_BASE = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::NATURAL, 10) as isize,
    GUEST_GDTR_BASE = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::NATURAL, 11) as isize,
    GUEST_IDTR_BASE = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::NATURAL, 12) as isize,
    GUEST_DR7 = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::NATURAL, 13) as isize,
    GUEST_RSP = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::NATURAL, 14) as isize,
    GUEST_RIP = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::NATURAL, 15) as isize,
    GUEST_RFLAGS = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::NATURAL, 16) as isize,
    GUEST_PENDING_DBG_EXCEPTIONS =
        encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::NATURAL, 17) as isize,
    GUEST_SYSENTER_ESP = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::NATURAL, 18) as isize,
    GUEST_SYSENTER_EIP = encode_vmcs_field_full(VmcsType::GUEST, VmcsWidth::NATURAL, 19) as isize,

    // [HOST] fields
    // host 16 bit fields
    HOST_ES_SELECTOR = encode_vmcs_field_full(VmcsType::HOST, VmcsWidth::BIT16, 0) as isize,
    HOST_CS_SELECTOR = encode_vmcs_field_full(VmcsType::HOST, VmcsWidth::BIT16, 1) as isize,
    HOST_SS_SELECTOR = encode_vmcs_field_full(VmcsType::HOST, VmcsWidth::BIT16, 2) as isize,
    HOST_DS_SELECTOR = encode_vmcs_field_full(VmcsType::HOST, VmcsWidth::BIT16, 3) as isize,
    HOST_FS_SELECTOR = encode_vmcs_field_full(VmcsType::HOST, VmcsWidth::BIT16, 4) as isize,
    HOST_GS_SELECTOR = encode_vmcs_field_full(VmcsType::HOST, VmcsWidth::BIT16, 5) as isize,
    HOST_TR_SELECTOR = encode_vmcs_field_full(VmcsType::HOST, VmcsWidth::BIT16, 6) as isize,
    // host 64 bit fields
    HOST_PAT = encode_vmcs_field_full(VmcsType::HOST, VmcsWidth::BIT64, 0) as isize,
    HOST_EFER = encode_vmcs_field_full(VmcsType::HOST, VmcsWidth::BIT64, 1) as isize,
    HOST_PERF_GLOBAL_CTRL = encode_vmcs_field_full(VmcsType::HOST, VmcsWidth::BIT64, 2) as isize,
    // host 32 bit fields
    HOST_SYSENTER_CS = encode_vmcs_field_full(VmcsType::HOST, VmcsWidth::BIT32, 0) as isize,
    // host natural fields
    HOST_CR0 = encode_vmcs_field_full(VmcsType::HOST, VmcsWidth::NATURAL, 0) as isize,
    HOST_CR3 = encode_vmcs_field_full(VmcsType::HOST, VmcsWidth::NATURAL, 1) as isize,
    HOST_CR4 = encode_vmcs_field_full(VmcsType::HOST, VmcsWidth::NATURAL, 2) as isize,
    HOST_FS_BASE = encode_vmcs_field_full(VmcsType::HOST, VmcsWidth::NATURAL, 3) as isize,
    HOST_GS_BASE = encode_vmcs_field_full(VmcsType::HOST, VmcsWidth::NATURAL, 4) as isize,
    HOST_TR_BASE = encode_vmcs_field_full(VmcsType::HOST, VmcsWidth::NATURAL, 5) as isize,
    HOST_GDTR_BASE = encode_vmcs_field_full(VmcsType::HOST, VmcsWidth::NATURAL, 6) as isize,
    HOST_IDTR_BASE = encode_vmcs_field_full(VmcsType::HOST, VmcsWidth::NATURAL, 7) as isize,
    HOST_SYSENTER_ESP = encode_vmcs_field_full(VmcsType::HOST, VmcsWidth::NATURAL, 8) as isize,
    HOST_SYSENTER_EIP = encode_vmcs_field_full(VmcsType::HOST, VmcsWidth::NATURAL, 9) as isize,
    HOST_RSP = encode_vmcs_field_full(VmcsType::HOST, VmcsWidth::NATURAL, 10) as isize,
    HOST_RIP = encode_vmcs_field_full(VmcsType::HOST, VmcsWidth::NATURAL, 11) as isize,
}

// (Intel Manual: 25.6 VM-EXECUTION CONTROL FIELDS)
bitflags! {
    // (Intel Manual: 25.6.1 Pin-Based VM-Execution Controls)
    #[allow(non_camel_case_types)]
    pub struct VmxPinBasedExecuteCtrl: u32 {
        const EXTERNAL_INTERRUPT_EXITING = 1 << 0; // external interrupts cause VM exits
        const NMI_EXITING = 1 << 3; // non-maskable interrupts (NMIs) cause VM exits.
        const VIRTUAL_NMIS = 1 << 5; // NMIs are never blocked and the “blocking by NMI” bit (bit 3) in the interruptibility-state field indicates “virtual-NMI blocking”
        const VMX_PREEMPTION_TIMER = 1 << 6; // the VMX-preemption timer counts down in VMX non-root operation
        const PROCESS_POSTED_INTERRUPTS = 1 << 7; // he processor treats interrupts with the posted-interrupt notification vector
    }

    // (Intel Manual: 25.6.2 Processor-Based VM-Execution Controls)
    #[allow(non_camel_case_types)]
    pub struct VmxPrimaryProcessBasedExecuteCtrl: u32{
        const INTERRUPT_WINDOW_EXITING = 1 << 2; // VM exits on interrupt window RFLAGS.IF = 1
        const USE_TSC_OFFSETTING = 1 << 3; // TSC offsetting is enabled
        const HLT_EXITING = 1 << 7;
        const INVLPG_EXITING = 1 << 9;
        const MWAIT_EXITING = 1 << 10;
        const RDPMC_EXITING = 1 << 11;
        const RDTSC_EXITING = 1 << 12;
        const CR3_LOAD_EXITING = 1 << 15;
        const CR3_STR_EXITING = 1 << 16;
        const CR8_LOAD_EXITING = 1 << 19;
        const CR8_STR_EXITING = 1 << 20;
        const USE_TPR_SHADOW = 1 << 21;
        const NMI_WINDOW_EXITING = 1 << 22;
        const MOV_DR_EXITING = 1 << 23;
        const UNCOND_IO_EXITING = 1 << 24;
        const USE_IO_BITMAPS = 1 << 25;
        const MONITOR_TRAP_FLAG = 1 << 27;
        const USE_MSR_BITMAPS = 1 << 28;
        const MONITOR_EXITING = 1 << 29;
        const PAUSE_EXITING = 1 << 30;
        const ACTIVATE_SECONDARY_CONTROLS = 1 << 31;
    }

    // (Intel Manual: 25.6.2 Processor-Based VM-Execution Controls)
    pub struct VmxSecondaryProcessBasedExecuteCtrl: u32{
        const VIRT_APIC_ACCESS = 1 << 0;
        const ENABLE_EPT = 1 << 1;
        const DESCRIPTOR_TABLE_EXITING = 1 << 2;
        const ENABLE_RDTSCP = 1 << 3;
        const VIRT_X2APIC_MODE = 1 << 4;
        const ENABLE_VPID = 1 << 5;
        const WBINVD_EXITING = 1 << 6;
        const UNRESTRICTED_GUEST = 1 << 7;
        const APCI_REGISTER_VIRT = 1 << 8;
        const VIRT_INTR_DELIVERY = 1 << 9;
        const PAUSE_LOOP_EXITING = 1 << 10;
        const RDRAND_EXITING = 1 << 11;
        const ENABLE_INVPCID = 1 << 12;
        const ENABLE_VM_FUNCTIONS = 1 << 13;
        const VMCS_SHADOWING = 1 << 14;
        const ENABLE_ENCLS_EXITING = 1 << 15;
        const RDSEED_EXITING = 1 << 16;
        const ENABLE_PML = 1 << 17;
        const EPT_VIOLATION_VE = 1 << 18;
        const CONCEAL_VMX_FROM_PT = 1 << 19;
        const ENABLE_XSAVES_XRSTORS = 1 << 20;
        const PASID_TRANSLATION = 1 << 21;
        const MODE_BASED_EPT_EXEC = 1 << 22;
        const SUB_PAGE_WRITE_PERM = 1 << 23;
        const PT_USE_GUEST_PYH_ADDR = 1 << 24;
        const USE_TSC_SCALING = 1 << 25;
        const ENABLE_USER_WAIT_PAUSE = 1 << 26;
        const ENABLE_PCONFIG = 1 << 27;
        const ENABLE_ENCLV_EXITING = 1 << 28;
        const VMM_BUS_LOCK_DETECTION = 1 << 30;
        const INST_TIMEOUT = 1 << 31;
    }

    // (Intel Manual: 25.7.1 VM-Exit Controls)
    #[allow(non_camel_case_types)]
    pub struct VmxPrimaryExitCtrl: u32 {
        const SAVE_DBG_CTRLS = 1 << 2;
        const HOST_ADDR_SPACE_SIZE = 1 << 9; // determines if a virtual processor will be in 64-bit mode after a VM exit
        const LOAD_IA32_PERF_GLOBAL_CTRL = 1 << 12;
        const ACK_INTERRUPT_ON_EXIT = 1 << 15;
        const SAVE_IA32_PAT = 1 << 18;
        const LOAD_IA32_PAT = 1 << 19;
        const SAVE_IA32_EFER = 1 << 20;
        const LOAD_IA32_EFER = 1 << 21;
        const SAVE_VMX_PREEMPT_TIMER_VALUE = 1 << 22;
        const CLEAR_IA32_BNDCFGS = 1 << 23;
        const CONCEAL_VMX_FROM_PT = 1 << 24;
        const CLEAR_IA32_RTIT_CTL = 1 << 25;
        const CLEAR_IA32_LBR_CTL = 1 << 26;
        const CLEAR_UINV = 1 << 27;
        const LOAD_CET_STATE = 1 << 28;
        const LOAD_PKRS = 1 << 29;
        const SAVE_IA32_PERF_GLOBAL_CTL = 1 << 30;
        const ACTIVATE_SECONDARY_CONTROLS = 1 << 31;
    }

    // (Intel Manual: 25.8.1 VM-Entry Controls)
    #[allow(non_camel_case_types)]
    pub struct VmxEntryCtrl: u32 {
        const LOAD_DBG_CTRLS = 1 << 2;
        const IA32E_MODE_GUEST = 1 << 9;
        const ENTRY_TO_SMM = 1 << 10;
        const DEACTIVATE_DUAL_MONITOR = 1 << 11;
        const LOAD_IA32_PERF_GLOBAL_CTRL = 1 << 13;
        const LOAD_IA32_PAT = 1 << 14;
        const LOAD_IA32_EFER = 1 << 15;
        const LOAD_IA32_BNDCFGS = 1 << 16;
        const CONCEAL_VMX_FROM_PT = 1 << 17;
        const LOAD_IA32_RTIT_CTL = 1 << 18;
        const LOAD_UINV = 1 << 19;
        const LOAD_CET_STATE = 1 << 20;
        const LOAD_PKRS = 1 << 21;
        const LOAD_IA32_PERF_GLOBAL_CTL = 1 << 22;
    }

}

#[derive(FromPrimitive)]
#[allow(non_camel_case_types)]
pub enum VmxExitReason {
    EXCEPTION_OR_NMI = 0,
    EXTERNAL_INTERRUPT = 1,
    TRIPLE_FAULT = 2,
    INIT_SIGNAL = 3,
    SIPI = 4,
    IO_SMI = 5,
    OTHER_SMI = 6,
    INTERRUPT_WINDOW = 7,
    NMI_WINDOW = 8,
    TASK_SWITCH = 9,
    CPUID = 10,
    GETSEC = 11,
    HLT = 12,
    INVD = 13,
    INVLPG = 14,
    RDPMC = 15,
    RDTSC = 16,
    RSM = 17,
    VMCALL = 18,
    VMCLEAR = 19,
    VMLAUNCH = 20,
    VMPTRLD = 21,
    VMPTRST = 22,
    VMREAD = 23,
    VMRESUME = 24,
    VMWRITE = 25,
    VMXOFF = 26,
    VMXON = 27,
    CR_ACCESS = 28,
    DR_ACCESS = 29,
    IO_INSTRUCTION = 30,
    RDMSR = 31,
    WRMSR = 32,
    VM_ENTRY_FAILURE_INVALID_GUEST_STATE = 33,
    VM_ENTRY_FAILURE_MSR_LOADING = 34,
    MWAIT = 36,
    MONITOR_TRAP_FLAG = 37,
    MONITOR = 39,
    PAUSE = 40,
    VM_ENTRY_FAILURE_MACHINE_CHECK_EVENT = 41,
    TPR_BELOW_THRESHOLD = 43,
    APIC_ACCESS = 44,
    VIRTUALIZED_EOI = 45,
    ACCESS_GDTR_OR_IDTR = 46,
    ACCESS_LDTR_OR_TR = 47,
    EPT_VIOLATION = 48,
    EPT_MISCONFIG = 49,
    INVEPT = 50,
    RDTSCP = 51,
    VMX_PREEMPTION_TIMER_EXPIRED = 52,
    INVVPID = 53,
    WBINVD = 54,
    XSETBV = 55,
    APIC_WRITE = 56,
    RDRAND = 57,
    INVPCID = 58,
    VMFUNC = 59,
    ENCLS = 60,
    RDSEED = 61,
    PML_FULL = 62,
    XSAVES = 63,
    XRSTORS = 64,
}

impl From<i32> for VmxExitReason {
    fn from(num: i32) -> Self {
        match num {
            0 => VmxExitReason::EXCEPTION_OR_NMI,
            1 => VmxExitReason::EXTERNAL_INTERRUPT,
            2 => VmxExitReason::TRIPLE_FAULT,
            3 => VmxExitReason::INIT_SIGNAL,
            4 => VmxExitReason::SIPI,
            5 => VmxExitReason::IO_SMI,
            6 => VmxExitReason::OTHER_SMI,
            7 => VmxExitReason::INTERRUPT_WINDOW,
            8 => VmxExitReason::NMI_WINDOW,
            9 => VmxExitReason::TASK_SWITCH,
            10 => VmxExitReason::CPUID,
            11 => VmxExitReason::GETSEC,
            12 => VmxExitReason::HLT,
            13 => VmxExitReason::INVD,
            14 => VmxExitReason::INVLPG,
            15 => VmxExitReason::RDPMC,
            16 => VmxExitReason::RDTSC,
            17 => VmxExitReason::RSM,
            18 => VmxExitReason::VMCALL,
            19 => VmxExitReason::VMCLEAR,
            20 => VmxExitReason::VMLAUNCH,
            21 => VmxExitReason::VMPTRLD,
            22 => VmxExitReason::VMPTRST,
            23 => VmxExitReason::VMREAD,
            24 => VmxExitReason::VMRESUME,
            25 => VmxExitReason::VMWRITE,
            26 => VmxExitReason::VMXOFF,
            27 => VmxExitReason::VMXON,
            28 => VmxExitReason::CR_ACCESS,
            29 => VmxExitReason::DR_ACCESS,
            30 => VmxExitReason::IO_INSTRUCTION,
            31 => VmxExitReason::RDMSR,
            32 => VmxExitReason::WRMSR,
            33 => VmxExitReason::VM_ENTRY_FAILURE_INVALID_GUEST_STATE,
            34 => VmxExitReason::VM_ENTRY_FAILURE_MSR_LOADING,
            36 => VmxExitReason::MWAIT,
            37 => VmxExitReason::MONITOR_TRAP_FLAG,
            39 => VmxExitReason::MONITOR,
            40 => VmxExitReason::PAUSE,
            41 => VmxExitReason::VM_ENTRY_FAILURE_MACHINE_CHECK_EVENT,
            43 => VmxExitReason::TPR_BELOW_THRESHOLD,
            44 => VmxExitReason::APIC_ACCESS,
            45 => VmxExitReason::VIRTUALIZED_EOI,
            46 => VmxExitReason::ACCESS_GDTR_OR_IDTR,
            47 => VmxExitReason::ACCESS_LDTR_OR_TR,
            48 => VmxExitReason::EPT_VIOLATION,
            49 => VmxExitReason::EPT_MISCONFIG,
            50 => VmxExitReason::INVEPT,
            51 => VmxExitReason::RDTSCP,
            52 => VmxExitReason::VMX_PREEMPTION_TIMER_EXPIRED,
            53 => VmxExitReason::INVVPID,
            54 => VmxExitReason::WBINVD,
            55 => VmxExitReason::XSETBV,
            56 => VmxExitReason::APIC_WRITE,
            57 => VmxExitReason::RDRAND,
            58 => VmxExitReason::INVPCID,
            59 => VmxExitReason::VMFUNC,
            60 => VmxExitReason::ENCLS,
            61 => VmxExitReason::RDSEED,
            62 => VmxExitReason::PML_FULL,
            63 => VmxExitReason::XSAVES,
            64 => VmxExitReason::XRSTORS,
            _ => panic!("Invalid VmxExitReason number: {}", num),
        }
    }
}

const fn encode_vmcs_field(
    access_type: VmcsAccessType,
    vmcs_type: VmcsType,
    vmcs_width: VmcsWidth,
    index: u32,
) -> u32 {
    let mut encoding: u32 = 0;
    encoding |=
        (access_type as u32) | (index) << 1 | (vmcs_type as u32) << 10 | (vmcs_width as u32) << 13;
    return encoding;
}

const fn encode_vmcs_field_full(vmcs_type: VmcsType, vmcs_width: VmcsWidth, index: u32) -> u32 {
    encode_vmcs_field(VmcsAccessType::FULL, vmcs_type, vmcs_width, index)
}

// fn decode_vmcs_field(field: u32) -> (VmcsAccessType, VmcsType, VmcsWidth, u16){
//     (FromPrimitive::from_u32(field & 1).unwrap() ,
//         FromPrimitive::from_u32((field>>10) & 0x3).unwrap(),
//         FromPrimitive::from_u32((field>>13) & 0x3).unwrap(),
//         ((field>>1) & 0x1ff) as u16
//     )
// }
