

# this is just a helper - not really an extension BUT we want to receive the callbacks
from ..utils_debug import debug_err, debug_log, debug_warn
import bpy

class AnimationHandler:

    def onBeforeExport(self, exportContext):
        self.animationIndex = 0
        self.animationReferences = []
        self.createdAnimationTracks = {}
        self.registeredActions = []
        self.__exportedActions = []
        pass

    ############################################################
    ## register an animation to be resolved in the animation export callback
    def registerAnimation(self, blender_object, target, property, action):
        if action is None:
            return
        self.animationReferences.append({"blender_object" : blender_object, "target": target, "property": property, "animation": action})
        self.ensureAnimationIsExported(blender_object, action)
        if isinstance(action, list):
            debug_log("> Register animation " + str(len(action)) + " actions on object " + blender_object.name + " for property " + property)
        elif action is not None:
            debug_log("> Register animation " + action.name + " on object " + blender_object.name + " for property " + property)
        pass

    # https://docs.blender.org/manual/en/latest/addons/import_export/scene_gltf2.html#export
    def ensureAnimationIsExported(self, blender_object, action):
        # if action is a list
        if isinstance(action, list):
            for a in action:
                self.ensureAnimationIsExported(blender_object, a)
            return

        if action is None: 
            return
        animationData = blender_object.animation_data
        if animationData is None:
            # try find the animated object from the action
            for fcurve in action.fcurves:
                pass
            for group in action.groups:
                pass
            return
            # create animation data
            # if blender_object.name != "lever":
            #     return
            # animationData = blender_object.animation_data_create()

        if action == blender_object.animation_data.action:
            # the action is the active action on the object
            return
        if action in self.registeredActions:
            # the action is already exported
            return
        self.registeredActions.append(action)
        # check if the action is in ANY nla track on this object
        tracks = animationData.nla_tracks
        if tracks is None:
            # create a track
            tracks = animationData.nla_tracks.new()
            # self.createdNlaTracks.append({"object": blender_object, "track": tracks})
        # Sometimes clips are not exported even if they are used in a timeline so we always create a new track:
        # for track in tracks:
        #     for strip in track.strips:
        #         if strip.action == action:
        #             return
        #         # HACK: the blender exporter currently only exports actions when they are the first strip in a track
        #         # for that reason we also check only the first strip. Issue: https://github.com/KhronosGroup/glTF-Blender-IO/issues/1835
        #         break
        # create a new strip
        newTrack = tracks.new()
        newTrack.mute = True
        self.registerAnimationTrack(blender_object, newTrack)
        newTrack.strips.new("NEEDLE_ensure_export", 0, action)
        pass

    def registerAnimationTrack(self, blender_object, track):
        # remove track after export
        if not blender_object in self.createdAnimationTracks:
            self.createdAnimationTracks[blender_object] = []
        self.createdAnimationTracks[blender_object].append(track)
        pass


    ############################################################
    ## handle callback
    
    # callback for blender 3.6
    def gather_actions_hook(self, blender_object, params, export_settings):
        blender_actions = getattr(params, "blender_actions", None)

        # I'm trying to export the actions here properly but this is not working as expected.
        # https://linear.app/needle/issue/NE-5491/
        # blender_tracks = getattr(params, "blender_tracks", None)
        # action_on_type = getattr(params, "action_on_type", None)
        # references = self.animationReferences
        # for ref in references:
        #     if ref["blender_object"] == blender_object:
        #         # make sure the action is in the blender_actions array
        #         animation = ref["animation"]
        #         found: bool = False
        #         for action in blender_actions:
        #             if action == animation:
        #                 found = True
        #                 break
        #         if not found:
        #             # if we add new actions we need to make sure the key exists in the blender_tracks dictionary too
        #             # see addons_core\io_scene_gltf2\blender\exp\animation\gltf2_blender_gather_action.py line 710
        #             blender_actions.append(animation)
        #             blender_tracks[animation.name] = None
        #             action_on_type[animation.name] = 'OBJECT'
        #             if not blender_object.animation_data:
        #                 blender_object.animation_data_create()
        
        # gltf_animations = getattr(params, "gltf_animations", None)
        if blender_actions is None or len(blender_actions) == 0:
            return
        
        # HACK workaround for blender gltf exporter sorting actions by name
        if bpy.app.version >= (3, 6, 4):
            debug_warn("!!! Blender version is 3.6.4 or higher, we need to sort the actions by name because the glTF exporter does that: https://github.com/KhronosGroup/glTF-Blender-IO/issues/2057")
            blender_actions.sort(key = lambda a: a.name.lower())
            
        for i in range(len(blender_actions)):
            self.gather_animation_hook(None, blender_actions[i], blender_object, export_settings)

    # callback prior blender 3.6, note that we dont have the gltf_animation anymore in 3.6 right now
    def gather_animation_hook(self, gltf2_animation, blender_action, blender_object, export_settings):
        # print ("gather_animation_hook", blender_action, blender_object)
        # check if the action has data:
        if blender_action is None or blender_action.fcurves is None or len(blender_action.fcurves) == 0:
            return

        animationIndex = self.animationIndex
        if blender_action in self.__exportedActions:
            animationIndex = self.__exportedActions.index(blender_action)
        else:
            self.__exportedActions.append(blender_action)
            self.animationIndex += 1
        
        pointer = "/animations/" + str(animationIndex)

        debug_log("< Export " + blender_action.name + " on " + blender_object.name + " as " + pointer)
        # iterate all components that have animation references and write their pointers here
        try:
            for ref in self.animationReferences:
                anim = ref["animation"]
                # check if we have an array
                if isinstance(anim, list):
                    for index in range(len(anim)):
                        animationEntry = anim[index]
                        if animationEntry == blender_action:
                            target = ref["target"]
                            propertyName = ref["property"]
                            if target[propertyName] is None:
                                target[propertyName] = []
                            targetList = target[propertyName]
                            if pointer in targetList:
                                continue
                            targetList.insert(index, pointer)
                            debug_log("> Set animation pointer " + blender_action.name + " on " + blender_object.name + " for property " + propertyName + " at index " + str(index)  + " as " + pointer)

                elif ref["animation"] == blender_action:
                    target = ref["target"]
                    propertyName = ref["property"]
                    currentValue =  target[propertyName]
                    if currentValue is None or currentValue == '':
                        target[propertyName] = pointer                      
                        debug_log("> Set animation pointer " + blender_action.name + " on " + blender_object.name + " for property " + propertyName + " as " + pointer)

        except Exception as e:
            debug_err("!!! Error while writing animation pointer: " + str(e) + " " + blender_action.name + " " + blender_object.name)
            raise e
        finally:
            self.afterAnimationExported(blender_object, blender_action)
        pass

    ## cleanup tracks
    def afterAnimationExported(self, blender_object, action):
        # check if we created animation tracks for this object
        # if blender_object in self.createdAnimationTracks:
        #     tracks = self.createdAnimationTracks[blender_object]
        #     for track in tracks:
        #         animationData = blender_object.animation_data
        #         animationData.nla_tracks.remove(track)
        #     del self.createdAnimationTracks[blender_object]
        pass

    def onAfterExport(self, exportContext):
        self.registeredActions.clear()
        for obj in self.createdAnimationTracks:
            if obj.animation_data.nla_tracks is None:
                continue
            tracks = self.createdAnimationTracks[obj]
            for track in tracks:
                obj.animation_data.nla_tracks.remove(track)
        pass
    