# -*- coding: utf-8 -*-
"""
    Custom GDB commands for thread awareness.


 Copyright 2023, 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 gdb
import os
import sys
import string
import types
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")

from zephyr import Zephyr, z_debug_on
from freertos import FreeRTOS

# To be updated when more RTOS are added
OS_SUPPORTED = [ Zephyr, FreeRTOS ]

import mem_reg_utils as utils
from errors import GtaLibException, ErrorCode

# Detect automatically the OS
def auto_detect_os(opts):
    for o in OS_SUPPORTED:
        os = o()
        os.set_debug(opts['debug'])
        os.set_perf(opts['perf'])
        found,_ = os.detect()
        if found:
            return o

    return None

def get_options(argv):
    keys = ['json', 'debug', 'perf']
    options = {}

    for k in keys:
        opt = '--' + k
        options[k] = False

        for a in argv:
            if opt in a:
                options[k] = True
                argv.remove(a)

    return options


class ListOsAwareCommand(gdb.Command):
    """List thread awareness OS supported.

usage: thread-aware-list-supported-os [--json] [--debug]

optional arguments:
  --json         use json format
  --debug        enable debug messages

    """

    def __init__(self):
        """
        Constructor
        """
        super(ListOsAwareCommand, self).__init__("thread-aware-list-supported-os", gdb.COMMAND_USER)

    def invoke(self, args, from_tty):
        """ Called when the command is invoked """
        self.dont_repeat()
        argv = gdb.string_to_argv(args)
        opts = get_options(argv)
        os_supported = []

        for os in OS_SUPPORTED:
            os_supported.append(os.__name__)

        os_detected = auto_detect_os(opts)
        os_name = None

        if os_detected:
            os_name = os_detected.__name__

        if opts['json']:
            dict = {}
            dict['SupportedOS'] = os_supported

            if os_name:
                dict['DetectedOS'] = os_name

            gdb.write(json.dumps(dict, indent=4, sort_keys=True) + '\n')
        else:
            gdb.write("Supported OS: ")
            os_list = ", ".join(os_supported)
            gdb.write(os_list)
                
            gdb.write('\n')

            if os_name:
                gdb.write("Detected OS: {0}\n".format(os_name))

class ListObjectsAwareCommand(gdb.Command):
    """List thread awareness objects supported by selected OS.

usage: thread-aware-list-supported-objs [--json] [--debug] [--perf] [OS]

optional arguments:
  OS             selected OS (or missing for auto detection)
  --json         use json format
  --debug        enable debug messages
  --perf         enable performance meassure

    """

    def __init__(self):
        """
        Constructor
        """
        super(ListObjectsAwareCommand, self).__init__('thread-aware-list-supported-objs', gdb.COMMAND_USER)

    def invoke(self, args, from_tty):
        """ Called when the command is invoked """
        self.dont_repeat()
        argv = gdb.string_to_argv(args)
        opts = get_options(argv)

        os = None
        os_req = None

        if len(argv) == 0:
            os_type = auto_detect_os(opts)
            if os_type:
                os = os_type()
        elif len(argv) == 1:
            os_req = argv[0]
            for o in OS_SUPPORTED:
                if os_req in o.__name__:
                    os_found = True
                    os = o()
        else:
            raise gdb.GdbError('Too many parameters than expected!')

        if os:
            os.set_debug(opts['debug'])
            os.set_perf(opts['perf'])
            gdb.write(os.list_supported_types(opts['json']) + '\n')
        elif os_req:
            raise gdb.GdbError('The OS selected <{0}> is not supported!'.format(os_req))
        else:
            raise gdb.GdbError('No OS detected!')

class ReadObjectsAwareCommand(gdb.Command):
    """Read thread awareness objects supported by selected OS.

usage: thread-aware-read-objs [--json] [--debug] [--perf] [OS] [list]

optional arguments:
  OS            selected OS (or missing for auto detection)

  --json        use json format
  --debug       enable debug messages
  --perf        enable performance meassure
  list          list with objects types to be read, separated by space.
                May be empty for all objects types.
                Use the command 'thread-aware-list-supported-objs' to list all
                OS' supported objects.
    """
    def __init__(self):
        """
        Constructor
        """
        super(ReadObjectsAwareCommand, self).__init__('thread-aware-read-objs', gdb.COMMAND_USER)

    def invoke(self, args, from_tty):
        """ Called when the command is invoked """
        self.dont_repeat()
        argv = gdb.string_to_argv(args)
        opts = get_options(argv)

        os_req = None
        os = None

        if argv:
            os_req = argv[0]

        if os_req:
            # Is this a supported OS?
            for o in OS_SUPPORTED:
                if os_req in o.__name__:
                    os = o()
                    argv.remove(argv[0])
                    break

        if not os:
            os_type = auto_detect_os(opts)
            if not os_type:
                raise gdb.GdbError('No OS detected!')
            else:
                os = os_type()

        types = []
        for a in argv:
            types.append(a)

        try:
            if os:
                os.set_debug(opts['debug'])
                os.set_perf(opts['perf'])
                gdb.write('\n' + os.list_objects(types, json_format=opts['json']) + '\n')
        except Exception as e:
            gdb.GdbError(str(e))

def create_commands():
    """Create GDB commands."""
    ListOsAwareCommand()
    ListObjectsAwareCommand()
    ReadObjectsAwareCommand()
