/*
Copyright (C) 2018-2019 de4dot@gmail.com

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

use crate::enum_utils::to_code;
use crate::iced_constants::IcedConstants;
use core::hash::{Hash, Hasher};
use pyo3::class::basic::CompareOp;
use pyo3::exceptions::PyValueError;
use pyo3::prelude::*;
use pyo3::PyObjectProtocol;
use std::collections::hash_map::DefaultHasher;

/// Opcode info, returned by :class:`Instruction.op_code` or created by the constructor
///
/// Args:
///     code (:class:`Code`): Code value
///
/// Examples:
///
/// .. testcode::
///
///     from iced_x86 import *
///
///     op_code = OpCodeInfo(Code.EVEX_VMOVAPD_YMM_K1Z_YMMM256)
///     assert op_code.op_code_string == "EVEX.256.66.0F.W1 28 /r"
///     assert op_code.encoding == EncodingKind.EVEX
///     assert OpCodeInfo(Code.SUB_R8_RM8).op_code == 0x2A
///     assert OpCodeInfo(Code.CVTPI2PS_XMM_MMM64).op_code == 0x2A
#[pyclass(module = "_iced_x86_py")]
#[text_signature = "(code, /)"]
pub(crate) struct OpCodeInfo {
	info: &'static iced_x86::OpCodeInfo,
}

#[pymethods]
impl OpCodeInfo {
	#[new]
	pub(crate) fn new(code: u32) -> PyResult<Self> {
		Ok(Self { info: to_code(code)?.op_code() })
	}

	/// :class:`Code`: Gets the code (a :class:`Code` enum value)
	///
	/// Examples:
	///
	/// .. testcode::
	///
	///     from iced_x86 import *
	///
	///     op_code = OpCodeInfo(Code.EVEX_VMOVAPD_YMM_K1Z_YMMM256)
	///     assert op_code.code == Code.EVEX_VMOVAPD_YMM_K1Z_YMMM256
	#[getter]
	fn code(&self) -> u32 {
		self.info.code() as u32
	}

	/// :class:`Mnemonic`: Gets the mnemonic (a :class:`Mnemonic` enum value)
	///
	/// Examples:
	///
	/// .. testcode::
	///
	///     from iced_x86 import *
	///
	///     op_code = OpCodeInfo(Code.EVEX_VMOVAPD_YMM_K1Z_YMMM256)
	///     assert op_code.mnemonic == Mnemonic.VMOVAPD
	#[getter]
	fn mnemonic(&self) -> u32 {
		self.info.mnemonic() as u32
	}

	/// :class:`EncodingKind`: Gets the encoding (an :class:`EncodingKind` enum value)
	///
	/// Examples:
	///
	/// .. testcode::
	///
	///     from iced_x86 import *
	///
	///     op_code = OpCodeInfo(Code.EVEX_VMOVAPD_YMM_K1Z_YMMM256)
	///     assert op_code.encoding == EncodingKind.EVEX
	#[getter]
	fn encoding(&self) -> u32 {
		self.info.encoding() as u32
	}

	/// bool: ``True`` if it's an instruction, ``False`` if it's eg. :class:`Code.INVALID`, ``db``, ``dw``, ``dd``, ``dq``
	///
	/// Examples:
	///
	/// .. testcode::
	///
	///     from iced_x86 import *
	///
	///     assert OpCodeInfo(Code.EVEX_VMOVAPD_YMM_K1Z_YMMM256).is_instruction
	///     assert not OpCodeInfo(Code.INVALID).is_instruction
	///     assert not OpCodeInfo(Code.DECLAREBYTE).is_instruction
	#[getter]
	fn is_instruction(&self) -> bool {
		self.info.is_instruction()
	}

	/// bool: ``True`` if it's an instruction available in 16-bit mode
	#[getter]
	fn mode16(&self) -> bool {
		self.info.mode16()
	}

	/// bool: ``True`` if it's an instruction available in 32-bit mode
	#[getter]
	fn mode32(&self) -> bool {
		self.info.mode32()
	}

	/// bool: ``True`` if it's an instruction available in 64-bit mode
	#[getter]
	fn mode64(&self) -> bool {
		self.info.mode64()
	}

	/// bool: ``True`` if an ``FWAIT`` (``9B``) instruction is added before the instruction
	#[getter]
	fn fwait(&self) -> bool {
		self.info.fwait()
	}

	/// int: (``u8``) (Legacy encoding) Gets the required operand size (16,32,64) or 0
	#[getter]
	fn operand_size(&self) -> u32 {
		self.info.operand_size()
	}

	/// int: (``u8``) (Legacy encoding) Gets the required address size (16,32,64) or 0
	#[getter]
	fn address_size(&self) -> u32 {
		self.info.address_size()
	}

	/// int: (``u8``) (VEX/XOP/EVEX) ``L`` / ``L'L`` value or default value if :class:`OpCodeInfo.is_lig` is ``True``
	#[getter]
	fn l(&self) -> u32 {
		self.info.l()
	}

	/// int: (``u8``) (VEX/XOP/EVEX) ``W`` value or default value if :class:`OpCodeInfo.is_wig` or :class:`OpCodeInfo.is_wig32` is ``True``
	#[getter]
	fn w(&self) -> u32 {
		self.info.w()
	}

	/// bool: (VEX/XOP/EVEX) ``True`` if the ``L`` / ``L'L`` fields are ignored.
	///
	/// EVEX: if reg-only ops and ``{er}`` (``EVEX.b`` is set), ``L'L`` is the rounding control and not ignored.
	#[getter]
	fn is_lig(&self) -> bool {
		self.info.is_lig()
	}

	/// bool: (VEX/XOP/EVEX) ``True`` if the ``W`` field is ignored in 16/32/64-bit modes
	#[getter]
	fn is_wig(&self) -> bool {
		self.info.is_wig()
	}

	/// bool: (VEX/XOP/EVEX) ``True`` if the ``W`` field is ignored in 16/32-bit modes (but not 64-bit mode)
	#[getter]
	fn is_wig32(&self) -> bool {
		self.info.is_wig32()
	}

	/// :class:`TupleType`: (EVEX) Gets the tuple type (a :class:`TupleType` enum value)
	#[getter]
	fn tuple_type(&self) -> u32 {
		self.info.tuple_type() as u32
	}

	/// :class:`MemorySize`: If it has a memory operand, gets the :class:`MemorySize` (non-broadcast memory type)
	#[getter]
	fn memory_size(&self) -> u32 {
		self.info.memory_size() as u32
	}

	/// :class:`MemorySize`: If it has a memory operand, gets the :class:`MemorySize` (broadcast memory type)
	#[getter]
	fn broadcast_memory_size(&self) -> u32 {
		self.info.broadcast_memory_size() as u32
	}

	/// bool: (EVEX) ``True`` if the instruction supports broadcasting (``EVEX.b`` bit) (if it has a memory operand)
	#[getter]
	fn can_broadcast(&self) -> bool {
		self.info.can_broadcast()
	}

	/// bool: (EVEX) ``True`` if the instruction supports rounding control
	#[getter]
	fn can_use_rounding_control(&self) -> bool {
		self.info.can_use_rounding_control()
	}

	/// bool: (EVEX) ``True`` if the instruction supports suppress all exceptions
	#[getter]
	fn can_suppress_all_exceptions(&self) -> bool {
		self.info.can_suppress_all_exceptions()
	}

	/// bool: (EVEX) ``True`` if an op mask register can be used
	#[getter]
	fn can_use_op_mask_register(&self) -> bool {
		self.info.can_use_op_mask_register()
	}

	/// bool: (EVEX) ``True`` if a non-zero op mask register must be used
	#[getter]
	fn require_op_mask_register(&self) -> bool {
		self.info.require_op_mask_register()
	}

	/// bool: (EVEX) ``True`` if the instruction supports zeroing masking (if one of the op mask registers ``K1``-``K7`` is used and destination operand is not a memory operand)
	#[getter]
	fn can_use_zeroing_masking(&self) -> bool {
		self.info.can_use_zeroing_masking()
	}

	/// bool: ``True`` if the ``LOCK`` (``F0``) prefix can be used
	#[getter]
	fn can_use_lock_prefix(&self) -> bool {
		self.info.can_use_lock_prefix()
	}

	/// bool: ``True`` if the ``XACQUIRE`` (``F2``) prefix can be used
	#[getter]
	fn can_use_xacquire_prefix(&self) -> bool {
		self.info.can_use_xacquire_prefix()
	}

	/// bool: ``True`` if the ``XRELEASE`` (``F3``) prefix can be used
	#[getter]
	fn can_use_xrelease_prefix(&self) -> bool {
		self.info.can_use_xrelease_prefix()
	}

	/// bool: ``True`` if the ``REP`` / ``REPE`` (``F3``) prefixes can be used
	#[getter]
	fn can_use_rep_prefix(&self) -> bool {
		self.info.can_use_rep_prefix()
	}

	/// bool: ``True`` if the ``REPNE`` (``F2``) prefix can be used
	#[getter]
	fn can_use_repne_prefix(&self) -> bool {
		self.info.can_use_repne_prefix()
	}

	/// bool: ``True`` if the ``BND`` (``F2``) prefix can be used
	#[getter]
	fn can_use_bnd_prefix(&self) -> bool {
		self.info.can_use_bnd_prefix()
	}

	/// bool: ``True`` if the ``HINT-TAKEN`` (``3E``) and ``HINT-NOT-TAKEN`` (``2E``) prefixes can be used
	#[getter]
	fn can_use_hint_taken_prefix(&self) -> bool {
		self.info.can_use_hint_taken_prefix()
	}

	/// bool: ``True`` if the ``NOTRACK`` (``3E``) prefix can be used
	#[getter]
	fn can_use_notrack_prefix(&self) -> bool {
		self.info.can_use_notrack_prefix()
	}

	/// bool: ``True`` if rounding control is ignored (#UD is not generated)
	#[getter]
	fn ignores_rounding_control(&self) -> bool {
		self.info.ignores_rounding_control()
	}

	/// bool: ``True`` if the ``LOCK`` prefix can be used as an extra register bit (bit 3) to access registers 8-15 without a ``REX`` prefix (eg. in 32-bit mode)
	#[getter]
	fn amd_lock_reg_bit(&self) -> bool {
		self.info.amd_lock_reg_bit()
	}

	/// bool: ``True`` if the default operand size is 64 in 64-bit mode. A ``66`` prefix can switch to 16-bit operand size.
	#[getter]
	fn default_op_size64(&self) -> bool {
		self.info.default_op_size64()
	}

	/// bool: ``True`` if the operand size is always 64 in 64-bit mode. A ``66`` prefix is ignored.
	#[getter]
	fn force_op_size64(&self) -> bool {
		self.info.force_op_size64()
	}

	/// bool: ``True`` if the Intel decoder forces 64-bit operand size. A ``66`` prefix is ignored.
	#[getter]
	fn intel_force_op_size64(&self) -> bool {
		self.info.intel_force_op_size64()
	}

	/// bool: ``True`` if it can only be executed when CPL=0
	#[getter]
	fn must_be_cpl0(&self) -> bool {
		self.info.must_be_cpl0()
	}

	/// bool: ``True`` if it can be executed when CPL=0
	#[getter]
	fn cpl0(&self) -> bool {
		self.info.cpl0()
	}

	/// bool: ``True`` if it can be executed when CPL=1
	#[getter]
	fn cpl1(&self) -> bool {
		self.info.cpl1()
	}

	/// bool: ``True`` if it can be executed when CPL=2
	#[getter]
	fn cpl2(&self) -> bool {
		self.info.cpl2()
	}

	/// bool: ``True`` if it can be executed when CPL=3
	#[getter]
	fn cpl3(&self) -> bool {
		self.info.cpl3()
	}

	/// bool: ``True`` if the instruction accesses the I/O address space (eg. ``IN``, ``OUT``, ``INS``, ``OUTS``)
	#[getter]
	fn is_input_output(&self) -> bool {
		self.info.is_input_output()
	}

	/// bool: ``True`` if it's one of the many nop instructions (does not include FPU nop instructions, eg. ``FNOP``)
	#[getter]
	fn is_nop(&self) -> bool {
		self.info.is_nop()
	}

	/// bool: ``True`` if it's one of the many reserved nop instructions (eg. ``0F0D``, ``0F18-0F1F``)
	#[getter]
	fn is_reserved_nop(&self) -> bool {
		self.info.is_reserved_nop()
	}

	/// bool: ``True`` if it's a serializing instruction (Intel CPUs)
	#[getter]
	fn is_serializing_intel(&self) -> bool {
		self.info.is_serializing_intel()
	}

	/// bool: ``True`` if it's a serializing instruction (AMD CPUs)
	#[getter]
	fn is_serializing_amd(&self) -> bool {
		self.info.is_serializing_amd()
	}

	/// bool: ``True`` if the instruction requires either CPL=0 or CPL<=3 depending on some CPU option (eg. ``CR4.TSD``, ``CR4.PCE``, ``CR4.UMIP``)
	#[getter]
	fn may_require_cpl0(&self) -> bool {
		self.info.may_require_cpl0()
	}

	/// bool: ``True`` if it's a tracked ``JMP``/``CALL`` indirect instruction (CET)
	#[getter]
	fn is_cet_tracked(&self) -> bool {
		self.info.is_cet_tracked()
	}

	/// bool: ``True`` if it's a non-temporal hint memory access (eg. ``MOVNTDQ``)
	#[getter]
	fn is_non_temporal(&self) -> bool {
		self.info.is_non_temporal()
	}

	/// bool: ``True`` if it's a no-wait FPU instruction, eg. ``FNINIT``
	#[getter]
	fn is_fpu_no_wait(&self) -> bool {
		self.info.is_fpu_no_wait()
	}

	/// bool: ``True`` if the mod bits are ignored and it's assumed ``modrm[7:6] == 11b``
	#[getter]
	fn ignores_mod_bits(&self) -> bool {
		self.info.ignores_mod_bits()
	}

	/// bool: ``True`` if the ``66`` prefix is not allowed (it will #UD)
	#[getter]
	fn no66(&self) -> bool {
		self.info.no66()
	}

	/// bool: ``True`` if the ``F2``/``F3`` prefixes aren't allowed
	#[getter]
	fn nfx(&self) -> bool {
		self.info.nfx()
	}

	/// bool: ``True`` if the index reg's reg-num (vsib op) (if any) and register ops' reg-nums must be unique,
	/// eg. ``MNEMONIC XMM1,YMM1,[RAX+ZMM1*2]`` is invalid. Registers = ``XMM``/``YMM``/``ZMM``/``TMM``.
	#[getter]
	fn requires_unique_reg_nums(&self) -> bool {
		self.info.requires_unique_reg_nums()
	}

	/// bool: ``True`` if it's a privileged instruction (all CPL=0 instructions (except ``VMCALL``) and IOPL instructions ``IN``, ``INS``, ``OUT``, ``OUTS``, ``CLI``, ``STI``)
	#[getter]
	fn is_privileged(&self) -> bool {
		self.info.is_privileged()
	}

	/// bool: ``True`` if it reads/writes too many registers
	#[getter]
	fn is_save_restore(&self) -> bool {
		self.info.is_save_restore()
	}

	/// bool: ``True`` if it's an instruction that implicitly uses the stack register, eg. ``CALL``, ``POP``, etc
	#[getter]
	fn is_stack_instruction(&self) -> bool {
		self.info.is_stack_instruction()
	}

	/// bool: ``True`` if the instruction doesn't read the segment register if it uses a memory operand
	#[getter]
	fn ignores_segment(&self) -> bool {
		self.info.ignores_segment()
	}

	/// bool: ``True`` if the op mask register is read and written (instead of just read). This also implies that it can't be ``K0``.
	#[getter]
	fn is_op_mask_read_write(&self) -> bool {
		self.info.is_op_mask_read_write()
	}

	/// bool: ``True`` if it can be executed in real mode
	#[getter]
	fn real_mode(&self) -> bool {
		self.info.real_mode()
	}

	/// bool: ``True`` if it can be executed in protected mode
	#[getter]
	fn protected_mode(&self) -> bool {
		self.info.protected_mode()
	}

	/// bool: ``True`` if it can be executed in virtual 8086 mode
	#[getter]
	fn virtual8086_mode(&self) -> bool {
		self.info.virtual8086_mode()
	}

	/// bool: ``True`` if it can be executed in compatibility mode
	#[getter]
	fn compatibility_mode(&self) -> bool {
		self.info.compatibility_mode()
	}

	/// bool: ``True`` if it can be executed in 64-bit mode
	#[getter]
	fn long_mode(&self) -> bool {
		self.info.long_mode()
	}

	/// bool: ``True`` if it can be used outside SMM
	#[getter]
	fn use_outside_smm(&self) -> bool {
		self.info.use_outside_smm()
	}

	/// bool: ``True`` if it can be used in SMM
	#[getter]
	fn use_in_smm(&self) -> bool {
		self.info.use_in_smm()
	}

	/// bool: ``True`` if it can be used outside an enclave (SGX)
	#[getter]
	fn use_outside_enclave_sgx(&self) -> bool {
		self.info.use_outside_enclave_sgx()
	}

	/// bool: ``True`` if it can be used inside an enclave (SGX1)
	#[getter]
	fn use_in_enclave_sgx1(&self) -> bool {
		self.info.use_in_enclave_sgx1()
	}

	/// bool: ``True`` if it can be used inside an enclave (SGX2)
	#[getter]
	fn use_in_enclave_sgx2(&self) -> bool {
		self.info.use_in_enclave_sgx2()
	}

	/// bool: ``True`` if it can be used outside VMX operation
	#[getter]
	fn use_outside_vmx_op(&self) -> bool {
		self.info.use_outside_vmx_op()
	}

	/// bool: ``True`` if it can be used in VMX root operation
	#[getter]
	fn use_in_vmx_root_op(&self) -> bool {
		self.info.use_in_vmx_root_op()
	}

	/// bool: ``True`` if it can be used in VMX non-root operation
	#[getter]
	fn use_in_vmx_non_root_op(&self) -> bool {
		self.info.use_in_vmx_non_root_op()
	}

	/// bool: ``True`` if it can be used outside SEAM
	#[getter]
	fn use_outside_seam(&self) -> bool {
		self.info.use_outside_seam()
	}

	/// bool: ``True`` if it can be used in SEAM
	#[getter]
	fn use_in_seam(&self) -> bool {
		self.info.use_in_seam()
	}

	/// bool: ``True`` if #UD is generated in TDX non-root operation
	#[getter]
	fn tdx_non_root_gen_ud(&self) -> bool {
		self.info.tdx_non_root_gen_ud()
	}

	/// bool: ``True`` if #VE is generated in TDX non-root operation
	#[getter]
	fn tdx_non_root_gen_ve(&self) -> bool {
		self.info.tdx_non_root_gen_ve()
	}

	/// bool: ``True`` if an exception (eg. #GP(0), #VE) may be generated in TDX non-root operation
	#[getter]
	fn tdx_non_root_may_gen_ex(&self) -> bool {
		self.info.tdx_non_root_may_gen_ex()
	}

	/// bool: (Intel VMX) ``True`` if it causes a VM exit in VMX non-root operation
	#[getter]
	fn intel_vm_exit(&self) -> bool {
		self.info.intel_vm_exit()
	}

	/// bool: (Intel VMX) ``True`` if it may cause a VM exit in VMX non-root operation
	#[getter]
	fn intel_may_vm_exit(&self) -> bool {
		self.info.intel_may_vm_exit()
	}

	/// bool: (Intel VMX) ``True`` if it causes an SMM VM exit in VMX root operation (if dual-monitor treatment is activated)
	#[getter]
	fn intel_smm_vm_exit(&self) -> bool {
		self.info.intel_smm_vm_exit()
	}

	/// bool: (AMD SVM) ``True`` if it causes a #VMEXIT in guest mode
	#[getter]
	fn amd_vm_exit(&self) -> bool {
		self.info.amd_vm_exit()
	}

	/// bool: (AMD SVM) ``True`` if it may cause a #VMEXIT in guest mode
	#[getter]
	fn amd_may_vm_exit(&self) -> bool {
		self.info.amd_may_vm_exit()
	}

	/// bool: ``True`` if it causes a TSX abort inside a TSX transaction
	#[getter]
	fn tsx_abort(&self) -> bool {
		self.info.tsx_abort()
	}

	/// bool: ``True`` if it causes a TSX abort inside a TSX transaction depending on the implementation
	#[getter]
	fn tsx_impl_abort(&self) -> bool {
		self.info.tsx_impl_abort()
	}

	/// bool: ``True`` if it may cause a TSX abort inside a TSX transaction depending on some condition
	#[getter]
	fn tsx_may_abort(&self) -> bool {
		self.info.tsx_may_abort()
	}

	/// bool: ``True`` if it's decoded by iced's 16-bit Intel decoder
	#[getter]
	fn intel_decoder16(&self) -> bool {
		self.info.intel_decoder16()
	}

	/// bool: ``True`` if it's decoded by iced's 32-bit Intel decoder
	#[getter]
	fn intel_decoder32(&self) -> bool {
		self.info.intel_decoder32()
	}

	/// bool: ``True`` if it's decoded by iced's 64-bit Intel decoder
	#[getter]
	fn intel_decoder64(&self) -> bool {
		self.info.intel_decoder64()
	}

	/// bool: ``True`` if it's decoded by iced's 16-bit AMD decoder
	#[getter]
	fn amd_decoder16(&self) -> bool {
		self.info.amd_decoder16()
	}

	/// bool: ``True`` if it's decoded by iced's 32-bit AMD decoder
	#[getter]
	fn amd_decoder32(&self) -> bool {
		self.info.amd_decoder32()
	}

	/// bool: ``True`` if it's decoded by iced's 64-bit AMD decoder
	#[getter]
	fn amd_decoder64(&self) -> bool {
		self.info.amd_decoder64()
	}

	/// :class:`DecoderOptions`: Gets the decoder option that's needed to decode the instruction or :class:`DecoderOptions.NONE`.
	#[getter]
	fn decoder_option(&self) -> u32 {
		self.info.decoder_option()
	}

	/// :class:`OpCodeTableKind`: Gets the opcode table (a :class:`OpCodeTableKind` enum value)
	#[getter]
	fn table(&self) -> u32 {
		self.info.table() as u32
	}

	/// :class:`MandatoryPrefix`: Gets the mandatory prefix (a :class:`MandatoryPrefix` enum value)
	#[getter]
	fn mandatory_prefix(&self) -> u32 {
		self.info.mandatory_prefix() as u32
	}

	/// int: (``u32``) Gets the opcode byte(s). The low byte(s) of this value is the opcode. The length is in :class:`OpCodeInfo.op_code_len`.
	/// It doesn't include the table value, see :class:`OpCodeInfo.table`.
	///
	/// Examples:
	///
	/// .. testcode::
	///
	///     from iced_x86 import *
	///
	///     assert OpCodeInfo(Code.FFREEP_STI).op_code == 0xDFC0
	///     assert OpCodeInfo(Code.VMRUNW).op_code == 0x01D8
	///     assert OpCodeInfo(Code.SUB_R8_RM8).op_code == 0x2A
	///     assert OpCodeInfo(Code.CVTPI2PS_XMM_MMM64).op_code == 0x2A
	#[getter]
	fn op_code(&self) -> u32 {
		self.info.op_code()
	}

	/// int: (``u8``) Gets the length of the opcode bytes (:class:`OpCodeInfo.op_code`). The low bytes is the opcode value.
	///
	/// Examples:
	///
	/// .. testcode::
	///
	///     from iced_x86 import *
	///
	///     assert OpCodeInfo(Code.FFREEP_STI).op_code_len == 2
	///     assert OpCodeInfo(Code.VMRUNW).op_code_len == 2
	///     assert OpCodeInfo(Code.SUB_R8_RM8).op_code_len == 1
	///     assert OpCodeInfo(Code.CVTPI2PS_XMM_MMM64).op_code_len == 1
	#[getter]
	fn op_code_len(&self) -> u32 {
		self.info.op_code_len()
	}

	/// bool: ``True`` if it's part of a group
	#[getter]
	fn is_group(&self) -> bool {
		self.info.is_group()
	}

	/// int: (``i8``) Group index (0-7) or -1. If it's 0-7, it's stored in the ``reg`` field of the ``modrm`` byte.
	#[getter]
	fn group_index(&self) -> i32 {
		self.info.group_index()
	}

	/// bool: ``True`` if it's part of a modrm.rm group
	#[getter]
	fn is_rm_group(&self) -> bool {
		self.info.is_rm_group()
	}

	/// int: (``i8``) Group index (0-7) or -1. If it's 0-7, it's stored in the ``rm`` field of the ``modrm`` byte.
	#[getter]
	fn rm_group_index(&self) -> i32 {
		self.info.rm_group_index()
	}

	/// int: (``u8``) Gets the number of operands
	#[getter]
	fn op_count(&self) -> u32 {
		self.info.op_count()
	}

	/// :class:`OpCodeOperandKind`: Gets operand #0's opkind (a :class:`OpCodeOperandKind` enum value)
	#[getter]
	fn op0_kind(&self) -> u32 {
		self.info.op0_kind() as u32
	}

	/// :class:`OpCodeOperandKind`: Gets operand #1's opkind (a :class:`OpCodeOperandKind` enum value)
	#[getter]
	fn op1_kind(&self) -> u32 {
		self.info.op1_kind() as u32
	}

	/// :class:`OpCodeOperandKind`: Gets operand #2's opkind (a :class:`OpCodeOperandKind` enum value)
	#[getter]
	fn op2_kind(&self) -> u32 {
		self.info.op2_kind() as u32
	}

	/// :class:`OpCodeOperandKind`: Gets operand #3's opkind (a :class:`OpCodeOperandKind` enum value)
	#[getter]
	fn op3_kind(&self) -> u32 {
		self.info.op3_kind() as u32
	}

	/// :class:`OpCodeOperandKind`: Gets operand #4's opkind (a :class:`OpCodeOperandKind` enum value)
	#[getter]
	fn op4_kind(&self) -> u32 {
		self.info.op4_kind() as u32
	}

	/// Gets an operand's opkind (a :class:`OpCodeOperandKind` enum value)
	///
	/// Args:
	///     `operand` (int): Operand number, 0-4
	///
	/// Returns:
	///     :class:`OpCodeOperandKind`: Operand kind
	///
	/// Raises:
	///     ValueError: If `operand` is invalid
	#[text_signature = "($self, operand, /)"]
	fn op_kind(&self, operand: u32) -> PyResult<u32> {
		if operand < IcedConstants::MAX_OP_COUNT as u32 {
			Ok(self.info.op_kind(operand) as u32)
		} else {
			Err(PyValueError::new_err("Invalid operand"))
		}
	}

	/// Gets all operand kinds (a list of :class:`OpCodeOperandKind` enum values)
	///
	/// Returns:
	///     List[:class:`OpCodeOperandKind`]: All operand kinds
	#[text_signature = "($self, /)"]
	fn op_kinds(&self) -> Vec<u32> {
		self.info.op_kinds().iter().map(|x| *x as u32).collect()
	}

	/// Checks if the instruction is available in 16-bit mode, 32-bit mode or 64-bit mode
	///
	/// Args:
	///     `bitness` (int): 16, 32 or 64
	///
	/// Returns:
	///     bool: ``True`` if it's available in the mode
	#[text_signature = "($self, bitness, /)"]
	fn is_available_in_mode(&self, bitness: u32) -> bool {
		self.info.is_available_in_mode(bitness)
	}

	/// str: Gets the opcode string, eg. ``VEX.128.66.0F38.W0 78 /r``, see also :class:`OpCodeInfo.instruction_string`
	///
	/// Examples:
	///
	/// .. testcode::
	///
	///     from iced_x86 import *
	///
	///     op_code = OpCodeInfo(Code.EVEX_VMOVAPD_YMM_K1Z_YMMM256)
	///     assert op_code.op_code_string == "EVEX.256.66.0F.W1 28 /r"
	#[getter]
	fn op_code_string(&self) -> &str {
		self.info.op_code_string()
	}

	/// str: Gets the instruction string, eg. ``VPBROADCASTB xmm1, xmm2/m8``, see also :class:`OpCodeInfo.op_code_string`
	///
	/// Examples:
	///
	/// .. testcode::
	///
	///     from iced_x86 import *
	///
	///     op_code = OpCodeInfo(Code.EVEX_VMOVAPD_YMM_K1Z_YMMM256)
	///     assert op_code.instruction_string == "VMOVAPD ymm1 {k1}{z}, ymm2/m256"
	#[getter]
	fn instruction_string(&self) -> &str {
		self.info.instruction_string()
	}
}

#[pyproto]
impl PyObjectProtocol for OpCodeInfo {
	fn __repr__(&self) -> &str {
		self.info.instruction_string()
	}

	fn __str__(&self) -> &str {
		self.info.instruction_string()
	}

	fn __format__(&self, format_spec: &str) -> PyResult<&str> {
		match format_spec {
			"" | "i" => Ok(self.info.instruction_string()),
			"o" => Ok(self.info.op_code_string()),
			_ => Err(PyValueError::new_err(format!("Unknown format specifier '{}'", format_spec))),
		}
	}

	fn __richcmp__(&self, other: PyRef<OpCodeInfo>, op: CompareOp) -> PyObject {
		match op {
			CompareOp::Eq => (self.info.code() == other.info.code()).into_py(other.py()),
			CompareOp::Ne => (self.info.code() != other.info.code()).into_py(other.py()),
			_ => other.py().NotImplemented(),
		}
	}

	fn __hash__(&self) -> u64 {
		let mut hasher = DefaultHasher::new();
		self.info.code().hash(&mut hasher);
		hasher.finish()
	}
}
