-- SPDX-License-Identifier: MIT
-- Copyright (C) 2018-present iced project and contributors

describe("OpCodeInfo", function()
	local Code = require("iced_x86.Code")
	local DecoderOptions = require("iced_x86.DecoderOptions")
	local EncodingKind = require("iced_x86.EncodingKind")
	local MandatoryPrefix = require("iced_x86.MandatoryPrefix")
	local MemorySize = require("iced_x86.MemorySize")
	local Mnemonic = require("iced_x86.Mnemonic")
	local MvexConvFn = require("iced_x86.MvexConvFn")
	local MvexEHBit = require("iced_x86.MvexEHBit")
	local MvexTupleTypeLutKind = require("iced_x86.MvexTupleTypeLutKind")
	local OpCodeInfo = require("iced_x86.OpCodeInfo")
	local OpCodeOperandKind = require("iced_x86.OpCodeOperandKind")
	local OpCodeTableKind = require("iced_x86.OpCodeTableKind")
	local TupleType = require("iced_x86.TupleType")

	it("props", function()
		local op_code = OpCodeInfo.new(Code.EVEX_Vmovapd_ymm_k1z_ymmm256)

		assert.equals(Code.EVEX_Vmovapd_ymm_k1z_ymmm256, op_code:code())
		assert.equals(Mnemonic.Vmovapd, op_code:mnemonic())
		assert.equals(EncodingKind.EVEX, op_code:encoding())
		assert.is_true(op_code:is_instruction())
		assert.is_true(op_code:mode16())
		assert.is_true(op_code:mode32())
		assert.is_true(op_code:mode64())
		assert.is_false(op_code:fwait())
		assert.equals(0, op_code:operand_size())
		assert.equals(0, op_code:address_size())
		assert.equals(1, op_code:l())
		assert.equals(1, op_code:w())
		assert.is_false(op_code:is_lig())
		assert.is_false(op_code:is_wig())
		assert.is_false(op_code:is_wig32())
		assert.equals(TupleType.N32, op_code:tuple_type())
		assert.equals(MvexEHBit.None, op_code:mvex_eh_bit())
		assert.is_false(op_code:mvex_can_use_eviction_hint())
		assert.is_false(op_code:mvex_can_use_imm_rounding_control())
		assert.is_false(op_code:mvex_ignores_op_mask_register())
		assert.is_false(op_code:mvex_no_sae_rc())
		assert.equals(MvexTupleTypeLutKind.Int32, op_code:mvex_tuple_type_lut_kind())
		assert.equals(MvexConvFn.None, op_code:mvex_conversion_func())
		assert.equals(0, op_code:mvex_valid_conversion_funcs_mask())
		assert.equals(0, op_code:mvex_valid_swizzle_funcs_mask())
		assert.equals(MemorySize.Packed256_Float64, op_code:memory_size())
		assert.equals(MemorySize.Unknown, op_code:broadcast_memory_size())
		assert.is_false(op_code:can_broadcast())
		assert.is_false(op_code:can_use_rounding_control())
		assert.is_false(op_code:can_suppress_all_exceptions())
		assert.is_true(op_code:can_use_op_mask_register())
		assert.is_false(op_code:require_op_mask_register())
		assert.is_true(op_code:can_use_zeroing_masking())
		assert.is_false(op_code:can_use_lock_prefix())
		assert.is_false(op_code:can_use_xacquire_prefix())
		assert.is_false(op_code:can_use_xrelease_prefix())
		assert.is_false(op_code:can_use_rep_prefix())
		assert.is_false(op_code:can_use_repne_prefix())
		assert.is_false(op_code:can_use_bnd_prefix())
		assert.is_false(op_code:can_use_hint_taken_prefix())
		assert.is_false(op_code:can_use_notrack_prefix())
		assert.is_false(op_code:ignores_rounding_control())
		assert.is_false(op_code:amd_lock_reg_bit())
		assert.is_false(op_code:default_op_size64())
		assert.is_false(op_code:force_op_size64())
		assert.is_false(op_code:intel_force_op_size64())
		assert.is_false(op_code:must_be_cpl0())
		assert.is_true(op_code:cpl0())
		assert.is_true(op_code:cpl1())
		assert.is_true(op_code:cpl2())
		assert.is_true(op_code:cpl3())
		assert.is_false(op_code:is_input_output())
		assert.is_false(op_code:is_nop())
		assert.is_false(op_code:is_reserved_nop())
		assert.is_false(op_code:is_serializing_intel())
		assert.is_false(op_code:is_serializing_amd())
		assert.is_false(op_code:may_require_cpl0())
		assert.is_false(op_code:is_cet_tracked())
		assert.is_false(op_code:is_non_temporal())
		assert.is_false(op_code:is_fpu_no_wait())
		assert.is_false(op_code:ignores_mod_bits())
		assert.is_false(op_code:no66())
		assert.is_false(op_code:nfx())
		assert.is_false(op_code:requires_unique_reg_nums())
		assert.is_false(op_code:requires_unique_dest_reg_num())
		assert.is_false(op_code:is_privileged())
		assert.is_false(op_code:is_save_restore())
		assert.is_false(op_code:is_stack_instruction())
		assert.is_false(op_code:ignores_segment())
		assert.is_false(op_code:is_op_mask_read_write())
		assert.is_false(op_code:real_mode())
		assert.is_true(op_code:protected_mode())
		assert.is_false(op_code:virtual8086_mode())
		assert.is_true(op_code:compatibility_mode())
		assert.is_true(op_code:long_mode())
		assert.is_true(op_code:use_outside_smm())
		assert.is_true(op_code:use_in_smm())
		assert.is_true(op_code:use_outside_enclave_sgx())
		assert.is_true(op_code:use_in_enclave_sgx1())
		assert.is_true(op_code:use_in_enclave_sgx2())
		assert.is_true(op_code:use_outside_vmx_op())
		assert.is_true(op_code:use_in_vmx_root_op())
		assert.is_true(op_code:use_in_vmx_non_root_op())
		assert.is_true(op_code:use_outside_seam())
		assert.is_true(op_code:use_in_seam())
		assert.is_false(op_code:tdx_non_root_gen_ud())
		assert.is_false(op_code:tdx_non_root_gen_ve())
		assert.is_false(op_code:tdx_non_root_may_gen_ex())
		assert.is_false(op_code:intel_vm_exit())
		assert.is_false(op_code:intel_may_vm_exit())
		assert.is_false(op_code:intel_smm_vm_exit())
		assert.is_false(op_code:amd_vm_exit())
		assert.is_false(op_code:amd_may_vm_exit())
		assert.is_false(op_code:tsx_abort())
		assert.is_false(op_code:tsx_impl_abort())
		assert.is_false(op_code:tsx_may_abort())
		assert.is_true(op_code:intel_decoder16())
		assert.is_true(op_code:intel_decoder32())
		assert.is_true(op_code:intel_decoder64())
		assert.is_true(op_code:amd_decoder16())
		assert.is_true(op_code:amd_decoder32())
		assert.is_true(op_code:amd_decoder64())
		assert.equals(DecoderOptions.None, op_code:decoder_option())
		assert.equals(OpCodeTableKind.T0F, op_code:table())
		assert.equals(MandatoryPrefix.P66, op_code:mandatory_prefix())
		assert.equals(0x28, op_code:op_code())
		assert.equals(1, op_code:op_code_len())
		assert.is_false(op_code:is_group())
		assert.equals(-1, op_code:group_index())
		assert.is_false(op_code:is_rm_group())
		assert.equals(-1, op_code:rm_group_index())
		assert.equals(2, op_code:op_count())
		assert.equals(OpCodeOperandKind.ymm_reg, op_code:op0_kind())
		assert.equals(OpCodeOperandKind.ymm_or_mem, op_code:op1_kind())
		assert.equals(OpCodeOperandKind.None, op_code:op2_kind())
		assert.equals(OpCodeOperandKind.None, op_code:op3_kind())
		assert.equals(OpCodeOperandKind.None, op_code:op4_kind())
		assert.equals(op_code:op0_kind(), op_code:op_kind(0))
		assert.equals(op_code:op1_kind(), op_code:op_kind(1))
		assert.equals(op_code:op2_kind(), op_code:op_kind(2))
		assert.equals(op_code:op3_kind(), op_code:op_kind(3))
		assert.equals(op_code:op4_kind(), op_code:op_kind(4))
		assert.same({ OpCodeOperandKind.ymm_reg, OpCodeOperandKind.ymm_or_mem }, op_code:op_kinds())
		assert.is_false(op_code:is_available_in_mode(1))
		assert.is_true(op_code:is_available_in_mode(16))
		assert.is_true(op_code:is_available_in_mode(32))
		assert.is_true(op_code:is_available_in_mode(64))
		assert.equals("EVEX.256.66.0F.W1 28 /r", op_code:op_code_string())
		assert.equals("VMOVAPD ymm1 {k1}{z}, ymm2/m256", op_code:instruction_string())
		assert.equals("VMOVAPD ymm1 {k1}{z}, ymm2/m256", tostring(op_code))

		assert.has_error(function()
			op_code:op_kind(0xFF)
		end)
		assert.has_error(function()
			op_code:op_kind(-0x80000001)
		end)
		assert.has_error(function()
			op_code:op_kind(0x100000000)
		end)
		assert.has_error(function()
			op_code:is_available_in_mode(-0x80000001)
		end)
		assert.has_error(function()
			op_code:is_available_in_mode(0x100000000)
		end)
	end)

	it("invalid Code value", function()
		assert.has_error(function()
			OpCodeInfo.new(0x789A)
		end)
	end)

	it("eq", function()
		local op_code1 = OpCodeInfo.new(Code.EVEX_Vmovapd_ymm_k1z_ymmm256)
		local op_code2 = OpCodeInfo.new(Code.EVEX_Vmovapd_ymm_k1z_ymmm256)
		local op_code3 = OpCodeInfo.new(Code.Add_AL_imm8)
		assert.is_true(op_code1 == op_code2)
		assert.is_true(op_code2 == op_code1)
		assert.is_false(op_code1 == op_code3)
		assert.is_false(op_code3 == op_code1)
	end)
end)
