from ...utils_debug import debug_log, debug_warn
from ...extensions.extensionbase import ExtensionBase
from ...component_utils import isEnabled
import bpy

def serializeTimeline(extension : ExtensionBase, blender_object : bpy.types.Object, blenderComponent: object|None):

    timeline = None
    animationTracks = None

    if blenderComponent:
        enabled = isEnabled(blenderComponent)
        if enabled == False:
            return None
        timeline = blenderComponent.playableAsset

    trackModels = []
    assetModel = {
        "name": "Timeline " + blender_object.name,
        "guid" : str(id(blender_object)) + "_timeline",
        "tracks" : trackModels
    }

    if timeline:
        animationTracks = timeline.animationtracks.array
        for item in animationTracks:
            obj = item.value
            if obj is None or obj.animation_data is None:
                continue
            animationTrackModel = createAnimationTrackModel(extension, obj)
            if animationTrackModel is None:
                continue
            trackModels.append(animationTrackModel)
    
    if animationTracks is None or len(animationTracks) == 0:
        # if we dont have *any* tracks assigned
        # iterate all objects in the scene and put them into the timeline
        debug_log("No tracks assigned to timeline " + blender_object.name + ", adding all objects in scene")
        for obj in bpy.data.objects:
            animationTrackModel = createAnimationTrackModel(extension, obj)
            if animationTrackModel is None:
                continue
            trackModels.append(animationTrackModel)
    
    # TODO: register in persistent assets instead of serializing into component

    return extension.registerPeristentAsset(assetModel)


def createAnimationTrackModel(extension, blender_object):

    animation_data = blender_object.animation_data
    if animation_data is None:
        shape_keys = getattr(blender_object.data, "shape_keys", None)
        if shape_keys is None:
            return None
        animation_data = shape_keys.animation_data
        if animation_data is None:
            return None
        
    nlaTracks = animation_data.nla_tracks
    if nlaTracks is None:
        return None

    outputs = [
        str(id(blender_object))
    ]
    clips = []

    model = {
        "name" : blender_object.name,
        "type" : "AnimationTrack",
        "muted" : False,
        "outputs" : outputs,
        "clips" : clips,
        "markers" : [0],
        # "trackOffset" : {
        #     "position" : {"x":0, "y":0, "z":0},
        #     "rotation" : {"x":0, "y":0, "z":0, "w":1},
        # }
    }

    # export active action
    if animation_data.action is not None:
        clipModel = createClipModel(extension, animation_data.action, blender_object)
        if clipModel is not None:
            clips.append(clipModel)
        else:
            debug_warn("Timeline: Failed to create clip model for action: " + str(animation_data.action))

    # export nla strips
    for track in nlaTracks:
        for strip in track.strips:
            clipModel = createClipModel(extension, strip, blender_object)
            if clipModel is not None:
                clips.append(clipModel)
            else:
                debug_warn("Timeline: Failed to create clip model for strip: " + str(strip))

    if len(clips) == 0:
        return None

    # sort clips by start time
    clips.sort(key=lambda clip: clip["start"])

    return model


def createClipModel(extension : ExtensionBase, clip : bpy.types.NlaStrip or bpy.types.Action, blender_object):
    # if its an action, create a clip from it
    action = None
    if isinstance(clip, bpy.types.Action):
    #     debug_log("Timeline: Can only export NLA strips: " + str(clip))
    #     return None
        action = clip
    else:
        action = clip.action
        if clip.mute: return None
    
    # special clip name that we emit on EXPORT (might be legacy....)
    if clip.name == "NEEDLE_ensure_export": None

    assetModel = {
        "clip" : "",
    }
    frameRate = bpy.context.scene.render.fps
    startFrame = getStartFrame(clip)
    endFrame = getEndFrame(clip)
    model = {
        "name" : clip.name,
        "start" : startFrame / frameRate,
        "end" : endFrame / frameRate,
        "duration" : (endFrame - startFrame) / frameRate,
        "timeScale" : 1 / getClipScale(clip),
        "asset" : assetModel,
        "clipIn" : getClipInFrame(clip) / frameRate,
        "easeInDuration" : getBlendIn(clip) / frameRate,
        "easeOutDuration" : getBlendOut(clip) / frameRate,
        "preExtrapolationMode": getExtrapolationMode(clip),
        "postExtrapolationMode" : getExtrapolationMode(clip),
        "reversed" : getReversed(clip),
    }
    extension.registerAnimation(assetModel, "clip", action, blender_object)
    return model

def getStartFrame(clip):
    if isinstance(clip, bpy.types.NlaStrip):
        return clip.frame_start
    range = clip.frame_range
    return range[0]

def getEndFrame(clip):
    if isinstance(clip, bpy.types.NlaStrip):
        return clip.frame_end
    range = clip.frame_range
    return range[1]

def getClipScale(clip):
    if isinstance(clip, bpy.types.NlaStrip):
        return clip.scale
    return 1

def getClipInFrame(clip):
    if isinstance(clip, bpy.types.NlaStrip):
        return clip.action_frame_start
    return 0

def getBlendIn(clip):
    if isinstance(clip, bpy.types.NlaStrip):
        return clip.blend_in
    return 0

def getBlendOut(clip):
    if isinstance(clip, bpy.types.NlaStrip):
        return clip.blend_out
    return 0

def getExtrapolationMode(clip):
    if isinstance(clip, bpy.types.NlaStrip):
        mode = clip.extrapolation
        if mode == 'HOLD':
            return 1
        else:
            return 0
    return 1

def getReversed(clip):
    if isinstance(clip, bpy.types.NlaStrip):
        return clip.use_reverse
    return False