"""Enable automatic profiling of the python code using sys.profile."""

import sys

import omni.kit.app
import omni.ext

import carb
import carb.profiler
import carb.dictionary
import carb.settings

_profiler_enabled = False
_profile_zone_counter = 0
_python_stack_level = 0
MAX_STACK_LEVEL = 10

EXT_SETTINGS_SECTION = "/exts/omni.kit.profile_python"


def set_profiler_enabled(value: bool):
    global _profile_zone_counter
    global _python_stack_level
    global _profiler_enabled
    global MAX_STACK_LEVEL
    if value == _profiler_enabled:
        return

    MAX_STACK_LEVEL = carb.settings.get_settings().get(EXT_SETTINGS_SECTION + "/maxStackLevel")

    if value:

        def profile_calls(frame, event, arg):
            global _profile_zone_counter
            global _python_stack_level

            if event == "call":
                _python_stack_level += 1

                if _python_stack_level > MAX_STACK_LEVEL:
                    return

                co = frame.f_code
                filename = co.co_filename
                if filename[-25:] == r"carb\profiler\__init__.py" or filename[-25:] == "carb/profiler/__init__.py":
                    # Do not profile profiler events
                    return
                func_name = co.co_name
                line_no = frame.f_lineno
                carb.profiler.begin_with_location(1, "Py::" + func_name, func_name, filename, line_no)
                _profile_zone_counter += 1
                return profile_calls
            elif event == "return":
                _python_stack_level -= 1

                if _python_stack_level >= MAX_STACK_LEVEL:
                    return

                if _profile_zone_counter == 0:
                    return
                carb.profiler.end(1)
                _profile_zone_counter -= 1
                return profile_calls

        _profile_zone_counter = 0
        sys.setprofile(profile_calls)
    else:
        while _profile_zone_counter > 0:
            carb.profiler.end(1)
            _profile_zone_counter -= 1
        sys.setprofile(None)

    _profiler_enabled = value


class ProfilePython(omni.ext.IExt):
    def _on_enable_change(self, item: carb.dictionary.Item, change_event_type: carb.settings.ChangeEventType):
        self._enabled = self._settings.get(EXT_SETTINGS_SECTION + "/enable")
        set_profiler_enabled(self._enabled)

    def on_startup(self):
        self._dictionary = carb.dictionary.acquire_dictionary_interface()
        self._settings = carb.settings.get_settings()

        self._change_subs = self._settings.subscribe_to_node_change_events(
            EXT_SETTINGS_SECTION + "/enable", self._on_enable_change
        )
        self._enabled = self._settings.get(EXT_SETTINGS_SECTION + "/enable")
        set_profiler_enabled(self._enabled)

    def on_shutdown(self):
        self._settings.unsubscribe_to_change_events(self._change_subs)
        self._enabled = False
        set_profiler_enabled(self._enabled)

        self._change_subs = None
        self._settings = None
        self._dictionary = None
