# Copyright 2009 Autodesk, Inc.  All rights reserved.
# Use of this software is subject to the terms of the Autodesk license agreement 
# provided at the time of installation or download, or which otherwise accompanies
# this software in either electronic or hard copy form.
#
# Topic: FBTime, FCurve, FBFCurveKey
#
# Get the necessary symbols from pyfbsdk.
from pyfbsdk import FBSystem, FBModelList, FBGetSelectedModels, FBTime

# This recursive function finds the first and last keyframe of an
# animation node.
def FindLimits( pNode, pLLimit=None, pRLimit=None ):
    # First let's see if the node has any keys
    if pNode.FCurve:

        # Got thru the list, updating the first and last frame if necessary.
        # Limits are initialised on first comparaison attempt.
        for lKey in pNode.FCurve.Keys:

            if pLLimit:
                if lKey.Time.Get() < pLLimit.Get():
                    pLLimit.Set( lKey.Time.Get())
            else:
                pLLimit = FBTime()
                pLLimit.Set( lKey.Time.Get())
            if pRLimit:
                if lKey.Time.Get() > pRLimit.Get():
                    pRLimit.Set( lKey.Time.Get())
            else:
                pRLimit = FBTime()
                pRLimit.Set( lKey.Time.Get())

    # If the node has any children nodes, we navigate those.
    if pNode.Nodes:
        for lNode in pNode.Nodes:
            ( pLLimit, pRLimit ) = FindLimits( lNode, pLLimit, pRLimit )

    return ( pLLimit, pRLimit )


# This function iterates thru the list of keys and offsets them according to
# a given delta. The internal list of keys is sorted according to time. This
# means that we have to be careful how we iterate the list to ensure that a
# moved key will keep the same position in the list being modified.
def OffsetKeys( pNode, pDelta ):
    # Modify all the keys of the current node.
    if pNode.FCurve:

        # Create a list of the keys. This new list can be re-sorted without
        # affecting the internal list.
        lKeys = [ lKey for lKey in pNode.FCurve.Keys ]

        # If the delta is positive, we need to move the keys starting with
        # the last. This prevent any changes in the order of the keys in the
        # internal list.
        if pDelta.Get() > 0:
            lKeys.reverse()

        # Set the new time value for all the keys.
        for lKey in lKeys:
            lTime = FBTime()
            lTime.Set( lKey.Time.Get() + pDelta.Get() )
            lKey.Time = lTime

    # Now deal with all the children nodes.
    if pNode.Nodes:
        for lNode in pNode.Nodes:
            OffsetKeys( lNode, pDelta )


# Get the list of selected models, if any.
lSystem = FBSystem()
lModels = FBModelList()
FBGetSelectedModels( lModels )

# Iterate the list of selected models.
for lModel in lModels:
    # Find the earliest keyframe.
    ( lLTime, lRTime ) = FindLimits( lModel.AnimationNode )

    # If we do have a first keyframe...
    if lLTime:
        # Compute the delta between the current time and the first key.
        lCurrentTime = lSystem.LocalTime
        lDelta = FBTime()
        lDelta.Set( lCurrentTime.Get() - lLTime.Get())

        # Do the work...
        OffsetKeys( lModel.AnimationNode, lDelta )

        # Cleanup of local variables.
        del( lCurrentTime, lDelta )

    # Cleanup of local variables.
    del( lModel, lLTime, lRTime )

# Cleanup

# Cleanup of local variable
del( lSystem, lModels )

# Cleanup of local functions
del( FindLimits, OffsetKeys )

# Cleanup of imported symbols
del( FBSystem, FBModelList, FBGetSelectedModels, FBTime )