# -*- coding: utf-8 -*-
"""
    Implementation read and write commands using monitor command

Copyright 2016 Freescale Semiconductor, Inc.
Copyright 2020-2025 NXP.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of Freescale Semiconductor nor the names of its
      contributors may be used to endorse or promote products derived from
      this software without specific prior written permission.

This software is provided by Freescale Semiconductor "as is" and any
express or implied warranties, including, but not limited to, the implied
warranties of merchantability and fitness for a particular purpose are
disclaimed. In no event shall Freescale Semiconductor be liable for any
direct, indirect, incidental, special, exemplary, or consequential damages
(including, but not limited to, procurement of substitute goods or services;
loss of use, data, or profits; or business interruption) however caused and
on any theory of liability, whether in contract, strict liability, or tort
(including negligence or otherwise) arising in any way out of the use of
this software, even if advised of the possibility of such damage.

"""
import sys
import gdb  # @UnresolvedImport  # pylint: disable=F0401
import os
import re
import json

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

import mem_reg_utils as utils
from errors import GtaLibException, ErrorCode
from operator import itemgetter

from break_config_command import BreakConfigCommand
from system_config_command import SystemConfigCommand
from mem_region import MemRegion

class ReadCommand(gdb.Command):
    """Read memory content.
    Read memory from address using the provided access size, memory space and count.
    The result is displayed as a hexadecimal encoded byte stream
    usage: mem_read [context] address [access_size] [space] [count]

    [context]      -        GTA (GDB server) debug context :ccs:soc[:core]
                            e.g. :ccs:S32G2XX or :ccs:S32G2XX:CortexA53#0
                            If present, the context can be a core context;
                            if not, the current context is used.
    address        -        Start address.
    [access_size]  -        Access size.
    [space]        -        Memory space.
    [count]        -        Number of elements, each element having access_size bytes

    Eg: mem_read :ccs:S32G2XX:CortexA53#0 0x80000000 4 virtual 1
    """

    def __init__(self):
        super(ReadCommand, self).__init__("mem_read", gdb.COMMAND_USER)
        utils.gdb_exec("set pagination off")

    def invoke(self, arguments, from_tty):
        self.dont_repeat()
        argv = gdb.string_to_argv(arguments)
        context, address, access_size, mem_space, count = self.__parse_read_mem_args(argv)

        # if context is soc then build SoC_0 context for read via system bus
        # for core addresses use current context
        if context.lower() == "soc":
            context = getSocContext(address)
        
        if context.lower() == "dap":
            context = getDapContext(address)

        # Check memory space
        cmd = "monitor mem list-ms " + context
        ms_list = (utils.gdb_exec(cmd, False, True).
                       unwrap().rstrip()).splitlines()
        # if not given by the user, mem_space defaults to 'virtual'
        if mem_space not in ms_list:
            raise gdb.GdbError(
            "[S32Debugger]Error: Please select a valid memory space: {}\n".format(
                ", ".join(ms_list))
        )
        region_list, is_info_from_gdb_server = getConstraints(mem_space, context)
        current_ctx = (utils.gdb_exec("monitor ctx get current", False, True).
                       unwrap().rstrip())

        if (context != current_ctx) and not is_info_from_gdb_server:
            self.__do_monitor_read_mem_for_other_ctxs(
                context, address, access_size, mem_space, count)            
            return

        # Some memory regions may be accessible through current core context 
        # but not through SoC context. 
        # Get the current context region list as well.
        current_context_region_list, _ = getConstraints(mem_space, current_ctx)

        # in the requested memory block, find all valid readable memory subregions;
        # for this, we need to find all intersections of the block requested to be read
        # with each of the valid memory regions listed by GTA
        readable_regions = []
        all_regions = []
        start_addr_block = address
        end_addr_block = address + (access_size * count) / getBytesPerAddress(
            context, mem_space.lower()
        )

        # Search the current context and append all regions that have extended parameteres to override the SoC context and requested memory space.
        for current_context_region in current_context_region_list:
            override_region = current_context_region.getIntersection(address, end_addr_block)
            # Check if the current region has attributes.
            if override_region and override_region["attribute"]:
                extended_attributes = getExtendedAttributes(override_region["attribute"])
                # Check if the attributes contain a different context and memory space to override the SoC context and requested memory space.
                if extended_attributes["attribute_present"]:
                    # Append the found region.
                    readable_regions.append(override_region)

        # Search the SoC context region list for any remaining regions that intersect the requested region and do not have any extended parameters.
        for region in region_list:
            valid_region = region.getIntersection(address, end_addr_block)
            if valid_region:
                add_this_region = True
                # Check if the region was appended with extended parameters from current context. If it was not appended, append it now.
                if readable_regions:
                    for existing_region in readable_regions:
                        if (valid_region["start_address"] == existing_region["start_address"]) and \
                            (valid_region["end_address"] == existing_region["end_address"]):
                            add_this_region = False
                if add_this_region:
                    readable_regions.append(valid_region)

        if not readable_regions:
            # whole block of memory is inaccesible
            all_regions = [createRegion(address, end_addr_block, False)]
        else:
            # regions must be consecutive
            readable_regions = sorted(readable_regions, key=itemgetter("start_address"))
            # add the rest of the regions which do not have read rights - this should be done because
            # the IDE will expect to receive data for the whole block:

            first_region = readable_regions[0]
            # check if first address in the requested block is not in the readable region
            if address < first_region["start_address"]:
                all_regions.append(
                    createRegion(address, first_region["start_address"], False)
                )

            for i in range(len(readable_regions)):
                region = readable_regions[i]
                all_regions.append(region)
                if i + 1 < len(readable_regions):
                    next_region = readable_regions[i + 1]
                    if region["end_address"] != next_region["start_address"]:
                        all_regions.append(
                            createRegion(
                                region["end_address"],
                                next_region["start_address"],
                                False,
                            )
                        )

            # check if last address in the requested block is not in the readable region
            if end_addr_block > readable_regions[-1]["end_address"]:
                all_regions.append(
                    createRegion(
                        readable_regions[-1]["end_address"],
                        end_addr_block,
                        False,
                    )
                )

        ## Now send monitor read cmd for all valid regions and mark the unreadable regions with "??":
        result = ""
        for region in all_regions:
            # Compute the whole length of the region in bytes
            region_length = (
                region["end_address"] - region["start_address"]
            ) * getBytesPerAddress(context, mem_space.lower())
            # In case the region length is not divisible by the requested access_size,
            # we will read more bytes than requested and truncate later the server response to the requested length.
            # The total bytes read will be: count * access_size
            if region_length % access_size != 0:
                count = region_length / access_size + 1
            else:
                count = region_length / access_size

            if region["access"] is False:
                result += "?" * 2 * int(region_length)
            else:
                # if the requested length is bigger than the region length
                # there might be a case where the access_size may overlap a readable and non-readable region,
                # resulting in a bus error from CCS
                unaligned = region["start_address"] % access_size
                if unaligned and (access_size * count > region_length):
                    # align read address to access_size bytes
                    start_address = region["start_address"] - (
                        region["start_address"] % access_size
                    )
                    # compute the difference in bytes between the two addresses
                    diff = (
                        region["start_address"] - start_address
                    ) / getBytesPerAddress(context, mem_space.lower())
                    unwrap_result = executeReadMemCommand(
                        region,
                        context,
                        start_address,
                        access_size,
                        mem_space,
                        count,
                    )
                    read_value = checkResult(unwrap_result).strip()
                    result += read_value[int(diff * 2) : int((diff + region_length) * 2)]
                else:
                    unwrap_result = executeReadMemCommand(
                        region,
                        context,
                        region["start_address"],
                        access_size,
                        mem_space,
                        count,
                    )
                    # Truncate the read string to the requested length.
                    # region_length is multiplied by 2 to match the 2-characters hex format
                    read_value = checkResult(unwrap_result).strip()
                    result += read_value[: int(region_length * 2)]
        gdb.write("[S32Debugger]" + str(result).strip() + os.linesep)
        gdb.flush()

    # Execute directly monitor read mem command without any check for constraints
    def __do_monitor_read_mem_for_other_ctxs(
            self, context, address, access_size, mem_space, count):
        unwrap_result = utils.read_mem_str(
                        context,
                        address,
                        access_size,
                        mem_space,
                        count,
                    )
        read_value = checkResult(unwrap_result).strip()
        gdb.write("[S32Debugger]" + str(read_value).strip() + os.linesep)
        gdb.flush()
    
    def __parse_read_mem_args(self, argv):
        if len(argv) == 5:
            context = argv[0]
            address = utils.validate_numeric_input(argv[1], "address", min_value=0)
            access_size = utils.validate_numeric_input(argv[2], "access_size", min_value=1)
            mem_space = argv[3]
            count = utils.validate_numeric_input(argv[4], "count", min_value=1)
        elif len(argv) == 4:
            context = (
                utils.gdb_exec("monitor ctx get current", False, True).unwrap().rstrip()
            )
            address = utils.validate_numeric_input(argv[0], "address", min_value=0)
            access_size = utils.validate_numeric_input(argv[1], "access_size", min_value=1)
            mem_space = argv[2]
            count = utils.validate_numeric_input(argv[3], "count", min_value=1)
        elif len(argv) == 3:
            context = (
                utils.gdb_exec("monitor ctx get current", False, True).unwrap().rstrip()
            )
            address = utils.validate_numeric_input(argv[0], "address", min_value=0)
            access_size = 4
            mem_space = argv[1]
            count = utils.validate_numeric_input(argv[2], "count", min_value=1)
        elif len(argv) == 2:
            context = (
                utils.gdb_exec("monitor ctx get current", False, True).unwrap().rstrip()
            )
            address = utils.validate_numeric_input(argv[0], "address", min_value=0)
            access_size = 4
            mem_space = "virtual"
            count = utils.validate_numeric_input(argv[1], "count", min_value=1)
        elif len(argv) == 1:
            context = (
                utils.gdb_exec("monitor ctx get current", False, True).unwrap().rstrip()
            )
            address = utils.validate_numeric_input(argv[0], "address", min_value=0)
            access_size = 4
            mem_space = "virtual"
            count = 1
        else:
            raise gdb.GdbError("[S32Debugger]Error: Invalid number of parameters")
        
        return context, address, access_size, mem_space, count


class WriteCommand(gdb.Command):
    """Write memory content.
    Write memory to address using the provided access size and memory space.
    The data is presented as a series of hexadecimal byte values
    usage: mem_write [context] <address> <access_size> <space> <data>

    [context]      -        GTA (GDB server) debug context :ccs:soc[:core]
                            e.g. :ccs:S32G2XX or :ccs:S32G2XX:CortexA53#0
                            If present, the context can be a core context;
                            if not, the current context is used.
    <address>      -        Start address.
    <access_size>  -        Access size.
    <space>        -        Memory space.
    <data>         -        Data to be written as a sequence of hexadecimal byte values.

    Eg: mem_write :ccs:S32G2XX:CortexA53#0 0x89ab1234 1 virtual 1234
    """

    def __init__(self):
        super(WriteCommand, self).__init__("mem_write", gdb.COMMAND_USER)

    def invoke(self, args, from_tty):
        self.dont_repeat()
        argv = gdb.string_to_argv(args)
        context, address, access_size, mem_space, data = self.__parse_write_mem_args(argv)

        if context.lower() == "soc":
            context = getSocContext(address)
        
        if context.lower() == "dap":
            context = getDapContext(address)

        # Check memory space
        cmd = "monitor mem list-ms " + context
        ms_list = (utils.gdb_exec(cmd, False, True).
                       unwrap().rstrip()).splitlines()
        # if not given by the user, mem_space defaults to 'virtual'
        if mem_space not in ms_list:
            raise gdb.GdbError(
            "[S32Debugger]Error: Please select a valid memory space: {}\n".format(
                ", ".join(ms_list))
        )
        region_list, is_info_from_gdb_server = getConstraints(mem_space, context)

        # Some memory regions may be accessible through current core context but
        # not through SoC context. Get the current context region list as well.
        current_ctx = (utils.gdb_exec("monitor ctx get current", False, True).
                       unwrap().rstrip())
        current_context_region_list, na = getConstraints(mem_space, current_ctx)

        address_hit = False
        for region in region_list:
            if region.isInMemRegion(address)["in_memory_region"]:
                # The read location is part of a valid memory region. Check the current context memory regions to see if the memory region needs the context and memory space overriden.
                for current_context_region in current_context_region_list:
                    override_region = current_context_region.isInMemRegion(address)

                    # Check if the current region has attributes.
                    if override_region and override_region["attribute"]:
                        extended_attributes = getExtendedAttributes(override_region["attribute"])

                        # Check if the attributes contain a different context and memory space to override the SoC context and requested memory space.
                        if extended_attributes["attribute_present"]:
                            overriden_context = extended_attributes["overriden_context"]
                            overriden_mem_space = extended_attributes["overriden_mem_space"]

                            if overriden_context == "":
                                # Context string is empty. Use existing context.
                                overriden_context = context

                            if overriden_mem_space == "":
                                # Mem space string is empty. Use existing mem space.
                                overriden_mem_space = mem_space

                            # Override the context and memory space.
                            context = overriden_context
                            mem_space = overriden_mem_space
                            # Region was found with overriden attributes for the current context, use those attributes. Break the loop.
                            break
                address_hit = True
                # Memory region was found, no need to keep searching. Break the loop.
                break
        should_write_to_another_ctx =  (current_ctx != context) and not is_info_from_gdb_server

        
        if address_hit == True or len(region_list) == 0 or should_write_to_another_ctx:
            # Perform memory write operation based on context and access_size
            # Exception handling for 8-byte write operations if access size of 8bytes is not supported hw platform
            axi_cfg_state = utils.get_axi_cfg_ap_lde()
            #  Handle hardware platform limitations for 8-byte writes
            if (access_size == 8) and (axi_cfg_state == utils.Axi_Cfg_Ap_Lde_State.longdata_unsupported):
                if (("SoC" in context) or ("DAP" in context)):
                    # Split 8-byte data into two 4-byte writes for SoC/DAP contexts
                    data_low = data & 0xFFFFFFFF
                    data_high = (data >> 32) & 0xFFFFFFFF
                    # Call write_mem_verify for the high 32-bits first
                    self.__write_mem_verify(context, address, 4, mem_space, data_high)  # 63->32 bits
                    # Call write_mem_verify for the low 32-bits after high 32-bits write
                    self.__write_mem_verify(context, address + 4, 4, mem_space, data_low)  # 31->0   
                else:
                    raise gdb.GdbError("[S32Debugger]Error:  Access size of 8bytes is not supported in core context")
            else:
                # Write operation verification for access_sizes is 1 , 2, 4, 8bytes and other bytes
                self.__write_mem_verify(context, address, access_size, mem_space, data)
        else:
            raise gdb.GdbError(
                "[S32Debugger]Error: {}\n{}".format(
                    ErrorCode.WriteMemoryError.value,  # pylint: disable=no-member
                    "Write to restricted address",
                )
            )
        
    #Write data to target memory and verify the write operation.    
    def __write_mem_verify(self, context, address, access_size, mem_space, data):
        checkResult(utils.write_mem(context, address, access_size, mem_space, data))
        # Read back the memory to verify the write operation was successful

        count = 1 # Single read for number of access_size bytes
        result = checkResult(
            utils.read_mem_str(context, address, access_size, mem_space, count)
        )
        if conv_to_int("0x" + result) == data:
            gdb.write("[S32Debugger]OK" +  os.linesep)
        else:
            gdb.write(
                "[S32Debugger]Data mismatch: Expected "
                + hex(data).upper().replace('X', 'x')
                + " actual "
                + hex(conv_to_int("0x" + result)).upper().replace('X', 'x')
                + os.linesep
            )

    def __parse_write_mem_args(self, argv):
        if len(argv) == 5:
            context = argv[0]
            address = utils.validate_numeric_input(argv[1], "address", min_value=0)
            access_size = utils.validate_numeric_input(argv[2], "access_size", min_value=1)
            mem_space = argv[3]
            data = utils.validate_numeric_input(argv[4], "data")
        elif len(argv) == 4:
            context = (
                utils.gdb_exec("monitor ctx get current", False, True).unwrap().rstrip()
            )
            address = utils.validate_numeric_input(argv[0], "address", min_value=0)
            access_size = utils.validate_numeric_input(argv[1], "access_size", min_value=1)
            mem_space = argv[2]
            data = utils.validate_numeric_input(argv[3], "data")
        elif len(argv) == 3:
            context = (
                utils.gdb_exec("monitor ctx get current", False, True).unwrap().rstrip()
            )
            address = utils.validate_numeric_input(argv[0], "address", min_value=0)
            access_size = 4
            mem_space = argv[1]
            data = utils.validate_numeric_input(argv[2], "data")
        elif len(argv) == 2:
            context = (
                utils.gdb_exec("monitor ctx get current", False, True).unwrap().rstrip()
            )
            address = utils.validate_numeric_input(argv[0], "address", min_value=0)
            access_size = 4
            mem_space = "virtual"
            data = utils.validate_numeric_input(argv[1], "data")
        else:
            raise gdb.GdbError("[S32Debugger]Error: Invalid number of parameters")
            
        return context, address, access_size, mem_space, data

def create_commands():
    """Create GDB commands."""

    ReadCommand()
    WriteCommand()
    BreakConfigCommand()
    SystemConfigCommand()


def conv_to_int(num):
    # pylint: disable=all
    if sys.version_info.major == 2:
        if isinstance(num, str) or isinstance(num, unicode):
            if num.startswith("0x"):
                num = int(num, 16)
            else:
                num = int(num, 10)
        return num
    else:
        if isinstance(num, str):
            if num.startswith("0x"):
                num = int(num, 16)
            else:
                num = int(num, 10)
        return num

def getSocContext(address):
    return getContext(address, ":SoC#0")

def getDapContext(address):
    return getContext(address, ":DAP#0")

def getContext(address, suffix):
    context = utils.gdb_exec("monitor ctx get current", False, True).unwrap().rstrip()
    # use current context for addresses that are PPB mapped
    exception_list = ["M7", "M4", "M33", "M0"]
    if (
        address >= 0xE0000000
        and address <= 0xFFFFFFFF
        and any(arm in context for arm in exception_list)
    ):
        return context
    context = context[5:]  # strip :ccs:
    soc_end = context.find(":")
    soc = context[:soc_end]
    context = ":ccs:" + soc + suffix
    return context

# populate and get list of active constrains for a given memory space
def getConstraints(mem_space, context):
    utils.gdb_exec("set print elements 0")
    cmd = "monitor ctx id %s get prop %s" % (context, mem_space)
    mem_list = utils.gdb_exec(cmd, False, True)
    is_info_from_gdb_server = False
    if mem_list.iserror():
        # try to get regions from GDB, skipped for SoC ctx
        if ':SoC#0' in context or 'DAP#0' in context:
            is_info_from_gdb_server = True
        gdb_info_mem_regions = getInfoMemConstraints(mem_space)
        return gdb_info_mem_regions, is_info_from_gdb_server
    
    # if the information is retrieved from GDB Server with the passed context.
    mem_list_extended = json.loads(mem_list.unwrap().replace("\\", ""))
    mem_region_list = []
    for space in mem_list_extended.keys():
        mem_region_list.append(
            MemRegion(
                "y",
                mem_list_extended[space]["start"],
                mem_list_extended[space]["end"],
                mem_list_extended[space]["attr"],
            )
        )
    is_info_from_gdb_server = True
    return mem_region_list, is_info_from_gdb_server


# Used to parse regions from GDB "info mem" command
def getInfoMemConstraints(mem_space):
    mem_region_list = []
    mem_list = utils.gdb_exec("info mem", False, True).unwrap()
    for line in mem_list.split("\n"):
        # if "There are no memory regions defined" in line:
        #    return mem_region_list
        entry = line.split()[0] if len(line.split()) != 0 else None
        if entry != None and re.match("\d+", entry):
            subb = line.split()
            mem_region_list.append(MemRegion(subb[1], subb[2], subb[3], subb[4]))
    return mem_region_list


def getBytesPerAddress(context, mem_space):
    if "soc#0" in context.lower() or "dap#0" in context.lower():
        return 1
    cmd = "monitor mem list-ms extended"
    mem_list = utils.gdb_exec(cmd, False, True).unwrap()
    mem_list_extended = json.loads(mem_list.replace("\\", ""))
    return mem_list_extended[mem_space]["bytes-per-address"]


def checkResult(result):
    if result.iserror():
        if not ping():
            raise gdb.GdbError(
                "[S32Debugger]Error: {}\nBus error".format(
                    ErrorCode.BusError.value  # pylint: disable=no-member
                )
            )
        result_msg = str(result.error).splitlines()
        if "//" in result_msg:
            result_msg.remove("//")
        error_msg, additional, additional_str = result_msg[0:3]
        raise gdb.GdbError(
            "[S32Debugger]Error: {} \n{}\n{} {}".format(
                result.error.error_type.value,
                error_msg, additional, additional_str.replace("[", "").replace("]", ""),
            )
        )
    else:
        return result.unwrap()


def ping():
    context = utils.gdb_exec("monitor ctx get current", False, True).unwrap().rstrip()
    arm_list = ["M7", "M4", "M33", "M0", "A53", "R52"]
    if any(arm in context for arm in arm_list):  # available only for ARM context
        cmd = "x $pc"
        result = utils.gdb_exec(cmd)
        if result.iserror():
            return False
    return True


def createRegion(start, end, read_access):
    return {"start_address": start, "end_address": end, "access": read_access}

def executeReadMemCommand(region, context, start_address, access_size, mem_space, count):
        attribute = getExtendedAttributes(region["attribute"])
        
        if attribute["attribute_present"]:
            # Context and/or mem space attributes are present, override them in the current command.
            overriden_context = attribute["overriden_context"]
            overriden_mem_space = attribute["overriden_mem_space"]
            if overriden_context == "":
                # Context string is empty. Use existing context.
                overriden_context = context

            if overriden_mem_space == "":
                # Mem space string is empty. Use existing mem space.
                overriden_mem_space = mem_space
        else:
            # There are no attributes present. Use existing context and mem space.
            overriden_context = context
            overriden_mem_space = mem_space
        
        overiden_access_size = access_size
        overriden_count = count
        if access_size == 8:
            axi_cfg_state = utils.get_axi_cfg_ap_lde()
            # Attempt to catch the error for NPIs that cannot perform 8-byte read operations (8-byte read support has not been implemented for NPI)
            # Execute read mem command if access size is 8 bytes
            if axi_cfg_state == utils.Axi_Cfg_Ap_Lde_State.longdata_unsupported:
                if (("SoC" in overriden_context) or ("DAP" in overriden_context)):
                    overiden_access_size = 4 
                    overriden_count = count * 2
                else:
                    raise gdb.GdbError("[S32Debugger]Error:  Access size of 8bytes is not supported in core context")
       
        unwrap_result = utils.read_mem_str(
                            overriden_context,
                            start_address,
                            overiden_access_size,
                            overriden_mem_space,
                            overriden_count,
                        )

        return unwrap_result

def getStringAttribute(desired_attribute, attributes_string):
    """
    Here we MUST add to the desired_attribute string an = sign to be 100% sure it is what we are searching for.
    If we use the string without the = sign we will get an incorrect value, because there could be multiple 
    attributes which may have the desired attribute substring. Eg.: "context=", "context_value=", "context_type=", etc.
    """
    desired_attribute = desired_attribute + "="

    if desired_attribute in attributes_string:
        """ 
        The string of attributes contains the desired attribute.
        As the string may have a lot of attributes in any order, we split the string into one or two strings.
        Below is an example for the "context=" desired attribute.
        Eg.: 1. The string is "ro context=:ccs:SOCNAME:CORENAME mem_space=virtual" -> when we split this we have 2 strings
                ["ro", ":ccs:SOCNAME:CORENAME mem_space=virtual"]
             2. The string is "context=:ccs:SOCNAME:CORENAME mem_space=virtual" -> when we split this we have one string
                [":ccs:SOCNAME:CORENAME mem_space=virtual"]
        """
        attribute = attributes_string.split(desired_attribute)
        """
        Now we have to take the last element from the array and split it further like this:
            ":ccs:SOCNAME:CORENAME mem_space=virtual" -> [":ccs:SOCNAME:CORENAME", "mem_space=virtual"].

        Our desired attribute element will always be the first element from this sequence.
        """
        attribute = (attribute[-1].split(" "))[0]
    else:
        # The desired attribute present. Leave the return as an empty string.
        attribute = ""

    return attribute

def getExtendedAttributes(attribute_param):
    # Check if there are any attributes present.
    if attribute_param != "":
        overriden_context = getStringAttribute("context", attribute_param)
        overriden_mem_space = getStringAttribute("mem_space", attribute_param)
    else:
        overriden_context = ""
        overriden_mem_space = ""

    if overriden_context == "" and overriden_mem_space == "":
        # No attributes are present.
        attribute_present = False
    else:
        # At least one attribute is present.
        attribute_present = True

    return {
        "overriden_context": overriden_context,
        "overriden_mem_space": overriden_mem_space,
        "attribute_present": attribute_present
    }