# Copyright 2024-2025 NXP

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

import inspect
import logging
import os
import sys
import subprocess
from utils import demo_flash_utils

logging.basicConfig(format="%(message)s", level=logging.DEBUG)

_INITIAL_PYTHON_PATH = ''
_INITIAL_PYTHON_HOME = ''

_FILE_CONFIGURATION = ''
GTA_PATH = None

###################################################################
######### Parameters Default
###################################################################

#Reset settings
_RESET_DELAY = 1
_RESET_TYPE = 'default'
_REMOTE_TIMEOUT = 100

# Python path
_PYTHON_PATH = ""
# Python path
_PYTHON_lib = "Python/Lib;"
_PYTHON_packages = "Python/Lib/site-packages;"
_PYTHON_dynload = "Python/Lib/lib-dynload"

#Python home
_PYTHON_HOME = "Python"

_RESULTEXCEPTION = True

# Remote target responses time-out (seconds).
_GDB_TIMEOUT = 7200

INSTALL_DIRECTORY = None

# GDB path and version arm32 or arm64
ARM32_GDB_PATH = "gdb/gdb-arm/arm32-eabi/bin/arm-none-eabi-gdb-py"
ARM64_GDB_PATH = "gdb/gdb-arm/arm64-eabi/bin/aarch64-none-elf-gdb-py"
RISCV_GDB_PATH = "gdb/gdb-riscv/bin/riscv32-unknown-elf-gdb-py"


#Map parameters
def initialize_parameters_with_value(_SOC_NAME, _CORE_NAME, _PROBE_IP, _JTAG_SPEED, _GDB_SERVER_PORT,_CCS_IP,_CCS_PORT,_IS_LOGGING_ENABLED, _SECURE_TYPE = None, _SECURE_KEY = None, _LIFECYCLE = None):    
    global parameters_with_value

    parameters_with_value = dict([
                            ('_SOC_NAME', _SOC_NAME), 
                            ('_CORE_NAME', _SOC_NAME + "_" + _CORE_NAME ),
                            ('_PROBE_IP', _PROBE_IP), 
                            ('_JTAG_SPEED', _JTAG_SPEED), 
                            ('_GDB_SERVER_PORT', _GDB_SERVER_PORT),
                            ('_GDB_TIMEOUT', _GDB_TIMEOUT),  
                            ('_IS_LOGGING_ENABLED', _IS_LOGGING_ENABLED), 
                            ('_REMOTE_TIMEOUT', _REMOTE_TIMEOUT), 
                            ('_RESULTEXCEPTION', _RESULTEXCEPTION),
                            ('_RESET_DELAY', _RESET_DELAY), 
                            ('_CCS_IP', _CCS_IP), 
                            ('_CCS_PORT', _CCS_PORT), 
                            ('_RESET_TYPE', _RESET_TYPE),
                            ('_SECURE_TYPE', _SECURE_TYPE),
                            ('_SECURE_KEY', _SECURE_KEY),
                            ('_LIFECYCLE', _LIFECYCLE)])

# Get extra parameters to be passed to GTA
def get_extra_parameters():
    parameters = ""
    extra_params = {}
    try:
        stack = inspect.stack()
        for item in stack:
            if "_EXTRA_PARAMS" in item.frame.f_globals:
                extra_params = item.frame.f_globals["_EXTRA_PARAMS"]
                break
    except:
        pass
    for param, value in extra_params.items():
        if type(value) == str:
            parameters+= "py " + param + " = \"" + str(value) + "\" \n"
        else:
            parameters+= "py " + param + " = " + str(value) + "\n"
    return parameters

# Generating necessary parameters for gdb
def set_parameters_SOC(init_script, file_debug):
    parameters = ''

    list_Keys = parameters_with_value.keys()
    
    for x in list_Keys:
        if type(parameters_with_value.get(x)) == str:
            parameters+= "py " + x + " = \"" + str(parameters_with_value.get(x)) + "\" \n"
        else:
            parameters+= "py " + x + " = " + str(parameters_with_value.get(x)) + "\n"

    parameters+= get_extra_parameters()
    parameters+= "source " +  init_script + "\n"
    parameters+= "py " +  "board_init()" + "\n"
    parameters+= "py " +  "core_init()" + "\n"

    if( file_debug != ''):
        parameters += "symbol-file " + file_debug + "\n"
        parameters += "load " + file_debug + "\n"
        parameters += "b main\nc\n"
    else:
        logging.warning("Use file <elf_file> and load to specify an ELF file to load and debug.") 

    return parameters

#Enable logging
def enable_gdb_logging():
    parameters = ''
    parameters += "set logging file gdb_log.txt" +"\n"
    parameters += "set logging overwrite on" +"\n"
    parameters += "set logging enabled on" +"\n"
    return parameters

# Creating file with arguments necessary for gdb
def config_parameters_SOC(curent_directory, init_script, file_debug, install_directory="../../../../"):
    global _FILE_CONFIGURATION
    global INSTALL_DIRECTORY
    
    INSTALL_DIRECTORY = install_directory


    _FILE_CONFIGURATION = parameters_with_value.get('_SOC_NAME') + "_" + parameters_with_value.get('_CORE_NAME') + ".txt"

    file = open(os.path.join(curent_directory, _FILE_CONFIGURATION), 'w')

    parameters = enable_gdb_logging()
    parameters += set_parameters_SOC(init_script, file_debug)
    file.write(parameters)
    file.flush()

# Save initial python path
def get_initial_python_path():
    global _INITIAL_PYTHON_PATH
    global _INITIAL_PYTHON_HOME
    _INITIAL_PYTHON_PATH = os.getenv('PYTHONPATH')
    _INITIAL_PYTHON_HOME = os.getenv('PYTHONHOME')

# Set necesary python path value
def set_python_variable():
    global _PYTHON_HOME
    global _PYTHON_PATH
    global _PYTHON_packages
    global _PYTHON_dynload
    global _PYTHON_lib
    global INSTALL_DIRECTORY
    if INSTALL_DIRECTORY is None:
         _PYTHON_PATH = "../../../../" +  _PYTHON_lib + "../../../../" + _PYTHON_packages + "../../../../" + _PYTHON_dynload
         _PYTHON_HOME = "../../../../" + _PYTHON_HOME
    elif not os.path.isabs(_PYTHON_HOME):
        _PYTHON_PATH = INSTALL_DIRECTORY + "/" + _PYTHON_lib + INSTALL_DIRECTORY + "/" + _PYTHON_packages + INSTALL_DIRECTORY + "/" + _PYTHON_dynload
        _PYTHON_HOME = INSTALL_DIRECTORY + "/" + _PYTHON_HOME
    # absolute path
    if (os.name!='posix'):
        os.environ["PYTHONPATH"] = _PYTHON_PATH
        os.environ["PYTHONHOME"] = _PYTHON_HOME

# Set initial python path value
def clear_python_variable():
    if(_INITIAL_PYTHON_HOME is not None):
        os.environ["PYTHONHOME"] = _INITIAL_PYTHON_HOME

    if(_INITIAL_PYTHON_PATH is not None):     
        os.environ["PYTHONPATH"] = _INITIAL_PYTHON_PATH

def start_gta():
    logging.info("Starting GTA...")

    if(GTA_PATH is not None):
        path = GTA_PATH
    else:
        path = INSTALL_DIRECTORY + '/S32Debugger/Debugger/Server/gta/gta'

    if (os.name=='posix'):
        gta = subprocess.Popen(['gnome-terminal -- '+path], shell = True)
    else:
        gta = subprocess.Popen( 'start ' + path + ".exe", shell = True)

def start_gdb(interactive=True):

    get_initial_python_path()
    set_python_variable()

    if parameters_with_value.get('_CORE_NAME') is not None and "A53" in parameters_with_value.get('_CORE_NAME'):
        if os.path.isabs(ARM64_GDB_PATH):
            gdb_path = ARM64_GDB_PATH
        else:
            gdb_path = INSTALL_DIRECTORY + "/" + ARM64_GDB_PATH
    elif parameters_with_value.get('_CORE_NAME') is not None and "ZV" in parameters_with_value.get('_CORE_NAME'):
        if os.path.isabs(RISCV_GDB_PATH):
            gdb_path = RISCV_GDB_PATH
        else:
            gdb_path = INSTALL_DIRECTORY + "/" + RISCV_GDB_PATH
    else:
        if os.path.isabs(ARM32_GDB_PATH):
            gdb_path = ARM32_GDB_PATH
        else:
            gdb_path = INSTALL_DIRECTORY + "/" + ARM32_GDB_PATH 

    logging.info("Starting GDB...")
    if os.name == 'posix':
        if interactive:
            gdb_proc = subprocess.Popen(['gnome-terminal -- ' + gdb_path + ' -x ' + _FILE_CONFIGURATION], shell=True)
        else:
            gdb_proc = subprocess.Popen([gdb_path + ' -x ' + _FILE_CONFIGURATION], 
                                   stdin=subprocess.PIPE,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE,
                                   universal_newlines=True,
                                   shell=True)
    else:
        if interactive:
            gdb_proc = subprocess.Popen('start ' + gdb_path + ".exe -x " + _FILE_CONFIGURATION, shell=True)
        else:
            gdb_proc = subprocess.Popen(gdb_path + ".exe -x " + _FILE_CONFIGURATION, 
                                   stdin=subprocess.PIPE,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE,
                                   universal_newlines=True,
                                   shell=True)
    clear_python_variable()
    return gdb_proc


#Create name core with _SOC_NAME, _CORE_NAME, _CORE_ID, _CLUSTER_ID, _Core_LOCKSTEP 
def create_complete_Core_Name(soc_name,core_name,core_id,cluster_id,core_lockstep):

    if('S32G2' in soc_name) or ('S32K3' in soc_name) or ('S32R45' in soc_name) or ('S32R47' in soc_name) or ('S32R41' in soc_name):
        Core_CTX = core_name
    else:
        Core_CTX = soc_name+ "_" + core_name

    if ('S32G3' in soc_name) and core_lockstep:
        Core_CTX = Core_CTX
    elif(cluster_id!=''):
        Core_CTX = Core_CTX + "_" + str(cluster_id)
    
    if(core_id!=''):
        Core_CTX = Core_CTX + "_" + str(core_id)

    if core_lockstep:
        Core_CTX = Core_CTX + "_LS"

    parameters_with_value['_CORE_NAME'] = Core_CTX
        
def initialize_FP_parameters_with_value(_SOC_NAME, _FLASH_TYPE, _FLASH_NAME, _PROBE_IP,
                                        _JTAG_SPEED, _GDB_SERVER_PORT,
                                        _CCS_IP, _CCS_PORT,
                                        _IS_LOGGING_ENABLED,_GDB_TIMEOUT = 7200, 
                                        _RESET_DELAY = 2,_RESET_TYPE = "default",
                                        _SECURE_TYPE = None,_SECURE_KEY = None,
                                        _LIFECYCLE = None, _RESULTEXCEPTION = True
                                        ):
    global parameters_with_value

    parameters_with_value = dict([('soc_name', _SOC_NAME), ('flash_type', _FLASH_TYPE), ('flash_name', _FLASH_NAME), 
                       ('probe_ip', 's32dbg'), ('jtag_speed', _JTAG_SPEED), ('gdb_server_port', _GDB_SERVER_PORT), 
                       ('gdb_timeout', _GDB_TIMEOUT), ('reset_delay', _RESET_DELAY), ('reset_type', _RESET_TYPE),
                       ('is_logging_enabled', _IS_LOGGING_ENABLED),
                       ('secure_type', _SECURE_TYPE), ('secure_key', _SECURE_KEY), 
                       ('lifecycle', _LIFECYCLE), ('ccs_ip', _CCS_IP), 
                       ('ccs_port', _CCS_PORT), ('resultexception', _RESULTEXCEPTION)])
    
    if (_PROBE_IP != ''):
        parameters_with_value['probe_ip'] = parameters_with_value.get('probe_ip') + ":" + _PROBE_IP
    
    
def set_parameters_FP_SOC(directory, parameters_with_value, init_script, file_debug=None, file_bin=None, extra_flash_commands=None):
    parameters = ''
    parameters += "source " + directory + "/S32Debugger/Debugger/scripts/gdb_extensions/flash/s32flash.py" + " \n"
    
    list_Keys = parameters_with_value.keys()
    paramsbuff = ""
    for x in list_Keys:
        if type(parameters_with_value.get(x)) == str:
            paramsbuff+= x + " = \"" + str(parameters_with_value.get(x)) + "\"" + ","
        else:
            paramsbuff+= x + " = " + str(parameters_with_value.get(x)) + ","

    parameters += get_extra_parameters()
    paramsbuff += "init_script = \"" + init_script +"\""   
    parameters += "py setup(" + paramsbuff + ") \n"
    parameters += "py " +  "flash()" +"\n"

    # Handle ELF files
    if file_debug:
        if isinstance(file_debug, list):
            # Multiple ELF files
            for elf_file in file_debug:
                parameters += "fl_write_elf -e " + elf_file + "\n"
            parameters += "py " +  "reset()" +"\n"
            # Set debug symbols - first as main executable, rest as additional symbols
            parameters += "file " + file_debug[0] + "\n"
            for elf_file in file_debug[1:]:
                parameters += "symbol-file " + elf_file + "\n"
            parameters += "set $pc=$PC\n"
            parameters += "b main\nc\n"
        else:
            # Single ELF file (string)
            parameters += "fl_write_elf -e " + file_debug + "\n"
            parameters += "py " +  "reset()" +"\n"
            parameters += "file " +  file_debug +"\n"
            parameters += "set $pc=$PC\n"
            parameters += "b main\nc\n"
    # Handle binary files
    if file_bin:
        if isinstance(file_bin, dict):
            # Multiple binary files with address map: {"0x8000000": "file.bin", ...}
            parameters += "fl_erase_all\n"
            for address, bin_file in file_bin.items():
                parameters += "fl_write " + str(address) + " " + bin_file + "\n"
        else:
            # Single binary file string
            parameters += "fl_erase_all\n"
            parameters += "fl_write " + "0 " + file_bin + "\n"

    # Add extra flash commands
    if extra_flash_commands:
        if isinstance(extra_flash_commands, list):
            for cmd in extra_flash_commands:
                parameters += cmd + "\n"
        else:
            # Single command string
            parameters += extra_flash_commands + "\n"

    return parameters



def config_parameters_FP_SOC(curent_directory, init_script, file_debug=None,
                             file_bin=None, install_directory = "../../../../",
                             extra_flash_commands=None):
    global _FILE_CONFIGURATION
    global INSTALL_DIRECTORY

    INSTALL_DIRECTORY = install_directory

    _FILE_CONFIGURATION = parameters_with_value.get('soc_name') + parameters_with_value.get('flash_name') + "_FlashProgrammer_config_file.txt"
    
    file = open(os.path.join(curent_directory, _FILE_CONFIGURATION), 'w+')
    
    parameters = enable_gdb_logging()
    parameters += set_parameters_FP_SOC(install_directory, parameters_with_value, init_script,
                                       file_debug, file_bin, extra_flash_commands)
    file.write(parameters)
    file.flush()
