// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using ModernWpf.Tests.MUXControls.ApiTests.RepeaterTests.Common;
using MUXControlsTestApp.Utilities;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Controls;
using Common;

#if USING_TAEF
using WEX.TestExecution;
using WEX.TestExecution.Markup;
using WEX.Logging.Interop;
#else
using Microsoft.VisualStudio.TestTools.UnitTesting;
#endif

using ItemsRepeater = ModernWpf.Controls.ItemsRepeater;
using RecyclingElementFactory = ModernWpf.Controls.RecyclingElementFactory;
using RecyclePool = ModernWpf.Controls.RecyclePool;
using ItemsRepeaterScrollHost = ModernWpf.Controls.ItemsRepeaterScrollHost;
using AnimationContext = ModernWpf.Controls.AnimationContext;

namespace ModernWpf.Tests.MUXControls.ApiTests.RepeaterTests
{
    [TestClass]
    public class ElementAnimatorTests : ApiTestBase
    {
        [TestMethod]
        public void ValidateElementAnimator()
        {
            ItemsRepeater repeater = null;
            ElementAnimatorDerived animator = null;
            var data = new ObservableCollection<string>(Enumerable.Range(0, 10).Select(i => string.Format("Item #{0}", i)));
            var renderingEvent = new ManualResetEvent(false);

            RunOnUIThread.Execute(() =>
            {
                var elementFactory = new RecyclingElementFactory();
                elementFactory.RecyclePool = new RecyclePool();
                elementFactory.Templates["Item"] = (DataTemplate)XamlReader.Parse(
                    @"<DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'> 
                          <TextBlock Text='{Binding}' Height='50' />
                      </DataTemplate>");

                CompositionTarget.Rendering += (sender, args) =>
                {
                    renderingEvent.Set();
                };

                repeater = new ItemsRepeater()
                {
                    ItemsSource = data,
                    ItemTemplate = elementFactory,
                };

                Content = new ItemsRepeaterScrollHost()
                {
                    Width = 400,
                    Height = 800,
                    ScrollViewer = new ScrollViewer
                    {
                        Content = repeater
                    }
                };
            });

            IdleSynchronizer.Wait();
            Verify.IsTrue(renderingEvent.WaitOne(), "Waiting for rendering event");

            List<CallInfo> showCalls = new List<CallInfo>();
            List<CallInfo> hideCalls = new List<CallInfo>();
            List<CallInfo> boundsChangeCalls = new List<CallInfo>();
            RunOnUIThread.Execute(() =>
            {
                animator = new ElementAnimatorDerived()
                {
                    HasShowAnimationValue = true,
                    HasHideAnimationValue = true,
                    HasBoundsChangeAnimationValue = true,
                    StartShowAnimationFunc = (UIElement element, AnimationContext context) =>
                    {
                        showCalls.Add(new CallInfo(repeater.GetElementIndex(element), context));
                    },

                    StartHideAnimationFunc = (UIElement element, AnimationContext context) =>
                    {
                        hideCalls.Add(new CallInfo(repeater.GetElementIndex(element), context));
                    },

                    StartBoundsChangeAnimationFunc = (UIElement element, AnimationContext context, Rect oldBounds, Rect newBounds) =>
                    {
                        boundsChangeCalls.Add(new CallInfo(repeater.GetElementIndex(element), context, oldBounds, newBounds));
                    }
                };
                repeater.Animator = animator;

                renderingEvent.Reset();
                data.Insert(0, "new item");
                data.RemoveAt(2);
            });

            Verify.IsTrue(renderingEvent.WaitOne(), "Waiting for rendering event");
            IdleSynchronizer.Wait();

            Verify.AreEqual(1, showCalls.Count);
            var call = showCalls[0];
            Verify.AreEqual(0, call.Index);
            Verify.AreEqual(AnimationContext.CollectionChangeAdd, call.Context);

            Verify.AreEqual(1, hideCalls.Count);
            call = hideCalls[0];
            Verify.AreEqual(-1, call.Index); // not in the repeater anymore
            Verify.AreEqual(AnimationContext.CollectionChangeRemove, call.Context);

            Verify.AreEqual(1, boundsChangeCalls.Count);
            call = boundsChangeCalls[0];
            Verify.AreEqual(1, call.Index);
            Verify.AreEqual(AnimationContext.CollectionChangeAdd | AnimationContext.CollectionChangeRemove, call.Context);
            Verify.AreEqual(0, call.OldBounds.Y);
            Verify.AreEqual(50, call.NewBounds.Y);

            showCalls.Clear();
            hideCalls.Clear();
            boundsChangeCalls.Clear();

            // Hookup just for show animations and validate.
            RunOnUIThread.Execute(() =>
            {
                animator.HasShowAnimationValue = true;
                animator.HasHideAnimationValue = false;
                animator.HasBoundsChangeAnimationValue = false;

                renderingEvent.Reset();
                data.Insert(0, "new item");
                data.RemoveAt(2);
            });

            Verify.IsTrue(renderingEvent.WaitOne(), "Waiting for rendering event");
            IdleSynchronizer.Wait();

            Verify.AreEqual(1, showCalls.Count);
            call = showCalls[0];
            Verify.AreEqual(0, call.Index);
            Verify.AreEqual(AnimationContext.CollectionChangeAdd, call.Context);

            Verify.AreEqual(0, hideCalls.Count);
            Verify.AreEqual(0, boundsChangeCalls.Count);
        }

        struct CallInfo
        {
            public CallInfo(int index, AnimationContext context)
            {
                Index = index;
                Context = context;
                OldBounds = default;
                NewBounds = default;
            }

            public CallInfo(int index, AnimationContext context, Rect oldBounds, Rect newBounds)
            {
                Index = index;
                Context = context;
                OldBounds = oldBounds;
                NewBounds = newBounds;
            }

            public int Index { get; set; }
            public AnimationContext Context { get; set; }
            public Rect OldBounds { get; set; }
            public Rect NewBounds { get; set; }

            public override string ToString()
            {
                return "Index:" + Index + " Context:" + Context + " OldBounds:" + OldBounds + " NewBounds" + NewBounds;
            }
        };
    }
}
