// Decompiled with JetBrains decompiler
// Type: Microsoft.Iris.Animations.AnimationProxy
// Assembly: UIX, Version=4.8.0.0, Culture=neutral, PublicKeyToken=ddd0da4d3e678217
// MVID: A56C6C9D-B7F6-46A9-8BDE-B3D9B8D60B11
// Assembly location: C:\Program Files\Zune\UIX.dll

using Microsoft.Iris.Drawing;
using Microsoft.Iris.Library;
using Microsoft.Iris.Render;
using Microsoft.Iris.Session;

namespace Microsoft.Iris.Animations
{
    internal class AnimationProxy : DisposableObject
    {
        private ActiveSequence _activeSequence;
        private IKeyframeAnimation _animation;
        private int _keyframesValue;
        private bool _playingFlag;
        private AnimationType _type;
        private bool _dynamicFlag;
        private bool _doNotAutoReleaseFlag;
        private IAnimatable _animatableTarget;
        private RendererProperty _rendererProperty;
        private static DeferredHandler s_deferredCleanupWorker = new DeferredHandler(DeferredCleanupWorker);

        internal AnimationProxy(
          ActiveSequence activeSequence,
          IAnimatable animatableTarget,
          AnimationType createType,
          RendererProperty rendererProperty,
          int loopCount,
          StopCommand stopCmd)
        {
            UISession.Validate(activeSequence.Session);
            AnimationSystem.ValidateAnimationType(createType);
            _activeSequence = activeSequence;
            _type = createType;
            _animatableTarget = animatableTarget;
            _rendererProperty = rendererProperty;
            _animation = _activeSequence.Session.AnimationManager.BuildAnimation(this);
            CommonCreate(loopCount, stopCmd);
        }

        private void CommonCreate(int loopCount, StopCommand stopCmd)
        {
            _animation.RepeatCount = loopCount;
            _animation.AsyncNotifyEvent += new AsyncNotifyHandler(OnAsyncNotification);
            _animation.AddStageEvent(AnimationStage.Complete, new AnimationEvent(_animation, "AsyncNotify", 1U));
            _animation.AddStageEvent(AnimationStage.Reset, new AnimationEvent(_animation, "AsyncNotify", 2U));
            SetStopCommand(stopCmd);
            _activeSequence.OnAttachChildAnimation(this);
        }

        protected override void OnDispose()
        {
            CleanupWorker(0.0f, false);
            base.OnDispose();
        }

        public UISession Session => _activeSequence.Session;

        public AnimationType Type => _type;

        public bool HasDynamicKeyframes => _dynamicFlag;

        internal bool DoNotAutoRelease
        {
            get => _doNotAutoReleaseFlag;
            set => _doNotAutoReleaseFlag = value;
        }

        public void AddFloatKeyframe(BaseKeyframe keyframe, float value)
        {
            ++_keyframesValue;
            AnimationInput animationInput1;
            if (keyframe.IsRelativeToObject)
            {
                _dynamicFlag = true;
                animationInput1 = keyframe.RelativeTo.CreateAnimationInput(_animatableTarget, _rendererProperty.Property, _rendererProperty.SourceMask);
                if (keyframe.Multiply && value != 1.0)
                {
                    AnimationInput animationInput2 = new ConstantAnimationInput(value);
                    animationInput1 *= animationInput2;
                }
                else if (!keyframe.Multiply && value != 0.0)
                {
                    AnimationInput animationInput2 = new ConstantAnimationInput(value);
                    animationInput1 += animationInput2;
                }
            }
            else
                animationInput1 = new ConstantAnimationInput(value);
            _animation.AddKeyframe(new AnimationKeyframe(keyframe.Time, animationInput1, GenerateInterpolation(keyframe.Interpolation)));
        }

        public void AddVector2Keyframe(BaseKeyframe keyframe, Vector2 value)
        {
            ++_keyframesValue;
            AnimationInput animationInput1;
            if (keyframe.IsRelativeToObject)
            {
                _dynamicFlag = true;
                animationInput1 = keyframe.RelativeTo.CreateAnimationInput(_animatableTarget, _rendererProperty.Property, _rendererProperty.SourceMask);
                if (keyframe.Multiply && value != Vector2.UnitVector)
                {
                    AnimationInput animationInput2 = new ConstantAnimationInput(value);
                    animationInput1 *= animationInput2;
                }
                else if (!keyframe.Multiply && value != Vector2.Zero)
                {
                    AnimationInput animationInput2 = new ConstantAnimationInput(value);
                    animationInput1 += animationInput2;
                }
            }
            else
                animationInput1 = new ConstantAnimationInput(value);
            _animation.AddKeyframe(new AnimationKeyframe(keyframe.Time, animationInput1, GenerateInterpolation(keyframe.Interpolation)));
        }

        public void AddVector3Keyframe(BaseKeyframe keyframe, Vector3 value)
        {
            ++_keyframesValue;
            AnimationInput animationInput1;
            if (keyframe.IsRelativeToObject)
            {
                _dynamicFlag = true;
                animationInput1 = keyframe.RelativeTo.CreateAnimationInput(_animatableTarget, _rendererProperty.Property, _rendererProperty.SourceMask);
                if (keyframe.Multiply && value != Vector3.UnitVector)
                {
                    AnimationInput animationInput2 = new ConstantAnimationInput(value);
                    animationInput1 *= animationInput2;
                }
                else if (!keyframe.Multiply && value != Vector3.Zero)
                {
                    AnimationInput animationInput2 = new ConstantAnimationInput(value);
                    animationInput1 += animationInput2;
                }
            }
            else
                animationInput1 = new ConstantAnimationInput(value);
            _animation.AddKeyframe(new AnimationKeyframe(keyframe.Time, animationInput1, GenerateInterpolation(keyframe.Interpolation)));
        }

        public void AddVector4Keyframe(BaseKeyframe keyframe, Vector4 value)
        {
            ++_keyframesValue;
            AnimationInput animationInput1;
            if (keyframe.IsRelativeToObject)
            {
                _dynamicFlag = true;
                animationInput1 = keyframe.RelativeTo.CreateAnimationInput(_animatableTarget, _rendererProperty.Property, _rendererProperty.SourceMask);
                if (keyframe.Multiply && value != Vector4.UnitVector)
                {
                    AnimationInput animationInput2 = new ConstantAnimationInput(value);
                    animationInput1 *= animationInput2;
                }
                else if (!keyframe.Multiply && value != Vector4.Zero)
                {
                    AnimationInput animationInput2 = new ConstantAnimationInput(value);
                    animationInput1 += animationInput2;
                }
            }
            else
                animationInput1 = new ConstantAnimationInput(value);
            _animation.AddKeyframe(new AnimationKeyframe(keyframe.Time, animationInput1, GenerateInterpolation(keyframe.Interpolation)));
        }

        public void AddRotationKeyframe(BaseKeyframe keyframe, Rotation value)
        {
            if (keyframe.Type != AnimationType.Orientation)
            {
                AddVector4Keyframe(keyframe, new Vector4(value.Axis.X, value.Axis.Y, value.Axis.Z, value.AngleRadians));
            }
            else
            {
                ++_keyframesValue;
                AnimationInput animationInput1;
                if (keyframe.IsRelativeToObject)
                {
                    _dynamicFlag = true;
                    animationInput1 = keyframe.RelativeTo.CreateAnimationInput(_animatableTarget, _rendererProperty.Property, _rendererProperty.SourceMask);
                    if (value != Rotation.Default)
                    {
                        AnimationInput animationInput2 = new ConstantAnimationInput(new Quaternion(value.Axis, value.AngleRadians));
                        animationInput1 *= animationInput2;
                    }
                }
                else
                    animationInput1 = new ConstantAnimationInput(new Quaternion(value.Axis, value.AngleRadians));
                AnimationInterpolation interpolation = GenerateInterpolation(keyframe.Interpolation);
                interpolation.UseSphericalCombination = true;
                _animation.AddKeyframe(new AnimationKeyframe(keyframe.Time, animationInput1, interpolation));
            }
        }

        public void SetStopCommand(StopCommand stopCmd)
        {
            switch (stopCmd)
            {
                case StopCommand.LeaveCurrent:
                    _animation.ResetBehavior = AnimationResetBehavior.LeaveCurrent;
                    break;
                case StopCommand.MoveToBegin:
                    _animation.ResetBehavior = AnimationResetBehavior.SetInitialValue;
                    break;
                case StopCommand.MoveToEnd:
                    _animation.ResetBehavior = AnimationResetBehavior.SetFinalValue;
                    break;
            }
            _animation.AutoReset = true;
        }

        public bool ValidatePlayable()
        {
            if (_keyframesValue >= 2)
                return true;
            ErrorManager.ReportError("Animations must have at least 2 keyframes to play");
            return false;
        }

        public void Play()
        {
            if (!ValidatePlayable())
                return;
            if (!_activeSequence.Session.AnimationManager.CanPlayAnimationType(_type))
            {
                Cleanup(0.0f, true);
            }
            else
            {
                if (_playingFlag || _animation == null || !Session.IsValid)
                    return;
                _animation.AddTarget(_animatableTarget, _rendererProperty.Property, _rendererProperty.TargetMask);
                _animation.Play();
                _playingFlag = true;
            }
        }

        public void Stop() => StopWorker(false, StopCommand.MoveToEnd);

        public void Stop(StopCommand stopCommand) => StopWorker(true, stopCommand);

        private void StopWorker(bool stopCommandFlag, StopCommand stopCommand)
        {
            if (!_playingFlag)
                return;
            _playingFlag = false;
            if (_animation == null || !Session.IsValid)
                return;
            if (stopCommandFlag)
                SetStopCommand(stopCommand);
            _animation.Reset();
            _animation.RemoveAllTargets();
        }

        private void Cleanup(float progress, bool forceDeferFlag)
        {
            if (_animation == null)
                return;
            if (UIDispatcher.IsUIThread && !forceDeferFlag)
            {
                CleanupWorker(progress, true);
            }
            else
            {
                if (!Session.IsValid)
                    return;
                DeferredCall.Post(DispatchPriority.Housekeeping, s_deferredCleanupWorker, this);
            }
        }

        private void CleanupWorker(float progress, bool withNotifications)
        {
            _playingFlag = false;
            if (_animation != null)
            {
                _animation.AsyncNotifyEvent -= new AsyncNotifyHandler(OnAsyncNotification);
                _animation.UnregisterUsage(this);
                _animation = null;
            }
            _animatableTarget = null;
            if (!withNotifications)
                return;
            _activeSequence.OnDetachChildAnimation(this, progress);
        }

        private static void DeferredCleanupWorker(object args) => (args as AnimationProxy).CleanupWorker(0.0f, true);

        private static AnimationInterpolation GenerateInterpolation(
          Interpolation interpolation)
        {
            if (interpolation == null)
                return new LinearInterpolation();
            switch (interpolation.Type)
            {
                case InterpolationType.Linear:
                    return new LinearInterpolation();
                case InterpolationType.SCurve:
                    return new SCurveInterpolation(interpolation.Weight * 10f);
                case InterpolationType.Exp:
                    return interpolation.Weight > 0.0 ? new ExponentialInterpolation(interpolation.Weight * 10f) : (AnimationInterpolation)new LinearInterpolation();
                case InterpolationType.Log:
                    return interpolation.Weight > 0.0 ? new LogarithmicInterpolation(interpolation.Weight * 10f) : (AnimationInterpolation)new LinearInterpolation();
                case InterpolationType.Sine:
                    return new SineInterpolation();
                case InterpolationType.Cosine:
                    return new CosineInterpolation();
                case InterpolationType.Bezier:
                    return new BezierInterpolation(interpolation.BezierHandle1, interpolation.BezierHandle2);
                case InterpolationType.EaseIn:
                    return new EaseInInterpolation(interpolation.Weight * 10f, interpolation.EasePercent);
                case InterpolationType.EaseOut:
                    return new EaseOutInterpolation(interpolation.Weight * 10f, interpolation.EasePercent);
                default:
                    return new LinearInterpolation();
            }
        }

        private void OnAsyncNotification(int nCookie)
        {
            switch (nCookie)
            {
                case 1:
                    Cleanup(0.0f, false);
                    break;
                case 2:
                    Cleanup(0.0f, false);
                    break;
            }
        }

        private enum AsyncNotifications
        {
            OnComplete = 1,
            OnReset = 2,
        }
    }
}
