/**
 *
 * Copyright (c) 2024 Analog Devices, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
﻿/* eslint-disable @typescript-eslint/no-namespace */

export namespace DwarfData {
	// DIE tags (Section 7, figure 18).  typedef, friend, and namespace
	// have a trailing underscore because they are reserved words.
	export enum DW_TAG {
		array_type = 0x01,
		class_type = 0x02,
		entry_point = 0x03,
		enumeration_type = 0x04,
		formal_parameter = 0x05,
		imported_declaration = 0x08,
		label = 0x0a,
		lexical_block = 0x0b,
		member = 0x0d,
		pointer_type = 0x0f,
		reference_type = 0x10,
		compile_unit = 0x11,
		string_type = 0x12,
		structure_type = 0x13,
		subroutine_type = 0x15,
		typedef_ = 0x16,

		union_type = 0x17,
		unspecified_parameters = 0x18,
		variant = 0x19,
		common_block = 0x1a,
		common_inclusion = 0x1b,
		inheritance = 0x1c,
		inlined_subroutine = 0x1d,
		module = 0x1e,
		ptr_to_member_type = 0x1f,
		set_type = 0x20,
		subrange_type = 0x21,
		with_stmt = 0x22,
		access_declaration = 0x23,
		base_type = 0x24,
		catch_block = 0x25,
		const_type = 0x26,
		constant = 0x27,
		enumerator = 0x28,
		file_type = 0x29,
		friend_ = 0x2a,

		namelist = 0x2b,
		namelist_item = 0x2c,
		packed_type = 0x2d,
		subprogram = 0x2e,
		template_type_parameter = 0x2f,
		template_value_parameter = 0x30,
		thrown_type = 0x31,
		try_block = 0x32,
		variant_part = 0x33,
		variable = 0x34,
		volatile_type = 0x35,
		dwarf_procedure = 0x36,
		restrict_type = 0x37,
		interface_type = 0x38,
		namespace_ = 0x39,
		imported_module = 0x3a,
		unspecified_type = 0x3b,
		partial_unit = 0x3c,
		imported_unit = 0x3d,
		condition = 0x3f,

		shared_type = 0x40,
		type_unit = 0x41,
		rvalue_reference_type = 0x42,
		template_alias = 0x43,
		lo_user = 0x4080,
		hi_user = 0xffff,
	}

	// Child determination (Section 7, figure 19).
	export const enum DW_CHILDREN {
		no = 0x00,
		yes = 0x01,
	}

	// Attribute names (Section 7, figure 20).  inline, friend, mutable,
	// and explicit have a trailing underscore because they are reserved
	// words.
	//export const enum DW_AT {
	export enum DW_AT {
		sibling = 0x01, // reference
		location = 0x02, // exprloc, loclistptr
		name = 0x03, // string
		ordering = 0x09, // constant
		byte_size = 0x0b, // constant, exprloc, reference
		bit_offset = 0x0c, // constant, exprloc, reference
		bit_size = 0x0d, // constant, exprloc, reference
		stmt_list = 0x10, // lineptr
		low_pc = 0x11, // address
		high_pc = 0x12, // address, constant
		language = 0x13, // constant
		discr = 0x15, // reference
		discr_value = 0x16, // constant
		visibility = 0x17, // constant
		import = 0x18, // reference
		string_length = 0x19, // exprloc, loclistptr
		common_reference = 0x1a, // reference
		comp_dir = 0x1b, // string
		const_value = 0x1c, // block, constant, string

		containing_type = 0x1d, // reference
		default_value = 0x1e, // reference
		inline_ = 0x20, // constant
		is_optional = 0x21, // flag
		lower_bound = 0x22, // constant, exprloc, reference
		producer = 0x25, // string
		prototyped = 0x27, // flag
		return_addr = 0x2a, // exprloc, loclistptr
		start_scope = 0x2c, // constant, rangelistptr
		bit_stride = 0x2e, // constant, exprloc, reference
		upper_bound = 0x2f, // constant, exprloc, reference
		abstract_origin = 0x31, // reference
		accessibility = 0x32, // constant
		address_class = 0x33, // constant
		artificial = 0x34, // flag
		base_types = 0x35, // reference
		calling_convention = 0x36, // constant
		count = 0x37, // constant, exprloc, reference
		data_member_location = 0x38, // constant, exprloc, loclistptr
		decl_column = 0x39, // constant

		decl_file = 0x3a, // constant
		decl_line = 0x3b, // constant
		declaration = 0x3c, // flag
		discr_list = 0x3d, // block
		encoding = 0x3e, // constant
		external = 0x3f, // flag
		frame_base = 0x40, // exprloc, loclistptr
		friend_ = 0x41, // reference
		identifier_case = 0x42, // constant
		macro_info = 0x43, // macptr
		namelist_item = 0x44, // reference
		priority = 0x45, // reference
		segment = 0x46, // exprloc, loclistptr
		specification = 0x47, // reference
		static_link = 0x48, // exprloc, loclistptr
		type = 0x49, // reference
		use_location = 0x4a, // exprloc, loclistptr
		variable_parameter = 0x4b, // flag
		virtuality = 0x4c, // constant
		vtable_elem_location = 0x4d, // exprloc, loclistptr

		// DWARF 3
		allocated = 0x4e, // constant, exprloc, reference
		associated = 0x4f, // constant, exprloc, reference
		data_location = 0x50, // exprloc
		byte_stride = 0x51, // constant, exprloc, reference
		entry_pc = 0x52, // address
		use_UTF8 = 0x53, // flag
		extension = 0x54, // reference
		ranges = 0x55, // rangelistptr
		trampoline = 0x56, // address, flag, reference, string
		call_column = 0x57, // constant
		call_file = 0x58, // constant
		call_line = 0x59, // constant
		description = 0x5a, // string
		binary_scale = 0x5b, // constant
		decimal_scale = 0x5c, // constant
		small = 0x5d, // reference
		decimal_sign = 0x5e, // constant
		digit_count = 0x5f, // constant
		picture_string = 0x60, // string
		mutable_ = 0x61, // flag

		threads_scaled = 0x62, // flag
		explicit_ = 0x63, // flag
		object_pointer = 0x64, // reference
		endianity = 0x65, // constant
		elemental = 0x66, // flag
		pure = 0x67, // flag
		recursive = 0x68, // flag

		// DWARF 4
		signature = 0x69, // reference
		main_subprogram = 0x6a, // flag
		data_bit_offset = 0x6b, // constant
		const_expr = 0x6c, // flag
		enum_class = 0x6d, // flag
		linkage_name = 0x6e, // string

		lo_user = 0x2000,
		hi_user = 0x3fff,
	}

	// Attribute form encodings (Section 7, figure 21)
	//export const enum DW_FORM {
	export enum DW_FORM {
		addr = 0x01, // address
		block2 = 0x03, // block
		block4 = 0x04, // block
		data2 = 0x05, // constant
		data4 = 0x06, // constant
		data8 = 0x07, // constant
		string = 0x08, // string
		block = 0x09, // block
		block1 = 0x0a, // block
		data1 = 0x0b, // constant
		flag = 0x0c, // flag
		sdata = 0x0d, // constant
		strp = 0x0e, // string
		udata = 0x0f, // constant
		ref_addr = 0x10, // reference
		ref1 = 0x11, // reference
		ref2 = 0x12, // reference
		ref4 = 0x13, // reference
		ref8 = 0x14, // reference

		ref_udata = 0x15, // reference
		indirect = 0x16, // (Section 7.5.3)

		// DWARF 4
		sec_offset = 0x17, // lineptr, loclistptr, macptr, rangelistptr
		exprloc = 0x18, // exprloc
		flag_present = 0x19, // flag
		ref_sig8 = 0x20, // reference

		//DWARF 5
		implicit_const = 0x21,
	}

	// DWARF operation encodings (Section 7.7.1 and figure 24)
	export const enum DW_OP {
		addr = 0x03, // [constant address (size target specific)]
		deref = 0x06,

		const1u = 0x08, // [1-byte constant]
		const1s = 0x09, // [1-byte constant]
		const2u = 0x0a, // [2-byte constant]
		const2s = 0x0b, // [2-byte constant]
		const4u = 0x0c, // [4-byte constant]
		const4s = 0x0d, // [4-byte constant]
		const8u = 0x0e, // [8-byte constant]
		const8s = 0x0f, // [8-byte constant]
		constu = 0x10, // [ULEB128 constant]
		consts = 0x11, // [SLEB128 constant]
		dup = 0x12,
		drop = 0x13,
		over = 0x14,
		pick = 0x15, // [1-byte stack index]
		swap = 0x16,
		rot = 0x17,
		xderef = 0x18,
		abs = 0x19,
		and_ = 0x1a,
		div = 0x1b,

		minus = 0x1c,
		mod = 0x1d,
		mul = 0x1e,
		neg = 0x1f,
		not_ = 0x20,
		or_ = 0x21,
		plus = 0x22,
		plus_uconst = 0x23, // [ULEB128 addend]
		shl = 0x24,
		shr = 0x25,
		shra = 0x26,
		xor_ = 0x27,
		skip = 0x2f, // [signed 2-byte constant]
		bra = 0x28, // [signed 2-byte constant]
		eq = 0x29,
		ge = 0x2a,
		gt = 0x2b,
		le = 0x2c,
		lt = 0x2d,
		ne = 0x2e,

		// Literals 0..31 = (lit0 + literal)
		lit0 = 0x30,
		lit31 = 0x4f,

		// Registers 0..31 = (reg0 + regnum)
		reg0 = 0x50,
		reg31 = 0x6f,

		// Base register 0..31 = (breg0 + regnum)
		breg0 = 0x70, // [SLEB128 offset]
		breg31 = 0x8f, // [SLEB128 offset]

		regx = 0x90, // [ULEB128 register]
		fbreg = 0x91, // [SLEB128 offset]
		bregx = 0x92, // [ULEB128 register, SLEB128 offset]
		piece = 0x93, // [ULEB128 size of piece addressed]
		deref_size = 0x94, // [1-byte size of data retrieved]
		xderef_size = 0x95, // [1-byte size of data retrieved]
		nop = 0x96,

		// DWARF 3
		push_object_address = 0x97,
		call2 = 0x98, // [2-byte offset of DIE]
		call4 = 0x99, // [4-byte offset of DIE]
		call_ref = 0x9a, // [4- or 8-byte offset of DIE]
		form_tls_address = 0x9b,
		call_frame_cfa = 0x9c,
		bit_piece = 0x9d, // [ULEB128 size, ULEB128 offset]

		// DWARF 4
		implicit_value = 0x9e, // [ULEB128 size, block of that size]
		stack_value = 0x9f,

		lo_user = 0xe0,
		hi_user = 0xff,
	}

	// DW_AT::encoding constants (DWARF4 section 7.8 figure 25)
	export const enum DW_ATE {
		address = 0x01,
		boolean = 0x02,
		complex_float = 0x03,
		float_ = 0x04,
		signed_ = 0x05,
		signed_char = 0x06,
		unsigned_ = 0x07,
		unsigned_char = 0x08,
		imaginary_float = 0x09,
		packed_decimal = 0x0a,
		numeric_string = 0x0b,
		edited = 0x0c,
		signed_fixed = 0x0d,
		unsigned_fixed = 0x0e,
		decimal_float = 0x0f,

		// DWARF 4
		UTF = 0x10,

		lo_user = 0x80,
		hi_user = 0xff,
	}

	// DW_AT::decimal_sign constants (DWARF4 section 7.8 figure 26)
	export const enum DW_DS {
		unsigned_ = 0x01,
		leading_overpunch = 0x02,
		trailing_overpunch = 0x03,
		leading_separate = 0x04,
		trailing_separate = 0x05,
	}

	// DW_AT::endianity constants (DWARF4 section 7.8 figure 27)
	export const enum DW_END {
		default_ = 0x00,
		big = 0x01,
		little = 0x02,
		lo_user = 0x40,
		hi_user = 0xff,
	}

	// DW_AT::accessibility constants (DWARF4 section 7.9 figure 28)
	export const enum DW_ACCESS {
		public_ = 0x01,
		protected_ = 0x02,
		private_ = 0x03,
	}

	// DW_AT::visibility constants (DWARF4 section 7.10 figure 29)
	export const enum DW_VIS {
		local = 0x01,
		exported = 0x02,
		qualified = 0x03,
	}

	// DW_AT::virtuality constants (DWARF4 section 7.11 figure 30)
	export const enum DW_VIRTUALITY {
		none = 0x00,
		virtual_ = 0x01,
		pure_virtual = 0x02,
	}

	// DW_AT::language constants (DWARF4 section 7.12 figure 31)
	export const enum DW_LANG {
		C89 = 0x0001, // Lower bound 0
		C = 0x0002, // Lower bound 0
		Ada83 = 0x0003, // Lower bound 1
		C_plus_plus = 0x0004, // Lower bound 0
		Cobol74 = 0x0005, // Lower bound 1
		Cobol85 = 0x0006, // Lower bound 1
		Fortran77 = 0x0007, // Lower bound 1
		Fortran90 = 0x0008, // Lower bound 1
		Pascal83 = 0x0009, // Lower bound 1
		Modula2 = 0x000a, // Lower bound 1
		Java = 0x000b, // Lower bound 0
		C99 = 0x000c, // Lower bound 0
		Ada95 = 0x000d, // Lower bound 1
		Fortran95 = 0x000e, // Lower bound 1
		PLI = 0x000f, // Lower bound 1

		ObjC = 0x0010, // Lower bound 0
		ObjC_plus_plus = 0x0011, // Lower bound 0
		UPC = 0x0012, // Lower bound 0
		D = 0x0013, // Lower bound 0
		Python = 0x0014, // Lower bound 0
		lo_user = 0x8000,
		hi_user = 0xffff,
	}

	// DW_AT::identifier_case constants (DWARF4 section 7.14 figure 32)
	export const enum DW_ID {
		case_sensitive = 0x00,
		up_case = 0x01,
		down_case = 0x02,
		case_insensitive = 0x03,
	}

	// DW_AT::calling_convention constants (DWARF4 section 7.15 figure 33)
	export const enum DW_CC {
		normal = 0x01,
		program = 0x02,
		nocall = 0x03,
		lo_user = 0x40,
		hi_user = 0xff,
	}

	// DW_AT::inline constants (DWARF4 section 7.16 figure 34)
	export const enum DW_INL {
		not_inlined = 0x00,
		inlined = 0x01,
		declared_not_inlined = 0x02,
		declared_inlined = 0x03,
	}

	// DW_AT::ordering constants (DWARF4 section 7.17 figure 35)
	export const enum DW_ORD {
		row_major = 0x00,
		col_major = 0x01,
	}

	// DW_AT::discr_list constants (DWARF4 section 7.18 figure 36)
	export const enum DW_DSC {
		label = 0x00,
		range = 0x01,
	}

	// Line number standard opcodes (DWARF4 section 7.21 figure 37)
	export const enum DW_LNS {
		copy = 0x01,
		advance_pc = 0x02,
		advance_line = 0x03,
		set_file = 0x04,
		set_column = 0x05,
		negate_stmt = 0x06,
		set_basic_block = 0x07,
		const_add_pc = 0x08,
		fixed_advance_pc = 0x09,

		// DWARF 3
		set_prologue_end = 0x0a,
		set_epilogue_begin = 0x0b,
		set_isa = 0x0c,
	}

	// Line number extended opcodes (DWARF4 section 7.21 figure 38)
	export const enum DW_LNE {
		end_sequence = 0x01,
		set_address = 0x02,
		define_file = 0x03,

		// DWARF 4
		set_discriminator = 0x04,

		// DWARF 3
		lo_user = 0x80,
		hi_user = 0xff,
	}

	export enum ValueType {
		invalid = 0,
		address,
		block,
		constant,
		uconstant,
		sconstant,
		exprloc,
		flag,
		line,
		loclist,
		mac,
		rangelist,
		reference,
		string,
	}

	export class TypeResolver {
		static resolveType(name: DW_AT, form: DW_FORM): ValueType {
			switch (form) {
				case DW_FORM.addr:
					return ValueType.address;

				case DW_FORM.block:
				case DW_FORM.block1:
				case DW_FORM.block2:
				case DW_FORM.block4:
					// Prior to DWARF 4, exprlocs didn't have their own
					// form and were represented as blocks.
					// XXX Should this be predicated on version?
					switch (name) {
						case DW_AT.location:
						case DW_AT.byte_size:
						case DW_AT.bit_offset:
						case DW_AT.bit_size:
						case DW_AT.string_length:
						case DW_AT.lower_bound:
						case DW_AT.return_addr:
						case DW_AT.bit_stride:
						case DW_AT.upper_bound:
						case DW_AT.count:
						case DW_AT.data_member_location:
						case DW_AT.frame_base:
						case DW_AT.segment:
						case DW_AT.static_link:
						case DW_AT.use_location:
						case DW_AT.vtable_elem_location:
						case DW_AT.allocated:
						case DW_AT.associated:
						case DW_AT.data_location:
						case DW_AT.byte_stride:
							return ValueType.exprloc;
						default:
							return ValueType.block;
					}

				case DW_FORM.data4:
				case DW_FORM.data8:
					// Prior to DWARF 4, section offsets didn't have their
					// own form and were represented as data4 or data8.
					// DWARF 3 clarified that types that accepted both
					// constants and section offsets were to treat data4
					// and data8 as section offsets and other constant
					// forms as constants.
					// XXX Should this be predicated on version?
					switch (name) {
						case DW_AT.location:
						case DW_AT.stmt_list:
						case DW_AT.string_length:
						case DW_AT.return_addr:
						case DW_AT.start_scope:
						case DW_AT.data_member_location:
						case DW_AT.frame_base:
						case DW_AT.macro_info:
						case DW_AT.segment:
						case DW_AT.static_link:
						case DW_AT.use_location:
						case DW_AT.vtable_elem_location:
						case DW_AT.ranges:
							// Fall to DW_FORM.sec_offset
							return TypeResolver.resolveType(name, DW_FORM.sec_offset);
						default:
							// Fall through
							break;
					}
				case DW_FORM.data1:
				case DW_FORM.data2:
					return ValueType.constant;
				case DW_FORM.udata:
					return ValueType.uconstant;
				case DW_FORM.sdata:
					return ValueType.sconstant;

				case DW_FORM.exprloc:
					return ValueType.exprloc;

				case DW_FORM.flag:
				case DW_FORM.flag_present:
					return ValueType.flag;

				case DW_FORM.ref1:
				case DW_FORM.ref2:
				case DW_FORM.ref4:
				case DW_FORM.ref8:
				case DW_FORM.ref_addr:
				case DW_FORM.ref_sig8:
				case DW_FORM.ref_udata:
					return ValueType.reference;

				case DW_FORM.string:
				case DW_FORM.strp:
					return ValueType.string;

				case DW_FORM.indirect:
					// There's nothing meaningful we can do
					return ValueType.invalid;

				case DW_FORM.sec_offset:
					// The type of this form depends on the attribute
					switch (name) {
						case DW_AT.stmt_list:
							return ValueType.line;

						case DW_AT.location:
						case DW_AT.string_length:
						case DW_AT.return_addr:
						case DW_AT.data_member_location:
						case DW_AT.frame_base:
						case DW_AT.segment:
						case DW_AT.static_link:
						case DW_AT.use_location:
						case DW_AT.vtable_elem_location:
							return ValueType.loclist;

						case DW_AT.macro_info:
							return ValueType.mac;

						case DW_AT.start_scope:
						case DW_AT.ranges:
							return ValueType.rangelist;

						default:
							if (name >= DW_AT.lo_user && name <= DW_AT.hi_user) {
								//HACK: ignore vendor extensions
								return ValueType.invalid;
							}

							throw new Error(
								"DW_FORM_sec_offset not expected for attribute " + name,
							);
					}
			}
			throw new Error("unknown attribute form " + form);
		}
	}
} // namespace DwarfData
