# Copyright 2024-2025 NXP

# SPDX-License-Identifier: BSD-3-Clause

import gdb
import os
import sys

CURRENT_DIR = "".join([os.path.dirname(os.path.realpath(__file__)), "/"])
sys.path.insert(0, CURRENT_DIR)
sys.path.insert(0, CURRENT_DIR + "/../gdb_extensions")

from flash.scripts.elfparser import ElfFile

RV_ATTR_TAG_file = 1
RV_ATTR_TAG_arch = 5

INVALID_INSTR = ["unknown", "unimp"]

def int_from_bytes(bytes_string, byteorder="big"):
    if byteorder == "little":
        bytes_string = bytes_string[::-1]
    result = 0
    for byte in bytes_string:
        if isinstance(byte, str):
            byte = ord(byte)
        result = (result << 8) + byte
    return result


def validate_result(text, length, instr_bytes):
    valid_text = text
    for item in INVALID_INSTR:
        if item in text.lower():
            instr_len = 4 if length == 0 else length
            instr = int_from_bytes(instr_bytes, byteorder='little')
            valid_text = ".{}byte\t {}".format(length, hex(instr & ((1 << (length*8)) - 1)))
            break
    return valid_text


def get_target_address_value(target_address):
    function_name = ""
    offset = 0
    symbol = gdb.block_for_pc(target_address)
    if symbol and symbol.function:
        function_name = symbol.function.print_name
        funct_start_addr = int(symbol.function.value().address)
        offset = target_address - funct_start_addr
    else:
        try:
            gdb_response = gdb.execute("info symbol {}".format(target_address), to_string=True).strip()
            function_name, offset = parse_info_symbol_response(gdb_response)
        except gdb.error:
            pass
    if function_name:
        text = "   # {} <{}".format(hex(target_address), str(function_name))
        text = "{}+{}>".format(text, hex(offset)) if offset != 0 else "{}>".format(text)
    else:
        text = "   # {} lookup error".format(hex(target_address))
    return text


def parse_info_symbol_response(gdb_response):
    fields = gdb_response.split("+")
    function_name = fields[0].split()[0]
    try:
        offset = int(fields[1].split()[0])
    except IndexError:
        offset = 0
    return function_name, offset


def read_elf_section(elf_path, section_name):
    with ElfFile(elf_path) as elf_file:
        section = elf_file.get_section_header_by_name(section_name)
        if not section:
            return None
        section.read_data()
    return section


def parse_riscv_attributes(elf_path):
    rv_attr = read_elf_section(elf_path, ".riscv.attributes")
    if not rv_attr:
        return None

    if rv_attr.read_data()[0:1] != b'A':
        return None
    idx = 1
    remaining_size_attr = rv_attr.size - idx
    while remaining_size_attr - idx >= 4:
        sub_length = rv_attr.read_data()[idx]
        if sub_length <= 4 or sub_length > remaining_size_attr:
            return None
        idx += 4
        vendor = rv_attr.read_data()[idx:].split(b'\x00', 1)[0].decode()
        idx = idx + len(vendor) + 1
        while remaining_size_attr > 0:
            tag = rv_attr.read_data()[idx]
            idx += 1
            size = rv_attr.read_data()[idx]
            idx += 1
            if tag != RV_ATTR_TAG_file:
                idx += size
                remaining_size_attr = rv_attr.size - idx
                continue
            remaining_size_attr = rv_attr.size - idx
            idx += 3
            while size > 0:
                tag, value, attr_size = parse_attribute(rv_attr.read_data()[idx:])
                if tag == RV_ATTR_TAG_arch:
                    return value
                idx += attr_size
                size = size - attr_size
                remaining_size_attr -= attr_size


# Helper to parse one RISC-V attribute
def parse_attribute(data):
    idx = 0
    tag = data[idx]
    idx += 1
    if tag % 2 == 0:
        value = data[idx]
    else:
        value = data[idx:].split(b'\x00', 1)[0].decode()
        idx = idx + len(value) + 1
    size = idx + 1
    return tag, value, size
