internal static void BeginAnimation(
            DependencyObject d,
            DependencyProperty dp,
            AnimationTimeline animation,
            HandoffBehavior handoffBehavior)
        {
            // Caller should be validating animation.
            Debug.Assert(animation == null || IsAnimationValid(dp, animation));
            Debug.Assert(IsPropertyAnimatable(d, dp));

            Debug.Assert(HandoffBehaviorEnum.IsDefined(handoffBehavior),
                         "Public API caller of this internal method is responsible for validating that the HandoffBehavior value is valid.");

            AnimationStorage storage = GetStorage(d, dp);

            if (animation == null)
            {
                if (storage == null ||
                    handoffBehavior == HandoffBehavior.Compose)
                {
                    // Composing with a null animation is a no-op.
                    return;
                }
                else
                {
                    // When the incoming animation is passed in as null and
                    // handoffBehavior == HandoffBehavior.SnapshotAndReplace it means
                    // that we should stop any and all animation behavior for
                    // this property.
                    if (storage._hasStickySnapshotValue)
                    {
                        storage._hasStickySnapshotValue = false;
                        storage._animationClocks[0].CurrentStateInvalidated -= new EventHandler(storage.OnCurrentStateInvalidated);
                    }

                    storage._snapshotValue = DependencyProperty.UnsetValue;
                    storage.ClearAnimations();
                }
            }
            else if (animation.BeginTime.HasValue)
            {
                // We have an animation
                AnimationClock animationClock;
                animationClock = animation.CreateClock();  // note that CreateClock also calls InternalBeginIn

                ApplyAnimationClocks(d, dp, new AnimationClock[] { animationClock }, handoffBehavior);

                // ApplyAnimationClocks has fixed up the storage and called
                // WritePostscript already so we can just return.
                return;
            }
            else if (storage == null)
            {
                //  The user gave us an animation with a BeginTime of null which
                // means snapshot the current value and throw away all animations
                // for SnapshotAndReplace and means nothing for Compose.
                //  But since we don't have any animations the current we don't
                // have to do anything for either of these cases.

                return;
            }
            else if (handoffBehavior == HandoffBehavior.SnapshotAndReplace)
            {
                // This block handles the case where the user has called
                // BeginAnimation with an animation that has a null begin time.
                // We handle this by taking a snapshot value and throwing
                // out the animation, which is the same as keeping it because
                // we know it will never start.
                //
                // If the handoffBehavior is Compose, we ignore the user's call
                // because it wouldn't mean anything unless they were planning
                // on changing the BeginTime of their animation later, which we
                // don't support.

                // If _hasStickySnapshotValue is set, unset it and remove our
                // event handler from the clock. The current snapshot value
                // will still be used.
                if (storage._hasStickySnapshotValue)
                {
                    Debug.Assert(storage._animationClocks != null && storage._animationClocks.Count > 0,
                                 "If _hasStickySnapshotValue is set we should have at least one animation clock stored in the AnimationStorage.");

                    storage._hasStickySnapshotValue = false;
                    storage._animationClocks[0].CurrentStateInvalidated -= new EventHandler(storage.OnCurrentStateInvalidated);
                }
                // Otherwise take a new snapshot value.
                else
                {
                    storage._snapshotValue = d.GetValue(dp);
                }

                storage.ClearAnimations();
            }

            // If storage were null we would have returned already.
            storage.WritePostscript();
        }
        [FriendAccessAllowed] // Built into Core, also used by Framework.
        internal static void ApplyAnimationClocks(
            DependencyObject d,
            DependencyProperty dp,
            IList <AnimationClock> animationClocks,
            HandoffBehavior handoffBehavior)
        {
            Debug.Assert(animationClocks != null,
                         "The animationClocks parameter should not be passed in as null.");
            Debug.Assert(animationClocks.Count > 0,
                         "The animationClocks parameter should contain at least one clock.");
            Debug.Assert(!animationClocks.Contains(null),
                         "The animationClocks parameter should not contain a null entry.");
            Debug.Assert(HandoffBehaviorEnum.IsDefined(handoffBehavior),
                         "Public API caller of this internal method is responsible for validating that the HandoffBehavior value is valid.");

            AnimationStorage storage = GetStorage(d, dp);

            // handoffBehavior is SnapshotAndReplace or the situation is such
            // that it is the equivalent because we have nothing to compose
            // with.
            if (handoffBehavior == HandoffBehavior.SnapshotAndReplace ||
                storage == null ||
                storage._animationClocks == null)
            {
                if (storage != null)
                {
                    EventHandler handler = new EventHandler(storage.OnCurrentStateInvalidated);

                    // If we have a sticky snapshot value, the clock that would have
                    // unstuck it is being replaced, so we need to remove our event
                    // handler from that clock.
                    if (storage._hasStickySnapshotValue)
                    {
                        storage._animationClocks[0].CurrentStateInvalidated -= handler;
                    }
                    // Calculate a snapshot value if we don't already have one
                    // since the last tick.
                    else
                    {
                        storage._snapshotValue = d.GetValue(dp);
                    }

                    // If we have a new clock in a stopped state, then the snapshot
                    // value will be sticky.
                    if (animationClocks[0].CurrentState == ClockState.Stopped)
                    {
                        storage._hasStickySnapshotValue             = true;
                        animationClocks[0].CurrentStateInvalidated += new EventHandler(storage.OnCurrentStateInvalidated);
                    }
                    // Otherwise it won't be sticky.
                    else
                    {
                        storage._hasStickySnapshotValue = false;
                    }

                    storage.ClearAnimations();
                }
                else
                {
                    storage = CreateStorage(d, dp);
                }

                // Add and attach new animation.
                storage._animationClocks = new FrugalObjectList <AnimationClock>(animationClocks.Count);

                for (int i = 0; i < animationClocks.Count; i++)
                {
                    Debug.Assert(animationClocks[i] != null);

                    storage._animationClocks.Add(animationClocks[i]);
                    storage.AttachAnimationClock(animationClocks[i], storage._removeRequestedHandler);
                }
            }
            else
            {
                Debug.Assert(handoffBehavior == HandoffBehavior.Compose);
                Debug.Assert(storage != null);
                Debug.Assert(storage._animationClocks != null);

                FrugalObjectList <AnimationClock> newClockCollection = new FrugalObjectList <AnimationClock>(storage._animationClocks.Count + animationClocks.Count);

                for (int i = 0; i < storage._animationClocks.Count; i++)
                {
                    newClockCollection.Add(storage._animationClocks[i]);
                }

                storage._animationClocks = newClockCollection;

                for (int i = 0; i < animationClocks.Count; i++)
                {
                    newClockCollection.Add(animationClocks[i]);
                    storage.AttachAnimationClock(animationClocks[i], storage._removeRequestedHandler);
                }
            }

            storage.WritePostscript();
        }