#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_SHADERDECODER_H_
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_SHADERDECODER_H_

#include "common/common.h"
#include "common/stringUtils.h"

#include <span>
#include <vector>

namespace Libs::Graphics::ShaderRecompiler::Decoder {

enum class Family {
	Unknown,
	SOP1,
	SOP2,
	SOPK,
	SOPC,
	SOPP,
	VOP1,
	VOP2,
	VOP3,
	VOP3P,
	VOPC,
	VINTRP,
	SMEM,
	MUBUF,
	MTBUF,
	FLAT,
	DS,
	MIMG,
	EXP
};

enum class Opcode {
	Unknown,
	Unsupported,

	SMovB32,
	SMovB64,
	SMovkI32,
	SAbsI32,
	SBrevB32,
	SBcnt1I32B32,
	SBcnt1I32B64,
	SFf1I32B32,
	SFlbitI32B64,
	SBitreplicateB64B32,
	SGetpcB64,
	SSetpcB64,
	SAndSaveexecB32,
	SAndn1SaveexecB32,
	SAndSaveexecB64,
	SOrn2SaveexecB64,
	SAndn1SaveexecB64,
	SNotB32,
	SNotB64,
	SWqmB64,
	SAddU32,
	SAddcU32,
	SSubbU32,
	SSubU32,
	SAddI32,
	SSubI32,
	SBitcmp0B32,
	SBitcmp1B32,
	SBitset0B32,
	SBitset1B32,
	SMinI32,
	SMaxI32,
	SMinU32,
	SMaxU32,
	SAndB32,
	SAndB64,
	SAndn2B32,
	SAndn2B64,
	SOrB32,
	SOrB64,
	SOrn2B32,
	SOrn2B64,
	SXorB32,
	SXorB64,
	SNandB32,
	SNandB64,
	SNorB32,
	SNorB64,
	SXnorB32,
	SXnorB64,
	SLshlB32,
	SLshlB64,
	SLshl1AddU32,
	SLshl2AddU32,
	SLshl3AddU32,
	SLshl4AddU32,
	SLshrB32,
	SLshrB64,
	SAshrI32,
	SMulI32,
	SMulHiU32,
	SMulkI32,
	SBfeU32,
	SBfeU64,
	SBfmB32,
	SBfmB64,
	SCselectB32,
	SCselectB64,
	SPackLlB32B16,
	SPackLhB32B16,
	SPackHhB32B16,
	SCmpEqI32,
	SCmpLgI32,
	SCmpGtI32,
	SCmpGeI32,
	SCmpLtI32,
	SCmpLeI32,
	SCmpEqU32,
	SCmpLgU32,
	SCmpGtU32,
	SCmpGeU32,
	SCmpLtU32,
	SCmpLeU32,
	SCmpLgU64,

	VNop,
	VMovB32,
	VMovreldB32,
	VMovrelsB32,
	VReadfirstlaneB32,
	VReadlaneB32,
	VWritelaneB32,
	VPermlane16B32,
	VPermlanex16B32,
	VCubeidF32,
	VCubescF32,
	VCubetcF32,
	VCubemaF32,
	VCndmaskB32,
	VDot2cF32F16,
	VCvtF32I32,
	VCvtF32U32,
	VCvtU32F32,
	VCvtI32F32,
	VCvtF16F32,
	VCvtF32F16,
	VCvtF16U16,
	VCvtU16F16,
	VCvtF16I16,
	VCvtI16F16,
	VCvtRpiI32F32,
	VCvtFlrI32F32,
	VCvtOffF32I4,
	VCvtF32Ubyte0,
	VCvtF32Ubyte1,
	VCvtF32Ubyte2,
	VCvtF32Ubyte3,
	VRcpF32,
	VFractF32,
	VTruncF32,
	VCeilF32,
	VRndneF32,
	VFloorF32,
	VExpF32,
	VLogF32,
	VRsqF32,
	VSqrtF32,
	VRcpF16,
	VSqrtF16,
	VRsqF16,
	VLogF16,
	VExpF16,
	VFloorF16,
	VCeilF16,
	VTruncF16,
	VRndneF16,
	VSinF32,
	VCosF32,
	VNotB32,
	VBfrevB32,
	VFfbhU32,
	VFfblB32,
	VAddF32,
	VSubF32,
	VSubrevF32,
	VMulF32,
	VMacF32,
	VMadmkF32,
	VMadakF32,
	VMinF32,
	VMaxF32,
	VAddF16,
	VSubF16,
	VSubrevF16,
	VMulF16,
	VFmacF16,
	VFmamkF16,
	VFmaakF16,
	VMinI32,
	VMaxI32,
	VMulI32I24,
	VMulU32U24,
	VMulLoU32,
	VMulHiU32,
	VMulLoI32,
	VMulHiI32,
	VCvtPkrtzF16F32,
	VCvtPkU8F32,
	VMadF32,
	VMadI32I24,
	VMadU32U24,
	VMadU64U32,
	VFmaF32,
	VFmaF16,
	VPackB32F16,
	VBfeU32,
	VBfeI32,
	VBfiB32,
	VAlignbitB32,
	VMin3F32,
	VMin3I32,
	VMin3U32,
	VMin3F16,
	VMax3F32,
	VMax3I32,
	VMax3U32,
	VMax3F16,
	VMed3F32,
	VMed3I32,
	VMed3U32,
	VMed3F16,
	VSadU32,
	VAdd3U32,
	VLshlAddU32,
	VAddLshlU32,
	VXadU32,
	VLshlOrB32,
	VAndOrB32,
	VOr3B32,
	VXor3B32,
	VAddI32,
	VSubI32,
	VSubrevI32,
	VBfmB32,
	VLdexpF32,
	VCvtPknormI16F32,
	VCvtPknormU16F32,
	VCvtPkU16U32,
	VPkMadI16,
	VPkMulLoU16,
	VPkAddI16,
	VPkSubI16,
	VPkLshlrevB16,
	VPkLshrrevB16,
	VPkAshrrevI16,
	VPkMaxI16,
	VPkMinI16,
	VPkMadU16,
	VPkAddF16,
	VPkMulF16,
	VPkMinF16,
	VPkMaxF16,
	VPkFmaF16,
	VPkFmacF16,
	VPkAddU16,
	VPkSubU16,
	VPkMaxU16,
	VPkMinU16,
	VMadMixloF16,
	VMadMixhiF16,
	VAddNcU32,
	VAddcU32,
	VSubNcU32,
	VSubrevNcU32,
	VAddNcU16,
	VSubNcU16,
	VMaxU16,
	VMaxI16,
	VMinU16,
	VMinI16,
	VAddNcI16,
	VSubNcI16,
	VAndB32,
	VOrB32,
	VXorB32,
	VXnorB32,
	VLshlB32,
	VLshlrevB32,
	VLshrB32,
	VLshrrevB32,
	VAshrI32,
	VAshrrevI32,
	VLshlrevB16,
	VLshrrevB16,
	VAshrrevI16,
	VBcntU32B32,
	VMbcntLoU32B32,
	VMbcntHiU32B32,
	VMinU32,
	VMaxU32,
	VMaxF16,
	VMinF16,
	VCmpFF32,
	VCmpLtF32,
	VCmpEqF32,
	VCmpLeF32,
	VCmpGtF32,
	VCmpLgF32,
	VCmpGeF32,
	VCmpOF32,
	VCmpUF32,
	VCmpNgeF32,
	VCmpNlgF32,
	VCmpNgtF32,
	VCmpNleF32,
	VCmpNeqF32,
	VCmpNltF32,
	VCmpTruF32,
	VCmpxLtF32,
	VCmpxEqF32,
	VCmpxLeF32,
	VCmpxGtF32,
	VCmpxLgF32,
	VCmpxGeF32,
	VCmpxNgeF32,
	VCmpxNlgF32,
	VCmpxNgtF32,
	VCmpxNleF32,
	VCmpxNeqF32,
	VCmpxNltF32,
	VCmpFI32,
	VCmpLtI32,
	VCmpEqI32,
	VCmpLeI32,
	VCmpGtI32,
	VCmpNeI32,
	VCmpGeI32,
	VCmpTI32,
	VCmpClassF32,
	VCmpLtI16,
	VCmpEqI16,
	VCmpLeI16,
	VCmpGtI16,
	VCmpNeI16,
	VCmpGeI16,
	VCmpLtF16,
	VCmpEqF16,
	VCmpLeF16,
	VCmpGtF16,
	VCmpLgF16,
	VCmpGeF16,
	VCmpNeqF16,
	VCmpxLtF16,
	VCmpxEqF16,
	VCmpxLeF16,
	VCmpxGtF16,
	VCmpxGeF16,
	VCmpxNeqF16,
	VCmpxNltF16,
	VCmpxLtI32,
	VCmpxEqI32,
	VCmpxLeI32,
	VCmpxGtI32,
	VCmpxNeI32,
	VCmpxGeI32,
	VCmpLtU16,
	VCmpEqU16,
	VCmpLeU16,
	VCmpGtU16,
	VCmpNeU16,
	VCmpGeU16,
	VCmpFU32,
	VCmpLtU32,
	VCmpEqU32,
	VCmpLeU32,
	VCmpGtU32,
	VCmpNeU32,
	VCmpGeU32,
	VCmpTU32,
	VCmpNeU64,
	VCmpxLtU32,
	VCmpxEqU32,
	VCmpxLeU32,
	VCmpxGtU32,
	VCmpxNeU32,
	VCmpxGeU32,

	SLoadDword,
	SLoadDwordx2,
	SLoadDwordx4,
	SLoadDwordx8,
	SLoadDwordx16,
	SBufferLoadDword,
	SBufferLoadDwordx2,
	SBufferLoadDwordx4,
	SBufferLoadDwordx8,
	SBufferLoadDwordx16,
	BufferLoadFormatX,
	BufferLoadFormatXy,
	BufferLoadFormatXyz,
	BufferLoadFormatXyzw,
	BufferStoreFormatX,
	BufferStoreFormatXy,
	BufferStoreFormatXyz,
	BufferStoreFormatXyzw,
	BufferLoadUbyte,
	BufferLoadSbyte,
	BufferLoadUshort,
	BufferLoadSshort,
	BufferLoadDword,
	BufferLoadDwordx2,
	BufferLoadDwordx3,
	BufferLoadDwordx4,
	BufferStoreByte,
	BufferStoreShort,
	BufferStoreDword,
	BufferStoreDwordx2,
	BufferStoreDwordx3,
	BufferStoreDwordx4,
	TBufferLoadFormatX,
	TBufferLoadFormatXy,
	TBufferLoadFormatXyz,
	TBufferLoadFormatXyzw,
	TBufferStoreFormatX,
	TBufferStoreFormatXy,
	TBufferStoreFormatXyz,
	TBufferStoreFormatXyzw,
	BufferAtomicSwap,
	BufferAtomicAdd,
	BufferAtomicSub,
	BufferAtomicSMin,
	BufferAtomicUMin,
	BufferAtomicSMax,
	BufferAtomicUMax,
	BufferAtomicAnd,
	BufferAtomicOr,
	BufferAtomicXor,
	FlatLoadUbyte,
	FlatLoadSbyte,
	FlatLoadUshort,
	FlatLoadSshort,
	FlatLoadDword,
	FlatLoadDwordx2,
	FlatLoadDwordx3,
	FlatLoadDwordx4,
	FlatStoreByte,
	FlatStoreShort,
	FlatStoreDword,
	FlatStoreDwordx2,
	FlatStoreDwordx3,
	FlatStoreDwordx4,
	DsAddU32,
	DsAddRtnU32,
	DsSubU32,
	DsSubRtnU32,
	DsMinI32,
	DsMinRtnI32,
	DsMaxI32,
	DsMaxRtnI32,
	DsMinU32,
	DsMinRtnU32,
	DsMaxU32,
	DsMaxRtnU32,
	DsAndB32,
	DsAndRtnB32,
	DsOrB32,
	DsOrRtnB32,
	DsXorB32,
	DsXorRtnB32,
	DsWrxchgRtnB32,
	DsMinF32,
	DsMaxF32,
	DsSwizzleB32,
	DsConsume,
	DsAppend,
	DsReadSbyte,
	DsReadUbyte,
	DsReadSshort,
	DsReadUshort,
	DsRead2B32,
	DsReadB32,
	DsReadB64,
	DsRead2B64,
	DsRead2St64B64,
	DsReadB96,
	DsReadB128,
	DsWriteByte,
	DsWriteShort,
	DsWrite2B32,
	DsWrite2St64B32,
	DsWrite2B64,
	DsWrite2St64B64,
	DsWriteB32,
	DsWriteB64,
	DsWriteB96,
	DsWriteB128,
	DsWriteAddtidB32,
	DsReadAddtidB32,
	ImageGetResinfo,
	ImageGetLod,
	ImageLoad,
	ImageLoadMip,
	ImageStore,
	ImageStoreMip,
	ImageAtomicAdd,
	ImageAtomicUMin,
	ImageAtomicUMax,
	ImageAtomicAnd,
	ImageAtomicOr,
	ImageAtomicXor,
	ImageSample,
	ImageGather4Lz,
	ImageGather4C,
	ImageGather4CLz,
	ImageGather4LzO,
	ImageGather4CO,
	ImageGather4CLzO,
	ImageGather4H,
	VInterpP1F32,
	VInterpP2F32,
	VInterpMovF32,

	SNop,
	SWaitcnt,
	SBarrier,
	SBranch,
	SCbranchScc0,
	SCbranchScc1,
	SCbranchVccz,
	SCbranchVccnz,
	SCbranchExecz,
	SCbranchExecnz,
	SSendmsg,
	SSetregB32,
	SSleep,
	STtraceData,
	SInstPrefetch,
	SEndpgm,
	Exp
};

enum class OperandKind {
	Unknown,
	LiteralConstant,
	IntegerInlineConstant,
	FloatInlineConstant,
	Sgpr,
	VccLo,
	VccHi,
	VccZ,
	ExecLo,
	ExecHi,
	ExecZ,
	Scc,
	M0,
	PopsExitingWaveId,
	Null,
	Vgpr,
};

enum ImageSampleFlag : uint32_t {
	ImageSampleFlagLod              = 1u << 0u,
	ImageSampleFlagBias             = 1u << 1u,
	ImageSampleFlagDerivative       = 1u << 2u,
	ImageSampleFlagCompare          = 1u << 3u,
	ImageSampleFlagOffset           = 1u << 4u,
	ImageSampleFlagLevelZero        = 1u << 5u,
	ImageSampleFlagLodClamp         = 1u << 6u,
	ImageSampleFlagA16              = 1u << 7u,
	ImageSampleFlagCd               = 1u << 8u,
	ImageSampleFlagGatherHorizontal = 1u << 9u,
};

enum class ImageDimension : uint32_t {
	Unknown,
	Dim2D,
	Dim3D,
	Dim2DArray,
};

constexpr uint32_t MaxInstructionRawWords       = 5u;
constexpr uint32_t MaxImageNsaAddressComponents = 12u;

struct Operand {
	OperandKind kind               = OperandKind::Unknown;
	uint32_t    value              = 0;
	int32_t     signed_val         = 0;
	float       float_val          = 0.0f;
	uint32_t    reg                = 0;
	uint32_t    sdwa_sel           = 6;
	uint32_t    sdwa_dst_unused    = 0;
	uint32_t    omod               = 0;
	uint32_t    dpp_ctrl           = 0;
	uint32_t    dpp_row_mask       = 0xf;
	uint32_t    dpp_bank_mask      = 0xf;
	bool        sdwa_sext          = false;
	bool        dpp_fetch_inactive = false;
	bool        dpp_bound_ctrl     = false;
	bool        op_sel             = false;
	bool        op_sel_hi          = false;
	bool        negate             = false;
	bool        negate_hi          = false;
	bool        absolute           = false;
	bool        clamp              = false;
	bool        dpp                = false;
};

struct Instruction {
	uint32_t       pc                          = 0;
	uint32_t       word                        = 0;
	uint32_t       word_count                  = 1;
	uint32_t       raw[MaxInstructionRawWords] = {};
	uint32_t       raw_count                   = 1;
	Family         family                      = Family::Unknown;
	uint32_t       opcode_id                   = 0;
	Opcode         opcode                      = Opcode::Unknown;
	Operand        dst;
	Operand        dst2;
	Operand        src0;
	Operand        src1;
	Operand        src2;
	Operand        src3;
	uint32_t       src_count                                    = 0;
	uint32_t       offset                                       = 0;
	uint32_t       secondary_offset                             = 0;
	uint32_t       dmask                                        = 0;
	uint32_t       data_dwords                                  = 1;
	uint32_t       data_bits                                    = 32;
	uint32_t       data_format                                  = 0;
	uint32_t       number_format                                = 0;
	uint32_t       image_sample_flags                           = 0;
	ImageDimension image_dimension                              = ImageDimension::Unknown;
	uint32_t       image_address_components                     = 0;
	uint32_t       image_nsa_dwords                             = 0;
	uint32_t       image_nsa_addr[MaxImageNsaAddressComponents] = {};
	uint32_t       memory_segment                               = 0;
	bool           data_signed                                  = false;
	bool           typed                                        = false;
	bool           formatted                                    = false;
	bool           gds                                          = false;
	bool           glc                                          = false;
	bool           slc                                          = false;
	bool           idxen                                        = false;
	bool           offen                                        = false;
	int32_t        branch_offset                                = 0;
	uint32_t       branch_target                                = 0;
	struct {
		uint32_t target = 0;
		uint32_t en     = 0;
		bool     done   = false;
		bool     compr  = false;
		bool     vm     = false;
	} exp;
	std::string unsupported_reason;
};

struct Program {
	std::span<const uint32_t> code;
	std::vector<Instruction>  instructions;
};

bool DecodeProgram(std::span<const uint32_t> code, Program& program, std::string* error);

bool DecodeScalarSource(uint32_t code, uint32_t pc, Operand& operand, std::string* error);
bool DecodeScalarDestination(uint32_t code, uint32_t pc, Operand& operand, std::string* error);
bool DecodeVectorGpr(uint32_t reg, Operand& operand, std::string* error);
bool ReadLiteralOperands(std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
                         std::string* error);
void SetRawWords(Instruction& inst, std::span<const uint32_t> code, uint32_t word_index,
                 uint32_t word_count);
void SetUnsupported(Instruction& inst, Family family, uint32_t opcode_id, const char* reason);
std::string FamilyToString(Family family);
std::string OpcodeToString(Opcode opcode);
std::string OperandToString(const Operand& operand);
std::string InstructionToString(const Instruction& inst);
std::string ProgramToString(const Program& program);

} // namespace Libs::Graphics::ShaderRecompiler::Decoder

#endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_SHADERDECODER_H_ */
