# -*- coding: utf-8 -*-
"""
    Custom GDB command for system configuration management.

 Copyright 2025 NXP

 NXP Confidential and Proprietary. This software is owned or controlled by NXP
 and may only be used strictly in accordance with the applicable license terms.
 By expressly accepting such terms or by downloading, installing, activating
 and/or otherwise using the software, you are agreeing that you have read, and
 that you agree to comply with and are bound by, such license terms. If you do
 not agree to be bound by the applicable license terms, then you may not retain,
 install, activate or otherwise use the software.
"""

import sys
import gdb  # @UnresolvedImport  # pylint: disable=F0401
import os
import re
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 template_configs import CortexMConfig
from errors import ErrorCode

class SystemConfigOption(object):
    """Base class for system configuration options"""
    
    def __init__(self, name):
        self.name = name
        self.config_states = {}
    
    def get_help_info(self):
        """Return help information dictionary for this option"""
        raise NotImplementedError("Subclasses must implement get_help_info()")
    
    def handle(self, args):
        """Handle the option with given arguments"""
        raise NotImplementedError("Subclasses must implement handle()")
    
    def get_status(self):
        """Get current status of this option"""
        raise NotImplementedError("Subclasses must implement get_status()")


class ExternalInterruptOption(SystemConfigOption):
    """Enable masking of external interrupts on step configuration option"""
    
    def __init__(self):
        super(ExternalInterruptOption, self).__init__("imask_step")
        self.config_states = {
            'imask_step_contexts': {}
        }
        # Default state is enabled for all Cortex-M contexts
        self.default_state = True

    def _get_context_state(self, context):
        """Get the current state for a context (with default fallback)"""
        if context in self.config_states['imask_step_contexts']:
            return self.config_states['imask_step_contexts'][context]
        else:
            # Return default state for untracked contexts
            return self.default_state

    def _set_context_state(self, context, state):
        """Set the state for a context and track it"""
        self.config_states['imask_step_contexts'][context] = state

    def get_help_info(self):
        """Return help information for external_interrupt option"""
        return {
            'description': 'Configure masking of external interrupts on step for specific Cortex-M context',
            'usage': 'imask_step [context] <on/off/status>',
            'examples': [
                'system_config imask_step on',
                'system_config imask_step :ccs:S32G2XX:Cortex-M7#0 on',
                'system_config imask_step :ccs:S32G2XX:Cortex-M7#1 off',
                'system_config imask_step :ccs:S32G2XX:Cortex-M0#0 status',
                'system_config imask_step status',
                'system_config imask_step'
            ],
            'contexts': [
                'Only Cortex-M cores are supported for this functionality',
                'Context names follow the pattern: :ccs:S32G2XX:Cortex-M7#X or :ccs:S32G2XX:Cortex-M0#X',
                'Examples: :ccs:S32G2XX:Cortex-M7#0, :ccs:S32G2XX:Cortex-M7#1, :ccs:S32G2XX:Cortex-M0#0',
                'If context is not specified, the current context will be used (must be Cortex-M)'
            ]
        }
    
    def handle(self, args):
        """Handle masking of external interrupts configuration for on step scenario"""
        if len(args) == 0:
            # Show all configured contexts
            self._show_all_status()
            return
            
        if len(args) == 1:
            # Only action provided, use current context
            context = (
                utils.gdb_exec("monitor ctx get current", False, True).unwrap().rstrip()
            )
            action = args[0].lower()
        elif len(args) == 2:
            # Both context and action provided
            context = args[0]
            action = args[1].lower()
        else:
            raise gdb.GdbError("Masking of external interrupts on step option expects one or two arguments: [context] <on/off/status>")
        
        # Check if context contains "cortex-m" - currently only Cortex-M cores are supported for this functionality
        if "cortex-m" not in context.lower():
            raise gdb.GdbError("Masking of external interrupts on step configuration is currently supported only for Cortex-M cores")


        if action == "on":
            self._enable_external_interrupts_masking(context)
        elif action == "off":
            self._disable_external_interrupts_masking(context)
        elif action == "status":
            self._show_context_status(context)
        else:
            raise gdb.GdbError("Invalid action. Use: on/off/status")

    
    def get_status(self):
        """Get current status of masking of external interrupts on step"""
        return self.config_states['imask_step_contexts']
    
    def _enable_external_interrupts_masking(self, context):
        """Enable masking of external interrupts for specific context"""
        current_state = self._get_context_state(context)
        
        if current_state:
            raise gdb.GdbError("Masking of external interrupts on step for context '{0}' is already enabled".format(context))
         
        # Enable masking of external interrupts using template config command
        cmd_string = "monitor template config %s %d 1" % (context, 
                                                          int(CortexMConfig.cortexm_config_mask_interrupts_on_step))
        utils.gdb_exec(cmd_string, errcode=ErrorCode.CoreError).unwrap()

        # Track this context as enabled
        self._set_context_state(context, True)
        gdb.write("[S32Debugger]: Masking of external interrupts on step enabled for context '{0}'.\n".format(context))

    def _disable_external_interrupts_masking(self, context):
        """Disable masking of external interrupts on step for specific context"""
        current_state = self._get_context_state(context)
        
        if not current_state:
            raise gdb.GdbError("Masking of external interrupts on step for context '{0}' is already disabled".format(context))
            
        # Disable masking of external interrupts using template config command
        cmd_string = "monitor template config %s %d 0" % (context, 
                                                          int(CortexMConfig.cortexm_config_mask_interrupts_on_step))
        utils.gdb_exec(cmd_string, errcode=ErrorCode.CoreError).unwrap()

        # Track this context as disabled
        self._set_context_state(context, False)
        gdb.write("[S32Debugger]: Masking of external interrupts on step disabled for context '{0}'.\n".format(context))

    def _show_context_status(self, context):
        """Show status of masking of external interrupts on step for specific context"""
        current_state = self._get_context_state(context)
        
        if context not in self.config_states['imask_step_contexts']:
            # Context not explicitly configured, show default state
            status = "enabled (default)" if current_state else "disabled (default)"
            gdb.write("[S32Debugger]: Masking of external interrupts on step for context '{0}' is {1}.\n".format(context, status))
        else:
            # Context explicitly configured
            status = "enabled" if current_state else "disabled"
            gdb.write("[S32Debugger]: Masking of external interrupts on step for context '{0}' is {1}.\n".format(context, status))

    def _show_all_status(self):
        """Show status of all configured contexts for masking of external interrupts on step"""
        gdb.write("[S32Debugger]: Masking of External Interrupts Status:\n")
        gdb.write("=" * 50 + "\n")
        
        if self.config_states['imask_step_contexts']:
            for context, enabled in self.config_states['imask_step_contexts'].items():
                status = "enabled" if enabled else "disabled"
                gdb.write("  {0:30}: {1}\n".format(context, status))
        else:
            gdb.write("  No contexts explicitly configured.\n")
            gdb.write("  Default state for all Cortex-M contexts: enabled\n")
        
        gdb.write("=" * 50 + "\n")


class SystemConfigCommand(gdb.Command):
    '''
    Configure various system settings and parameters.
    
    Available options:
        - imask_step [context] <on/off/status>    : Enable masking of external interrupts on step for specific Cortex-M context

    usage (case-sensitive): system_config <option> [arguments...]
    
    Examples:
        system_config imask_step :ccs:S32G2XX:Cortex-M7#0 on
        system_config imask_step :ccs:S32G2XX:Cortex-M7#1 off
        system_config imask_step on
        system_config imask_step off
        system_config imask_step status
        system_config imask_step
        system_config help imask_step

    For detailed help on specific options, use: system_config help <option>
    '''

    ###########################################################################
    def __init__(self):
        super(SystemConfigCommand, self).__init__("system_config", gdb.COMMAND_USER)
        
        # Dictionary to store registered option instances
        self._options = {}
        
        # Register available options
        self._register_options()

    ###########################################################################
    def _register_options(self):
        """Register all available system configuration options"""
        # Register external_interrupt option
        external_interrupt_option = ExternalInterruptOption()
        self._options[external_interrupt_option.name] = external_interrupt_option
        
        # Future options will be registered here automatically
        # Example:
        # cache_option = CacheOption()
        # self._options[cache_option.name] = cache_option

    ###########################################################################
    def invoke(self, arguments, from_tty):
        self.dont_repeat()
        argv = gdb.string_to_argv(arguments)
        
        if len(argv) == 0:
            available_options = ', '.join(self._options.keys())
            error_msg = ("[S32Debugger]Error: system_config requires an option.\n"
                        "Available options: {0}, help\n"
                        "Run 'help system_config' for general information.".format(available_options))
            raise gdb.GdbError(error_msg)
            
        option = argv[0].lower()
        args = argv[1:] if len(argv) > 1 else []
        
        try:
            if option in self._options:
                self._options[option].handle(args)
            elif option == "help":
                if len(args) == 0:
                    available_options = ', '.join(self._options.keys())
                    error_msg = ("[S32Debugger]Error: help option requires a specific option name.\n"
                                "Available options: {0}\n"
                                "Example: system_config help external_interrupt".format(available_options))
                    raise gdb.GdbError(error_msg)
                else:
                    self._show_option_help(args[0])
            else:
                self._show_invalid_option_error(option)
        except Exception as e:
            error_msg = "[S32Debugger]Error: {0}".format(str(e))
            raise gdb.GdbError(error_msg)

    ###########################################################################
    def _show_option_help(self, option):
        """Show detailed help for a specific option"""
        option = option.lower()
        
        if option not in self._options:
            available_options = ', '.join(self._options.keys())
            error_msg = ("[S32Debugger]Error: No help available for option '{0}'.\n"
                        "Available options: {1}".format(option, available_options))
            raise gdb.GdbError(error_msg)
        
        option_info = self._options[option].get_help_info()
        
        gdb.write("\n[S32Debugger]: Help for '{0}' option\n".format(option))
        gdb.write("=" * 40 + "\n")
        gdb.write("Usage: system_config {0}\n\n".format(option_info['usage']))
        
        gdb.write("Examples:\n")
        for example in option_info['examples']:
            gdb.write("  {0}\n".format(example))
        
        if 'contexts' in option_info:
            gdb.write("\nContexts:\n")
            for context in option_info['contexts']:
                gdb.write("  {0}\n".format(context))
        
        gdb.write("\n")

    def _show_invalid_option_error(self, option):
        """Show error for invalid option"""
        available_options = ', '.join(self._options.keys())
        error_msg = ("[S32Debugger]Error: Invalid option '{0}'.\n"
                    "Available options: {1}, help\n"
                    "Run 'help system_config' for general information.".format(option, available_options))
        raise gdb.GdbError(error_msg)

    ###########################################################################
    def register_option(self, option_instance):
        """Register a new option instance (for external registration)"""
        if not isinstance(option_instance, SystemConfigOption):
            raise ValueError("Option must be an instance of SystemConfigOption")
        self._options[option_instance.name] = option_instance

    def get_option(self, option_name):
        """Get option instance by name"""
        return self._options.get(option_name.lower())

    def get_all_options(self):
        """Get all registered options"""
        return self._options
