
from .utils_auth import UserInformationModel, ensureLicensingServerRuns, getLicenses, getUserInformation, isWaitingForLoginOrLogout

from .utils_icons import getPreviewIcon

from .utils_auto_update import checkIfUpdateIsAvailableAndShowDialogueWithButton, drawUpdateButton
from .utils_blender import objectIsVisibleInViewport
from .utils_license import hasIndieLicense, hasProLicense
from .blender_export import ExportContext, onExportFromFileMenu, onExportFromFileMenuFinished, reportError
from .utils_debug import debug_err, debug_log
from .settings_scene import tryGetMainSceneSettings

from . import auto_load
from io_scene_gltf2.io.com.gltf2_io_extensions import Extension

from .__meta__ import getVersion, setName, setVersion
from .component_registry import *
from . import utils
from . import component_watcher
from .settings_addon import _registerAddonName
from .lightmapping.lightmapping import register as registerLightmapping, unregister as unregisterLightmapping
from .menu_items import register as registerMenuItems, unregister as unregisterMenuItems
from .server.server import register as registerServer, unregister as unregisterServer
from .panels_deploy import register as registerDeployPanel, unregister as unregisterDeployPanel
from .utils_bugreport import isExportingBug, register as registerBugreporter, unregister as unregisterBugreporter

from .extensions.NEEDLE_components_unknown import register as registerUnknownComponents, unregister as unregisterUnknownComponents
from .extensions.animationhandler import AnimationHandler
from .extensions.NEEDLE_lightmaps import NEEDLE_lightmaps
from .extensions.NEEDLE_components import NEEDLE_components
from .extensions.NEEDLE_gameobject_data import NEEDLE_gameobject_data, register as registerGameObjectData, unregister as unregisterGameObjectData
from .extensions.NEEDLE_persistent_assets import NEEDLE_persistent_assets
from .extensions.NEEDLE_progressive import NEEDLE_progressive_texture_settings
from .extensions.KHR_animation_2 import KHR_animation_2
from .extensions.NEEDLE_texture_compression import NEEDLE_texture_compression_EXT

from .analytics import send_analytics_started

from .types.animation import register as animator

bl_info = {
    "name": "Needle Engine Exporter for Blender",
    "author": "Needle",
    "description": "",
    "blender": (4, 0, 0),
    "location": "",
    "version": (0, 60, 2),
    "warning": "",
    "category": "Export",
    "doc_url": "https://engine.needle.tools/docs/blender?utm_source=blender&utm_medium=addon",
}

setName(bl_info["name"])
setVersion(bl_info["version"][0], bl_info["version"][1], bl_info["version"][2]) 

auto_load.init()

_registerAddonName(__name__)



teams_enum_items = []

# bpy.types.Scene.NEEDLE_team_id = bpy.props.StringProperty(
#     name="Needle Team ID",
#     description="The team id of the currently selected needle team",
# )

def getTeamsEnumItems(self, context):
    items = []
    userInformation = getUserInformation()
    if userInformation and hasattr(userInformation, "orgs"):
        return [(team.id, team.name, "") for team in userInformation.orgs]
    else :
        return [("0", "None", "No teams available")] 

def onSelectedTeamChanged(self, context):
    getLicenses(True)
    pass

class NeedleExporterPreferences(bpy.types.AddonPreferences):
    bl_idname = __name__
    
    def __forceUpdateLicense(self, context):
        # hasLicense(True) 
        pass


    manage_packages: bpy.props.BoolProperty(
        name="Allow Automatic Needle Engine Package Management",
        description="When enabled the Needle Engine addon will automatically manage a few known packages in web projects and update it to match the version that the addon expected. It is recommended to keep this setting enabled unless you know what you're doing!",
        default=True,
    )


    # in dev environment the selection will be reset to the first team (e.g. when the addon is reloaded)
    selected_team: bpy.props.EnumProperty(name="Active Team", items=getTeamsEnumItems, update=onSelectedTeamChanged)
    
    def drawTeamSelectionDropdown(self, layout, context):
        '''
        Draw the team selection dropdown in the preferences window.
        '''
        userInformation = getUserInformation()
        if userInformation and userInformation.authenticated:
            layout.prop(self, "selected_team", text="Team")
            return True
        return False
    
    def drawLoginLogoutButton(self, layout, userInformation:UserInformationModel | None):
        '''
        Draw the login/logout button in the preferences window.
        '''
        if not userInformation:
            userInformation = getUserInformation()
        if not userInformation:
            return
        isLoggedIn = userInformation and userInformation.authenticated
        row = layout.row(align=True)
        if isWaitingForLoginOrLogout():
            row.operator("needle.logout", text="Running...", icon="URL")
        elif isLoggedIn:
            row.operator("needle.logout", text="Logout", icon="CANCEL")
        else:
            row.operator("needle.login", text="Login", icon="URL")

    def draw(self, context):
        layout = self.layout

        # self.team_options.clear()
        # new_team = self.team_options.add()
        # new_team.team_id = "0"
        # new_team.team_name = "Personal"

        ensureLicensingServerRuns(False)

        latestVersion = utils.getLatestVersion()
        if latestVersion != None and latestVersion != getVersion():
            updateLayout = layout.column(align=True)
            updateLayout.label(text="A new version of Needle Engine is available. Click the button below to update:")
            row = updateLayout.row(align=True)
            row.operator("wm.url_open", text="Manual Update: Download new addon version", icon='LIGHT').url = "https://engine.needle.tools/downloads/blender"
            drawUpdateButton(row, latestVersion)


        licenseLayout = layout.column()

        userInformation = getUserInformation()
        row = licenseLayout.split(factor=0.8)
        row.label(text="Needle Account")
        accountButtons = row.row()
        accountButtons.enabled = isWaitingForLoginOrLogout() == False
        self.drawLoginLogoutButton(accountButtons, userInformation)

        if userInformation and userInformation.authenticated:
            row = licenseLayout.row()
            row.label(text="Logged in as")
            row.label(text=userInformation.name)
            row = licenseLayout.row()
            row.label(text="Email")
            row.label(text=userInformation.email)

            row = licenseLayout.row()
            self.drawTeamSelectionDropdown(row, context)

            row = licenseLayout.split()
            row.label(text="License")
            if hasIndieLicense():
                row.label(text="INDIE license")
            elif hasProLicense():
                row.label(text="PRO license")
            else:
                row.label(text="No license")
        else:
            row = licenseLayout.row()
            row.enabled = False
            row.label(text="Please login to your Needle account")
            

        # licenseLayout.separator()
        # licenseRow = licenseLayout.row()
        # open_url = licenseRow.operator("wm.url_open", text="Commercial Plans", icon="URL")
        # open_url.url = "https://needle.tools/pricing"
        # open_url = licenseRow.operator("wm.url_open", text="Manage Seats", icon="URL")
        # open_url.url = "https://billing.stripe.com/p/login/14k3cJ1Rra807QI7ss"
        # open_url = licenseRow.operator("wm.url_open", text="Terms of Use", icon="DOCUMENTS")
        # open_url.url = "https://needle.tools/eula"

        # licenseLayout.separator()
        # licenseLayout.label(text="Needle Spaces")
        # needleSpacesLayout = licenseLayout.column()
        # needleSpacesLayout.prop(self, "needle_spaces_access_token", text="Access Token")
        
        layout.separator()
        layout.label(text="Addon Settings")

        download_samples_url = "https://engine.needle.tools/downloads/blender/download-samples?utm_source=blender&utm_medium=preferences&utm_campaign=download-samples"
        layout.operator("wm.url_open", text="Download Needle Engine Blender Samples", icon='URL').url = download_samples_url

        layout.prop(self, "manage_packages")
        if self.manage_packages == False:
            layout.label(text="Auto Package management is disabled. The addon will not automatically update packages.", icon="ERROR")







class NeedleGLTFExportSettings(bpy.types.PropertyGroup):
    """These settings are used in the gltf export window for when a user exports a glTF or GLB using File/Export"""
    @staticmethod
    def get():
        if hasattr(bpy.context.scene, "NEEDLE_gltf_exporter_settings"):
            return bpy.context.scene.NEEDLE_gltf_exporter_settings
        return None
    
    useNeedleEngine: bpy.props.BoolProperty(
        name="Use Needle Engine",
        description="Use Needle Engine for glTF export",
        default=False,
    )

    exportMode: bpy.props.EnumProperty(
        name="Export",
        description="What to export from the Needle Engine Addon",
        items=[
            ("EVERYTHING", "Everything", "Export everything: This will export lightmaps, the skybox/environment light and will potentially create extra objects with components based on your Needle Engine scene settings"),
            ("COMPONENTS_ONLY", "Components Only", "Export only components: This will only export currently setup components in the scene. No extra objects will be created. This will not export lightmaps, the skybox or environment lighting images"),
        ],
        default="COMPONENTS_ONLY",
    )

__exporter_SettingsClosed = True
def draw(context, layout):
    global __exporter_SettingsClosed
    settings = NeedleGLTFExportSettings.get()
    if not settings: # e.g. if the addong has been disabled
        return
    """The glTF exporter window automatically calls this draw method. So these are the settings that are displayed in the export dialogue"""
    header, body = layout.panel("NEEDLE_gltf_exporter_window_root", default_closed=__exporter_SettingsClosed)

    header.use_property_split = False # fix offset https://github.com/KhronosGroup/glTF-Blender-IO/issues/2253
    icon = getPreviewIcon("logo_small")
    header.prop(settings, "useNeedleEngine", text="")
    header.label(text="Needle Engine")
    header.label(text="", icon_value=icon)
    header.separator_spacer()
    if body:
        __exporter_SettingsClosed = False
        body.enabled = settings.useNeedleEngine
        body.use_property_split = False # fix offset
        body.prop(settings, "exportMode", text="Export")
    else:
        __exporter_SettingsClosed = True


___exportingId = -1
___isExportSave = False

@bpy.app.handlers.persistent
def saved(context):
    global ___isExportSave
    if ___isExportSave:
        ___isExportSave = False
        return
    settings = tryGetMainSceneSettings()
    if(settings is not None and settings.exportOnSave is False):
        return
    if isExportingBug(): 
        debug_log("Auto Export is disabled during bug report")
        return
    global ___exportingId
    ___exportingId += 1
    id = ___exportingId
    def delayedExport():
        global ___exportingId
        if id != ___exportingId:
            return
        print("SAVED SCENE - auto export")
        bpy.ops.needle_scene.export("EXEC_DEFAULT", immediate=True)
        
        # the scene is dirty after export?
        if bpy.data.is_dirty:
            # then save again. Not ideal... but necessary to support AssetTracker and Production Management tools to work.
            global ___isExportSave
            ___isExportSave = True
            bpy.ops.wm.save_as_mainfile("EXEC_DEFAULT")


    bpy.app.timers.register(delayedExport, first_interval=0.5, persistent=False)


# listen to scene changes
installer_script_name = "Install Add-On: Needle Engine for Blender"
@bpy.app.handlers.persistent
def removeInstaller(_context):
    # remove installer script if it exists
    script = bpy.data.texts.get(installer_script_name)
    if script:
        script.use_module = False
        bpy.data.texts.remove(script)


def register():
    send_analytics_started()
    auto_load.register()
    # bpy.utils.register_class(TeamProperty)
    bpy.utils.register_class(NeedleExporterPreferences)
    animator.register()
    registerLightmapping()
    registerUnknownComponents()
    registerGameObjectData()
    registerMenuItems()
    registerDeployPanel()
    registerServer()
    registerBugreporter()
    bpy.app.handlers.save_post.append(saved)
    bpy.app.handlers.load_post.append(removeInstaller)
    bpy.app.timers.register(checkIfUpdateIsAvailableAndShowDialogueWithButton, first_interval=1, persistent=False)

    bpy.utils.register_class(NeedleGLTFExportSettings)
    bpy.types.Scene.NEEDLE_gltf_exporter_settings = bpy.props.PointerProperty(type=NeedleGLTFExportSettings)
    ensureLicensingServerRuns(False)


def unregister():
    component_watcher.stopFileWatchers()
    try:
        auto_load.unregister()
    except RuntimeError as e:
        debug_err("!!! ERR: auto_load.unregister() failed\n", e)
        pass
    try:
        bpy.utils.unregister_class(NeedleExporterPreferences)
    except RuntimeError as e:
        debug_err("!!! ERR: NeedleExporterPreferences.unregister() failed\n", e)
        pass
    animator.unregister()
    unregisterLightmapping()
    unregisterUnknownComponents()
    unregisterGameObjectData()
    unregisterMenuItems() 
    unregisterDeployPanel()
    unregisterServer()
    unregisterBugreporter()
    bpy.app.handlers.save_post.remove(saved)
    bpy.app.handlers.load_post.remove(removeInstaller)

    del bpy.types.Scene.NEEDLE_gltf_exporter_settings
    bpy.utils.unregister_class(NeedleGLTFExportSettings)





    
class ImportContext():
    def __init__(self, ext):
        self.root = ext
        pass
    def getExtensionHandler(self, name:str):
        return self.root.extensionHandler[name]

class UserExtensionBase:
    def __init__(self):
        # We need to wait until we create the gltf2UserExtension to import the gltf2 modules
        # Otherwise, it may fail because the gltf2 may not be loaded yet
        self.Extension = Extension

        self.enabled = True

        # create extension handlers
        self.extensionHandler = {}
        self.extensionHandler["animationhandler"] = AnimationHandler()
        self.extensionHandler["NEEDLE_lightmaps"] = NEEDLE_lightmaps(self)
        self.extensionHandler["NEEDLE_components"] = NEEDLE_components(self)
        self.extensionHandler["NEEDLE_gameobject_data"] = NEEDLE_gameobject_data(self)
        self.extensionHandler["NEEDLE_persistent_assets"] = NEEDLE_persistent_assets()
        self.extensionHandler["NEEDLE_progressive"] = NEEDLE_progressive_texture_settings()
        self.extensionHandler["NEEDLE_compression_texture"] = NEEDLE_texture_compression_EXT()
        self.extensionHandler["KHR_animation_2"] = KHR_animation_2()
        pass

    def tryGetExtension(self, name:str):
        return self.extensionHandler[name]

    ############################################################
    # invoke extension hooks similar to how gltf loader works
    def invoke_hook(self, hook_name, *args):
        if not self.enabled:
            return
        for item in self.extensionHandler.items():
            extensionName = item[0]
            extension = item[1]
            self.invoke_extension_hook(extensionName, extension, hook_name, *args)
        pass
    # invoke hook on one extension if it exists
    def invoke_extension_hook(self, extensionName, extension, hook_name, *args):
        if hasattr(extension, hook_name):
            attr = getattr(extension, hook_name)
            if callable(attr):
                try:
                    attr(*args)
                except Exception as e:
                    import traceback
                    debug_err("!!! ERR in extension:", extensionName, hook_name, e, traceback.format_exc(limit=10))
                    reportError("Error in extension " + extensionName + " hook " + hook_name + ": " + str(e))
        # else:
        #     print("WARN: hook not found " + hook_name + " on extension " + extensionName)

# https://github.com/KhronosGroup/glTF-Blender-IO/tree/master/example-addons/example_gltf_exporter_extension
class glTF2ExportUserExtension(UserExtensionBase):
    def __init__(self):

        # invoke base init
        super().__init__()

        self.__index = 0

    ############################################################
    # glTF2ExportUserExtension
    ############################################################

    # called on the beginning of export
    def gather_asset_hook(self, gltf2_asset, export_settings):

        # allow to disable running the export extension
        # https://github.com/needle-tools/needle-engine-blender/issues/11
        # settings = tryGetMainSceneSettings()
        # if settings and not settings.allowExtension:
        #     self.enabled = False
        #     print("Needle export extension is disabled by user settings")
        #     return

        

        # Our "Export" call does set the export id - any other export (via File/Export) has an empty ID
        # if exporting via File/Export we don't want to add our extensions
        # TODO: we need to unregister our object component types if we are not exporting because they might end up in extras (if users have that enabled)
        isFileMenuExport = not export_settings["gltf_export_id"] or len(export_settings["gltf_export_id"]) == 0
        fileMenuExportSettings = NeedleGLTFExportSettings.get()
        if isFileMenuExport:
            if not fileMenuExportSettings.useNeedleEngine:
                debug_log("Needle Engine extension export is disabled")
                self.enabled = False
                return
            else:
                debug_log("Needle export extension export is enabled")
                onExportFromFileMenu(export_settings)

        self.__index += 1
        self.exportContext = ExportContext(self)
        self.exportContext.exportSettings = export_settings 
        if isFileMenuExport:
            if fileMenuExportSettings.exportMode == "COMPONENTS_ONLY":
                self.exportContext.exportOnlyComponentsInScene = True

        # self.exportSettings = export_settings     
        self.invoke_hook("onBeforeExport", self.exportContext)
        # see https://github.com/KhronosGroup/glTF-Blender-IO/blob/9be538a53c374b50e109022965886f49d1eb4806/addons/io_scene_gltf2/blender/exp/gltf2_blender_export.py#L42
        export_settings["pre_export_callbacks"].append(self.pre_export_callback)
        export_settings["post_export_callbacks"].append(self.post_export_callback)
        self.invoke_hook("gather_asset_hook", gltf2_asset, export_settings)

    def gather_tree_filter_tag_hook(self, tree, export_settings):
        for key in tree.nodes:
            value = tree.nodes[key]
            blender_object = value.blender_object
            if len(blender_object.users_scene) <= 0:
                debug_log("Filtering out object because it's not in a scene", blender_object.name)
                value.keep_tag = False
                continue
            if blender_object and blender_object.hide_render:
                visibleInViewport = objectIsVisibleInViewport(blender_object)
                if not visibleInViewport:
                    debug_log("Filtering out object because it's not visible in the viewport and render", blender_object.name)
                    value.keep_tag = False
                    pass
        pass
    
    def pre_export_callback(self, export_settings):
        pass

    def post_export_callback(self, export_settings):
        self.invoke_hook("onAfterExport", self.exportContext)
        self.__index -= 1
        if self.__index == 0 and self.exportContext.isFileDialogueExport():
            onExportFromFileMenuFinished(self)
    
    def postExportCallback(self):
        pass

    def vtree_before_filter_hook(self, vtree, export_settings):
        # export_settings.gltf_loose_edges = True
        self.invoke_hook("vtree_before_filter_hook", vtree, export_settings)

    def gather_scene_hook(self, gltf2_scene, blender_scene, export_settings):
        self.invoke_hook("gather_scene_hook", gltf2_scene, blender_scene, export_settings)
        
    def pre_gather_actions_hook(self, blender_object, export_settings):
        self.invoke_hook("pre_gather_actions_hook", blender_object, export_settings)

    if bpy.app.version >= (3, 4, 0):
        def gather_actions_hook(self, blender_object, params, export_settings):
            self.invoke_hook("gather_actions_hook", blender_object, params, export_settings)
    else:
        def gather_actions_hook(self, blender_object, blender_actions, blender_tracks, action_on_type, export_settings):
            self.invoke_hook("gather_actions_hook", blender_object, blender_actions, blender_tracks, action_on_type, export_settings)

    # gather_animation_channel_hook(self, gltf2_animation_channel, channel, blender_object, action_name, node_channel_is_animated, export_settings)
    # gather_animation_channel_hook(self, gltf2_animation_channel, channel, blender_object, bone, action_name, node_channel_is_animated, export_settings)
    if bpy.app.version >= (4, 2, 2):
        def gather_animation_channel_hook(self, gltf2_animation_channel, channel, blender_object, bone, action_name, node_channel_is_animated, export_settings):
            # TODO: add action_name
            self.invoke_hook("gather_animation_channel_hook", gltf2_animation_channel, channel, blender_object, node_channel_is_animated, export_settings)
    else:
        def gather_animation_channel_hook(self, animation_channel, channel, blender_object, node_channel_is_animated, export_settings):
            self.invoke_hook("gather_animation_channel_hook", animation_channel, channel, blender_object, node_channel_is_animated, export_settings)
    
    # def animation_gather_fcurve(self, blender_object, blender_action, export_settings):
    #     self.invoke_hook("animation_gather_fcurve", blender_object, blender_action, export_settings)

    def pre_gather_animation_hook(self, export_settings, animation, blender_action, blender_object):
        self.invoke_hook("pre_gather_animation_hook", export_settings, animation, blender_action, blender_object)
    
    def gather_texture_hook(self, gltf2_texture, blender_shader_sockets, export_settings):
        self.invoke_hook("gather_texture_hook", gltf2_texture, blender_shader_sockets, export_settings)

    if bpy.app.version >= (4, 5, 1):
        def gather_image_hook(self, gltf2_image, mapping, blender_shader_sockets, export_settings):
            self.invoke_hook("gather_image_hook", gltf2_image, mapping, blender_shader_sockets, export_settings)
    else:
        def gather_image_hook(self, gltf2_image, blender_shader_sockets, export_settings):
            self.invoke_hook("gather_image_hook", gltf2_image, blender_shader_sockets, export_settings)

    def gather_material_hook(self, gltf2_material, blender_material, export_settings):
        self.invoke_hook("gather_material_hook", gltf2_material, blender_material, export_settings)

    def gather_mesh_hook(self, gltf2_mesh, blender_mesh, blender_object, vertex_groups, modifiers, materials, export_settings):
        self.invoke_hook("gather_mesh_hook", gltf2_mesh, blender_mesh, blender_object, vertex_groups, modifiers, materials, export_settings)

    # https://github.com/takahirox/glTF-Blender-IO-materials-variants/blob/2c72540af60028d4037b859f6a9589b24ed4931f/__init__.py#L266
    # exporting textures: https://github.com/KhronosGroup/glTF-Blender-IO/issues/1717
    def gather_gltf_extensions_hook(self, gltf2_object, export_settings):
        self.invoke_hook("gather_gltf_extensions_hook", gltf2_object, export_settings)

    def gather_animation_hook(self, gltf2_animation, blender_action, blender_object, export_settings):
        self.invoke_hook("gather_animation_hook", gltf2_animation, blender_action, blender_object, export_settings)

    def merge_animation_extensions_hook(self, gltf2_animation_source, gltf2_animation_destination, export_settings):
        self.invoke_hook("merge_animation_extensions_hook", gltf2_animation_source, gltf2_animation_destination, export_settings)

    def gather_joint_hook(self, gltf2_node, blender_bone, export_settings):
        self.invoke_hook("gather_joint_hook", gltf2_node, blender_bone, export_settings)

    def gather_node_hook(self, gltf2_node, blender_object, node):
        self.invoke_hook("gather_node_hook", gltf2_node, blender_object, node)
        return node

    def gather_gltf_hook(self, active_scene_idx, scenes, animations, export_settings):
        self.invoke_hook("gather_gltf_hook", active_scene_idx, scenes, animations, export_settings)








class glTF2ImportUserExtension(UserExtensionBase):

    def __init__(self):
        super().__init__()
        self.extensions = []
        self.importContext = ImportContext(self)
        pass
    
    def gather_import_gltf_before_hook(self, gltf):
        self.invoke_hook("onBeforeImport", self.importContext)

        self.invoke_hook("gather_import_gltf_before_hook", gltf)
        if gltf.data and gltf.data.animations:
            for anim in gltf.data.animations:
                convert_from_KHR_animation_2(anim)

    def gather_import_scene_before_hook(self, gltf_scene, blender_scene, gltf):
        self.invoke_hook("gather_import_scene_before_hook", gltf_scene, blender_scene, gltf)

    # NODES import
    def gather_import_node_before_hook(self, vnode, gltf_node, gltf):
        self.invoke_hook("gather_import_node_before_hook",  vnode, gltf_node, gltf)
    def gather_import_node_after_hook(self,  vnode, gltf_node, blender_object, gltf):
        self.invoke_hook("gather_import_node_after_hook",  vnode, gltf_node, blender_object, gltf)
    
    def gather_import_scene_after_nodes_hook(self, gltf_scene, blender_scene, gltf):
        self.invoke_hook("gather_import_scene_after_nodes_hook", gltf_scene, blender_scene, gltf)

    # MESH import
    def gather_import_mesh_before_hook(self, gltf_mesh, gltf):
        self.invoke_hook("gather_import_mesh_before_hook", gltf_mesh, gltf)
    def gather_import_mesh_after_hook(self, gltf_mesh, blender_mesh, gltf):
        self.invoke_hook("gather_import_mesh_after_hook", gltf_mesh, blender_mesh, gltf)
    
    # MATERIAL import
    def gather_import_material_before_hook(self, gltf_material, vertex_color, gltf):
        self.invoke_hook("gather_import_material_before_hook", gltf_material, vertex_color, gltf)
    def gather_import_material_after_hook(self, gltf_material, vertex_color, blender_mat, gltf):
        self.invoke_hook("gather_import_material_after_hook", gltf_material, vertex_color, blender_mat, gltf)

    # ANIMATION import
    if bpy.app.version >= (3, 6, 0):
        def gather_import_animation_before_hook(self, anim_idx, gltf):
            # second param is gltf_anim? not gltf? -> https://github.com/KhronosGroup/glTF-Blender-IO/tree/main/example-addons/example_gltf_importer_extension
            self.invoke_hook("gather_import_animation_before_hook", anim_idx)
        def gather_import_animation_after_hook(self, anim_idx, track_name, gltf):
            self.invoke_hook("gather_import_animation_after_hook", anim_idx, track_name, gltf)
    else:
        def gather_import_animation_before_hook(self, gltf_anim, anim_idx, gltf):
            # second param is gltf_anim? not gltf? -> https://github.com/KhronosGroup/glTF-Blender-IO/tree/main/example-addons/example_gltf_importer_extension
            self.invoke_hook("gather_import_animation_before_hook", gltf_anim, anim_idx, gltf)
        def gather_import_animation_after_hook(self, gltf_anim, anim_idx, track_name, gltf):
            self.invoke_hook("gather_import_animation_after_hook", gltf_anim, anim_idx, track_name, gltf)
    

def convert_from_KHR_animation_2(gltf_animation):
    '''Converting KHR_animation_2 to non json pointer version as long as the extension is not supported by Blender'''
    channels = gltf_animation.channels
    for channel in channels:
        target = channel.target
        target_node = target.node
        if target_node is None and target.extensions is not None:
            # we probably have a KHR_animation
            pointer_obj = target.extensions.get('KHR_animation_pointer', None)
            if pointer_obj:
                pointer = pointer_obj.get('pointer', None)
                if pointer.endswith('/translation') or pointer.endswith('/rotation') or pointer.endswith('/scale'):
                    pointer_sections = pointer.split('/')
                    target.node = int(pointer_sections[2])
                    target.path = pointer_sections[3]

    pass